使用具有内部边界的Google Earth KML的Clipper多边形联合的结果

时间:2016-02-27 06:54:01

标签: c# kml google-earth clipperlib

我想将多边形合并到一个可以有洞的区域。 Clipper可以做到这一点,但是当我在Google Earth中使用两个生成的多边形时,问题是Google earth会分别处理这些多边形并将它们相互挤压。 在KML中,可以为多边形创建OuterBoundary和InnerBoundary元素。问题是,使用Clipper结果你怎么知道什么是内在的,什么是外边界。 还有一点需要提及:即使它可以以某种方式确定,在实际的Clipper联合调用中我使用了几千个圆形多边形。所以有多个独立的区域有洞。 enter image description here 合并后: enter image description here

以下是具有四种简单形状的代码:

/* 
0     -------
9     |     |
8     |  2  |
7 -------   |-----
6 |     |----    |
5 |  1  |xx|  3  |  
4 |     |--|     |
3 -------  -------
2     |  4  |
1     |     |
0     -------
  0123456789012345             
*/

IntPolygons polygons = new IntPolygons();
// 1
polygons.Add(new IntPolygon{
    new IntPoint { X = 0, Y = 3 },
    new IntPoint { X = 6, Y = 3 },
    new IntPoint { X = 6, Y = 7 },
    new IntPoint { X = 0, Y = 7 }
});

// 2
polygons.Add(new IntPolygon{
    new IntPoint { X = 4, Y = 6 },
    new IntPoint { X = 10, Y = 6 },
    new IntPoint { X = 10, Y = 10 },
    new IntPoint { X = 4, Y = 10 }
});

// 3
polygons.Add(new IntPolygon{
    new IntPoint { X = 9, Y = 3 },
    new IntPoint { X = 15, Y = 3 },
    new IntPoint { X = 15, Y = 7 },
    new IntPoint { X = 9, Y = 7 }
});

// 4
polygons.Add(new IntPolygon{
    new IntPoint { X = 4, Y = 0 },
    new IntPoint { X = 10, Y = 0 },
    new IntPoint { X = 10, Y = 4},
    new IntPoint { X = 4, Y = 4 }
});

Clipper clipper = new Clipper();
foreach (var polygon in polygons)
{
    clipper.AddPath(polygon, PolyType.ptSubject, true);
}

IntPolygons mergedPolygons = new IntPolygons();

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

for (int i = 0; i < mergedPolygons.Count; i++)
{
    Console.WriteLine("polygon " + (i + 1));
    foreach (var point in mergedPolygons[i])
    {
        Console.WriteLine("X: " + point.X + "\t\t Y: " + point.Y);
    }
}

// Result:
//polygon 1
//X: 10            Y: 3
//X: 15            Y: 3
//X: 15            Y: 7
//X: 10            Y: 7
//X: 10            Y: 10
//X: 4             Y: 10
//X: 4             Y: 7
//X: 0             Y: 7
//X: 0             Y: 3
//X: 4             Y: 3
//X: 4             Y: 0
//X: 10            Y: 0

//polygon 2
//X: 6             Y: 4
//X: 6             Y: 6
//X: 9             Y: 6
//X: 9             Y: 4

// The second polygon is the inner boundary
/* 
0              
9                   
8               
7                
6       x  x        
5                 
4       x  x        
3                  
2                    
1                   
0                   
  0123456789012345             
*/

更新:在KML中,总有两组多边形列表,OuterBoundaries和InnerBoundaries。我设法递归地解析多边形,并检查每个最外面的多边形是否有内部多边形。最外面的内部多边形是InnerBoundary。所有其他内部多边形再次以OuterBoundary多边形开始。 一旦我发现了一些非常大的多边形集合的问题,我就会发布代码。

1 个答案:

答案 0 :(得分:0)

我基本上使用递归方法来遍历所有嵌套多边形。 OuterBoundary和InnerBoundary元素交替出现。我确信仍有改进的余地,但结果似乎与QGIS导出相同(后来我发现了)。 使用复杂数据时,未填充的多边形存在问题。我在GIS StackExchange页面中添加了一个单独的问题:

 // A class to hold the inner polygons
 public class HierarchicalPolygon
    {
        private Polygon _polygon;
        private List<HierarchicalPolygon> _innerPolygons;

        public HierarchicalPolygon(Polygon polygon)
        {
            _polygon = polygon;
        }

        public Polygon MainPolygon { get
            {
                return _polygon;
            }
            set
            {
                _polygon = value;
            }
        }


        public List<HierarchicalPolygon> InnerPolygons
        {
            get
            {
                return _innerPolygons;
            }
            set
            {
                _innerPolygons = value;
            }
        }

    }

public class PolygonHelper
{
    public static List<HierarchicalPolygon> GeneratePolygonHierachy(Polygons polygons)
        {
            // Step 1: get polygons that have no enclosing polygons
            var outerPolygons = new List<HierarchicalPolygon>();
            foreach (var polygon in polygons)
            {
                var enclosingPolygon = FindEnclosingPolygon((Polygon)polygon, polygons);
                if (enclosingPolygon == null)
                {
                    outerPolygons.Add(new HierarchicalPolygon((Polygon)polygon));
                }
            }

            // Step 2: recursively go through all nested polygons
            // Only two levels are allowed in KML. For example 
            //  OuterBoundary: country polygon 
            //  InnerBoundary: lake polygon
            //  OuterBoundary: island in lake polygon
            var polygonHierarchy = new List<HierarchicalPolygon>();
            foreach (var polygon in outerPolygons)
            {
                ParsePolygonRecursively(polygon, polygonHierarchy, polygons, true);
            }

            return polygonHierarchy;
        }

        private static void ParsePolygonRecursively(HierarchicalPolygon polygonToProcess, List<HierarchicalPolygon> mainList, Polygons allPolygons, bool currentIsOuterBoundary)
        {
            var innerPolygons = FindInnerPolygons(polygonToProcess.MainPolygon, allPolygons);

            if (currentIsOuterBoundary)
            {
                mainList.Add(polygonToProcess);

                // If OuterBoundary then add the nesteed Polygons the the current polygon
                if (innerPolygons != null && innerPolygons.Count > 0)
                {
                    polygonToProcess.InnerPolygons = new List<HierarchicalPolygon>();
                    foreach (var innerPolygon in innerPolygons)
                    {
                        var newPolygon = new HierarchicalPolygon((Polygon)innerPolygon);

                        // Not all inner polygons can be added, because they may be nested inside each other
                        // Adding of all inner polygons would only be possible, if the would not be contained in each other.
                        var enclosingPolygon = FindEnclosingPolygon((Polygon)innerPolygon, innerPolygons);

                        if (enclosingPolygon == null || enclosingPolygon.Count == 0)
                        {
                            polygonToProcess.InnerPolygons.Add(newPolygon);
                            ParsePolygonRecursively(new HierarchicalPolygon((Polygon)innerPolygon), mainList, allPolygons, false);

                            // don't break there could be multiple inner polygons that have again inner polygons
                            //break;
                        }
                    }
                }
            }
            else
            {
                // If InnerBoundary then don't add another layer but start at the OuterBoundary again
                foreach (var innerPolygon in innerPolygons)
                {
                    var enclosingPolygon = FindEnclosingPolygon((Polygon)innerPolygon, innerPolygons);

                    if (enclosingPolygon == null || enclosingPolygon.Count == 0)
                    {
                        ParsePolygonRecursively(new HierarchicalPolygon((Polygon)innerPolygon), mainList, allPolygons, true);
                    }
                }
            }
        }

        /// <summary>
        /// Uses IsPointInPolygon Method to check a points of a polygon to all other polygons
        /// </summary>
        /// <param name="insidePolygon"></param>
        /// <param name="polygonList"></param>
        /// <returns></returns>
        public static Polygon FindEnclosingPolygon(Polygon insidePolygon, Polygons polygonList)
        {
            //bool isInside = false;
            foreach (var polygon in polygonList)
            {
                int insidePointCount = 0;

                foreach (var insidePoint in insidePolygon)
                {
                    if (IsPointInPolygon(polygon, insidePoint))
                    {
                        insidePointCount += 1;
                    }
                    else
                    {
                        break;
                    }
                }

                if (insidePointCount == insidePolygon.Count)
                {
                    return (Polygon)polygon;
                }
            }

            return null;
        }

    /// <summary>
    /// Uses IsPointInPolygon Method to check a points of a polygon to all other polygons
    /// </summary>
    /// <param name="insidePolygon"></param>
    /// <param name="polygonList"></param>
    /// <returns></returns>
    public static Polygons FindInnerPolygons(Polygon parentPolygon, Polygons polygonList)
    {
        var innerPolygons = new Polygons();

        foreach (var polygon in polygonList)
        {
            int insidePointCount = 0;

            foreach (var point in polygon)
            {
                if (IsPointInPolygon(parentPolygon, point))
                {
                    insidePointCount += 1;
                }
                else
                {
                    break;
                }
            }

            if (insidePointCount == polygon.Count)
            {
                innerPolygons.Add((Polygon)polygon);
            }

        }

        return innerPolygons;
    }

        /// <summary>
        /// Source: https://stackoverflow.com/questions/4243042/c-sharp-point-in-polygon
        /// </summary>
        /// <param name="polygon"></param>
        /// <param name="testPoint"></param>
        /// <returns></returns>
        public static bool IsPointInPolygon(List<DoublePoint> polygon, DoublePoint testPoint)

            bool result = false;
            int j = polygon.Count() - 1;
            for (int i = 0; i < polygon.Count(); i++)
            {
                if (polygon[i].Y < testPoint.Y && polygon[j].Y >= testPoint.Y || polygon[j].Y < testPoint.Y && polygon[i].Y >= testPoint.Y)
                {
                    if (polygon[i].X + (testPoint.Y - polygon[i].Y) / (polygon[j].Y - polygon[i].Y) * (polygon[j].X - polygon[i].X) < testPoint.X)
                    {
                        result = !result;
                    }
                }
                j = i;
            }
            return result;
        }
  }