Использование результата объединения полигонов Clipper для Google Earth KML с внутренними границами

Я хочу объединить полигоны в одну область, в которой могут быть отверстия. Clipper может сделать это, однако, когда я использую два получающихся в Google Планета Земля многоугольника, проблема заключается в том, что Google Планета Земля обрабатывает эти полигоны отдельно и просто накладывает их друг на друга. В KML есть возможность создавать элементы OuterBoundary и InnerBoundary для многоугольника. Проблема в том, что с помощью Clipper вы узнаете, что является внутренним, а что внешним. Еще одна вещь, которую стоит упомянуть: даже если это можно как-то определить, в фактическом вызове союза Clipper я использую многоугольники нескольких тысяч кругов. Таким образом, есть несколько отдельных областей с отверстиями. введите описание изображения здесь После слияния: введите описание изображения здесь

Вот код с четырьмя простыми формами:

/* 
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. Мне удалось рекурсивно разобрать полигоны и проверить для каждого внешнего полигона, если у него есть внутренние полигоны. Внешние внутренние полигоны - это Внутренняя граница. Все остальные внутренние полигоны снова начинаются как полигоны OuterBoundary. Я опубликую код, как только я выясню некоторые проблемы с очень большими наборами полигонов.

1 ответ

Я в основном использовал рекурсивный метод, чтобы пройти через все вложенные многоугольники. Элементы OuterBoundary и InnerBoundary чередуются. Я уверен, что есть еще возможности для улучшения, но результат, по-видимому, такой же, как и при экспорте в QGIS (о чем я узнал позже). Есть проблемы с незаполненными полигонами, когда я использую сложные данные. Я добавил отдельный вопрос по этому поводу на странице ГИС 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://stackru.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;
        }
  }
Другие вопросы по тегам