在曲面上合并多边形

时间:2016-02-21 17:11:38

标签: c# geo clipperlib

我尝试使用限幅器库来合并我要导入Google地球的多边形(c#)。它使用了两个圆形的平面样本,但使用long和latitude作为X和Y有两个问题:

  1. Clipper只支持long作为数据类型(你可以在将值传递给Clipper之前使用乘数 - 这已经有question,但它并没有解决第二点< / LI>
  2. Clipper假设曲面是平的,因此输出坐标不是预期的位置。不可能只考虑X和Y作为经度和纬度,并为它们增加值x。它可以在赤道上工作,但是向北更高的地方增加相同的经度值会导致更短的距离。
  3. enter image description here

    我想知道是否已经有针对该场景的实现/库。

    更新:由于已经请求使用了源代码,我创建了一个包含相关代码的小型示例应用程序:

    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:创建圆圈的方式确实存在错误。通过更正,圆圈可以正确创建和合并: enter image description here

    我发现我可以使用Mercator Projection将上图中圆圈的点投影到平面上。然后可以使用Clipper进行合并。最后,所有点都再次投射到球体上(墨卡托投影的反面)。

    这将是一种解决方法(将更深入地测试它)。所以这个问题有点回答,但如果有其他解决方案的解决方法较少,那将会很有趣。

0 个答案:

没有答案