3

Google Earth にインポートしたいポリゴン (c#) をマージするためのクリッパー ライブラリを試しました。2 つの円の平面サンプルで動作しましたが、X と Y として経度と緯度を使用すると 2 つの問題があります。

  1. Clipper は long as データ型のみをサポートします (Clipper に値を渡す前に乗数を使用することで回避できます - それについては既に質問がありますが、2 番目のポイントには対応していません。
  2. クリッパーは表面が平らであると想定しているため、出力座標は期待される場所ではありません。X と Y を経度と緯度と考えて、値 x を追加することはできません。赤道でも機能する可能性がありますが、北上すると経度に同じ値を追加すると、距離が短くなります。

ここに画像の説明を入力

そのシナリオの実装/ライブラリが既にあるかどうか疑問に思っていました。

更新: 使用されたソース コードが要求されたため、関連するコードを含む小さなサンプル アプリケーションを作成しました。

internal class Polygon : List<DoublePoint> { }
internal class Polygons : List<List<DoublePoint>> { }

internal class IntPolygon : List<IntPoint> { }
internal class IntPolygons : List<List<IntPoint>> { }

class Program
{
    static void Main(string[] args)
    {
        MercatorProjection mercatorProjection = new MercatorProjection();

        List<GeoCoordinate> circle1Coordinates = GenerateCirclePolygonByMeters(new GeoCoordinate { Longitude = 50, Latitude = 50 }, 5000);
        List<GeoCoordinate> circle2Coordinates = GenerateCirclePolygonByMeters(new GeoCoordinate { Longitude = 50.05, Latitude = 50 }, 5000);

        Polygons polygons = new Polygons();

        Polygon circle1 = new Polygon();
        foreach(var coordinate in circle1Coordinates)
        {
            DoublePoint point = mercatorProjection.FromLatLngToPoint(coordinate.Latitude, coordinate.Longitude, 15);
            circle1.Add(point);
        }

        Polygon circle2 = new Polygon();
        foreach (var coordinate in circle2Coordinates)
        {
            DoublePoint point = mercatorProjection.FromLatLngToPoint(coordinate.Latitude, coordinate.Longitude, 15);
            circle2.Add(point);
        }

        polygons.Add(circle1);
        polygons.Add(circle2);

        // Workaround: get int-values
        IntPolygons intPolygons = ConvertToIntPolygons(polygons);

        // Merge
        IntPolygons mergedIntPolygons = MergePolygons(intPolygons);

        // Workaroud: Get doubles again
        Polygons mergedPolygons = ConvertToDoublePolygons(mergedIntPolygons);

        // Convert back to spherical surface
        // GeoCoordinate class is from System.Device
        List<GeoCoordinate> mergedCoordinates = new List<GeoCoordinate>();
        foreach (var polygon in mergedPolygons)
        {
            foreach (var point in polygon)
            {
                GeoCoordinate coordinate = mercatorProjection.FromPointToLatLng(point, 15);
                mergedCoordinates.Add(coordinate);
            }
        }

        // Generate output csv-list
        WriteOutputFile(mergedCoordinates);
    }

    private static void WriteOutputFile(List<GeoCoordinate> coordinates, string filename = "")
    {
        string uniquename = DateTime.Now.ToString("yyyyMMdd_HHmmss") + ".txt";
        string filenameToUse = String.IsNullOrEmpty(filename) ? uniquename : filename;
        var fileStream = File.OpenWrite(filenameToUse);

        using (var writer = new StreamWriter(fileStream))
        {
            // header
            writer.WriteLine("lat,long");

            // content
            foreach (var coordinate in coordinates)
            {
                writer.WriteLine(coordinate.Latitude.ToString(CultureInfo.InvariantCulture) + "," + coordinate.Longitude.ToString(CultureInfo.InvariantCulture));
            }

            writer.Close();
        }
    }

    private static Polygons ConvertToDoublePolygons(IntPolygons polygons)
    {
        double multiplier = 100;

        Polygons doublePolygons = new Polygons();
        foreach (var intPolygon in polygons)
        {
            Polygon doublePolygon = new Polygon();
            foreach (var intPoint in intPolygon)
            {
                doublePolygon.Add(new DoublePoint { X = intPoint.X / multiplier, Y = intPoint.Y / multiplier });
            }

            doublePolygons.Add(doublePolygon);
        }

        return doublePolygons;
    }

    private static IntPolygons ConvertToIntPolygons(Polygons polygons)
    {
        double multiplier = 100;
        IntPolygons intPolygons = new IntPolygons();
        foreach (var doublePolygon in polygons)
        {
            IntPolygon intPolygon = new IntPolygon();
            foreach (var doublePoint in doublePolygon)
            {
                intPolygon.Add(new IntPoint { X = (int)(doublePoint.X * multiplier), Y = (int)(doublePoint.Y * multiplier) });
            }

            intPolygons.Add(intPolygon);
        }

        return intPolygons;
    }

    private static List<GeoCoordinate> GenerateCirclePolygonByMeters(GeoCoordinate position, double distanceMeters)
    {
        // calc distance in coordinates-system
        double latitude2 = CalculateDerivedPosition(position, distanceMeters, 0).Latitude;
        double yRadius = latitude2 - position.Latitude;
        double longitude2 = CalculateDerivedPosition(position, distanceMeters, 90).Longitude;
        double xRadius = longitude2 - position.Longitude;

        var circle = GenerateCirclePolygon(position.Longitude, position.Latitude, xRadius, yRadius, 16);

        List<GeoCoordinate> circleCoordinates = new List<GeoCoordinate>();
        foreach(var point in circle)
        {
            circleCoordinates.Add(new GeoCoordinate { Latitude = point.Y, Longitude = point.X });
        }

        return circleCoordinates;
    }

    private static Polygon GenerateCirclePolygon(double xStart, double yStart, double xRadius, double yRadius, int points)
    {
        Polygon polygon = new Polygon();
        double slice = 2 * Math.PI / points;


        for (int i = 0; i < points; i++)
        {
            double angle = slice * i;
            Console.WriteLine(angle);
            double x = xRadius * Math.Cos(angle);
            double y = yRadius * Math.Sin(angle);

            polygon.Add(new DoublePoint(xStart + x, yStart + y));
        }

        return polygon;
    }

    // Source: https://stackoverflow.com/questions/1125144/how-do-i-find-the-lat-long-that-is-x-km-north-of-a-given-lat-long
    public static GeoCoordinate CalculateDerivedPosition(GeoCoordinate source, double rangeMeters, double bearing)
    {
        double radiansToDegrees = 57.2957795;
        double degreesToRadians = 0.0174532925;
        double twoPi = Math.PI * 2;
        double earthRadius = 6378137.0;

        double latA = source.Latitude * degreesToRadians;
        double lonA = source.Longitude * degreesToRadians;
        double angularDistance = rangeMeters / earthRadius;
        double trueCourse = bearing * degreesToRadians;

        double lat = Math.Asin(
            Math.Sin(latA) * Math.Cos(angularDistance) +
            Math.Cos(latA) * Math.Sin(angularDistance) * Math.Cos(trueCourse));

        double dlon = Math.Atan2(
            Math.Sin(trueCourse) * Math.Sin(angularDistance) * Math.Cos(latA),
            Math.Cos(angularDistance) - Math.Sin(latA) * Math.Sin(lat));

        double lon = ((lonA + dlon + Math.PI) % twoPi) - Math.PI;

        return new GeoCoordinate(
            lat * radiansToDegrees,
            lon * radiansToDegrees);
    }

    private static IntPolygons MergePolygons(IntPolygons polygons)
    {
        Clipper clipper = new Clipper();

        clipper.AddPaths(polygons, PolyType.ptSubject, true);

        IntPolygons mergedPolygons = new IntPolygons();
        clipper.Execute(ClipType.ctUnion, mergedPolygons,
            PolyFillType.pftNonZero, PolyFillType.pftNonZero);

        return mergedPolygons;
    }

更新 2: 円の作成方法に実際にエラーがありました。修正により、円が作成され、正しくマージされます。 ここに画像の説明を入力

メルカトル図法を使用して、上の画像の円の点を平面に投影できることがわかりました。その後、Clipper を使用してマージを行うことができます。最後に、すべてのポイントが再び球に投影されました (メルカトル投影の逆)。

これは回避策になります (より詳細にテストする予定です)。したがって、質問は一種の回答ですが、回避策が少ない他のソリューションがあれば興味深いでしょう.

4

0 に答える 0