public void NormalizeMultiPolygonOneHole() { var verts = MakeGeoCoordArray(new decimal[, ] { { 0, 0 }, { 0, 3 }, { 3, 3 }, { 3, 0 } }); var outer = CreateLinkedLoop(verts); var verts2 = MakeGeoCoordArray(new decimal[, ] { { 1, 1 }, { 2, 2 }, { 1, 2 } }); var inner = CreateLinkedLoop(verts2); var polygon = new LinkedGeoPolygon(); polygon.AddLinkedLoop(inner); polygon.AddLinkedLoop(outer); int result; (result, polygon) = polygon.NormalizeMultiPolygon(); Assert.AreEqual(result, Constants.LinkedGeo.NormalizationSuccess); Assert.AreEqual(polygon.CountPolygons, 1); Assert.AreEqual(polygon.CountLoops, 2); Assert.IsNotNull(polygon.Loops.First()); Assert.AreEqual(polygon.Loops.First(), outer); Assert.IsNotNull(polygon.Loops[1]); Assert.AreEqual(polygon.Loops[1], inner); polygon.Clear(); }
public void NormalizeMultiPolygonNoOuterLoops() { var verts = MakeGeoCoordArray(new decimal[, ] { { 0, 0 }, { 1, 1 }, { 0, 1 } }); var outer1 = CreateLinkedLoop(verts); var verts2 = MakeGeoCoordArray(new decimal[, ] { { 2, 2 }, { 3, 3 }, { 2, 3 } }); var outer2 = CreateLinkedLoop(verts2); var polygon = new LinkedGeoPolygon(); polygon.AddLinkedLoop(outer1); polygon.AddLinkedLoop(outer2); int result; (result, polygon) = polygon.NormalizeMultiPolygon(); Assert.AreEqual(H3Lib.Constants.LinkedGeo.NormalizationErrUnassignedHoles, result); Assert.AreEqual(1, polygon.CountPolygons); Assert.AreEqual(0, polygon.CountLoops); //polygon.Clear(); }
public void NormalizeMultiPolygonTwoOuterLoops() { var verts1 = MakeGeoCoordArray(new decimal[, ] { { 0, 0 }, { 0, 1 }, { 1, 1 } }); var outer1 = CreateLinkedLoop(verts1); var verts2 = MakeGeoCoordArray(new decimal[, ] { { 2, 2 }, { 2, 3 }, { 3, 3 } }); var outer2 = CreateLinkedLoop(verts2); var polygon = new LinkedGeoPolygon(); polygon.AddLinkedLoop(outer1); polygon.AddLinkedLoop(outer2); int result; (result, polygon) = polygon.NormalizeMultiPolygon(); Assert.AreEqual(result, Constants.LinkedGeo.NormalizationSuccess); Assert.AreEqual(polygon.CountPolygons, 2); Assert.AreEqual(polygon.CountLoops, 1); Assert.AreEqual(polygon.Next.CountLoops, 1); polygon.Clear(); }
public void NormalizeMultiPolygonNestedDonuts() { var verts = MakeGeoCoordArray(new[, ] { { 0.2m, 0.2m }, { 0.2m, -0.2m }, { -0.2m, -0.2m }, { -0.2m, 0.2m } }); var outer = CreateLinkedLoop(verts); var verts2 = MakeGeoCoordArray(new[, ] { { 0.1m, 0.1m }, { -0.1m, 0.1m }, { -0.1m, -0.1m }, { 0.1m, -0.1m } }); var inner = CreateLinkedLoop(verts2); var verts3 = MakeGeoCoordArray(new[, ] { { 0.6m, 0.6m }, { 0.6m, -0.6m }, { -0.6m, -0.6m }, { -0.6m, 0.6m } }); var outerBig = CreateLinkedLoop(verts3); var verts4 = MakeGeoCoordArray(new[, ] { { 0.5m, 0.5m }, { -0.5m, 0.5m }, { -0.5m, -0.5m }, { 0.5m, -0.5m } }); var innerBig = CreateLinkedLoop(verts4); var polygon = new LinkedGeoPolygon(); polygon.AddLinkedLoop(inner); polygon.AddLinkedLoop(outerBig); polygon.AddLinkedLoop(innerBig); polygon.AddLinkedLoop(outer); int result; (result, polygon) = polygon.NormalizeMultiPolygon(); Assert.AreEqual(result, Constants.LinkedGeo.NormalizationSuccess); Assert.AreEqual(polygon.CountPolygons, 2); Assert.AreEqual(polygon.CountLoops, 2); Assert.IsNotNull(polygon.Loops.First()); Assert.AreEqual(polygon.Loops.First(), outerBig); Assert.IsNotNull(polygon.Loops[1]); Assert.AreEqual(polygon.Loops[1], innerBig); Assert.AreEqual(polygon.Next.CountLoops, 2); Assert.IsNotNull(polygon.Next.Loops.First()); Assert.AreEqual(polygon.Next.Loops.First(), outer); Assert.IsNotNull(polygon.Next.Loops[1]); Assert.AreEqual(polygon.Next.Loops[1], inner); polygon.Clear(); }
public void NormalizeMultiPolygonTwoDonuts() { var verts = MakeGeoCoordArray(new decimal[, ] { { 0, 0 }, { 0, 3 }, { 3, 3 }, { 3, 0 } }); var outer = CreateLinkedLoop(verts); var verts2 = MakeGeoCoordArray(new decimal[, ] { { 1, 1 }, { 2, 2 }, { 1, 2 } }); var inner = CreateLinkedLoop(verts2); var verts3 = MakeGeoCoordArray(new decimal[, ] { { 0, 0 }, { 0, -3 }, { -3, -3 }, { -3, 0 } }); var outer2 = CreateLinkedLoop(verts3); var verts4 = MakeGeoCoordArray(new decimal[, ] { { -1, -1 }, { -2, -2 }, { -1, -2 } }); var inner2 = CreateLinkedLoop(verts4); var polygon = new LinkedGeoPolygon(); polygon.AddLinkedLoop(inner2); polygon.AddLinkedLoop(inner); polygon.AddLinkedLoop(outer); polygon.AddLinkedLoop(outer2); int result; (result, polygon) = polygon.NormalizeMultiPolygon(); Assert.AreEqual(result, Constants.LinkedGeo.NormalizationSuccess); Assert.AreEqual(polygon.CountPolygons, 2); Assert.AreEqual(polygon.CountLoops, 2); Assert.IsNotNull(polygon.Loops.First()); Assert.AreEqual(polygon.Loops.First().Count, 4); Assert.IsNotNull(polygon.Loops[1]); Assert.AreEqual(polygon.Loops[1].Count, 3); Assert.AreEqual(polygon.Next.CountLoops, 2); Assert.IsNotNull(polygon.Next.Loops.First()); Assert.AreEqual(polygon.Next.Loops.First().Count, 4); Assert.IsNotNull(polygon.Next.Loops[1]); Assert.AreEqual(polygon.Next.Loops[1].Count, 3); polygon.Clear(); }
/// <summary> /// Internal: Create a LinkedGeoPolygon from a vertex graph. It is the /// responsibility of the caller to call destroyLinkedPolygon on the populated /// linked geo structure, or the memory for that structure will not be freed. /// </summary> /// <param name="graph">Input graph</param> /// <returns>Output polygon</returns> /// <!-- /// algos.c /// void _vertexGraphToLinkedGeo /// --> public static LinkedGeoPolygon ToLinkedGeoPolygon(this VertexGraph graph) { var result = new LinkedGeoPolygon(); while (graph.Count > 0) { var edge = graph.FirstNode(); var loop = new LinkedGeoLoop(); while (edge.HasValue) { loop.AddLinkedCoord(edge.Value.From); //loop.GeoCoordList.AddLast(edge.Value.From); var nextVertex = edge.Value.To; graph.RemoveNode(edge.Value); edge = graph.FindVertex(nextVertex); } if (loop.Count > 0) { result.AddLinkedLoop(loop); } } return(result); }
public void NormalizeMultiPolygonSingle() { var verts = MakeGeoCoordArray(new decimal[, ] { { 0, 0 }, { 0, 1 }, { 1, 1 } }); var outer = CreateLinkedLoop(verts); var polygon = new LinkedGeoPolygon(); polygon.AddLinkedLoop(outer); int result; (result, polygon) = polygon.NormalizeMultiPolygon(); Assert.AreEqual(result, Constants.LinkedGeo.NormalizationSuccess); Assert.AreEqual(polygon.CountPolygons, 1); Assert.AreEqual(polygon.CountLoops, 1); if (polygon.Loops.First() != null) { Assert.AreEqual(polygon.Loops.First(), outer); } polygon.Clear(); }
public void NormalizeMultiPolygonTwoHoles() { var verts = MakeGeoCoordArray(new decimal[, ] { { 0, 0 }, { 0, 0.4m }, { 0.4m, 0.4m }, { 0.4m, 0 } }); var outer = CreateLinkedLoop(verts); var verts2 = MakeGeoCoordArray(new decimal[, ] { { 0.1m, 0.1m }, { 0.2m, 0.2m }, { 0.1m, 0.2m } }); var inner1 = CreateLinkedLoop(verts2); var verts3 = MakeGeoCoordArray(new decimal[, ] { { 0.2m, 0.2m }, { 0.3m, 0.3m }, { 0.2m, 0.3m } }); var inner2 = CreateLinkedLoop(verts3); var polygon = new LinkedGeoPolygon(); polygon.AddLinkedLoop(inner2); polygon.AddLinkedLoop(outer); polygon.AddLinkedLoop(inner1); int result; (result, polygon) = polygon.NormalizeMultiPolygon(); Assert.AreEqual(result, Constants.LinkedGeo.NormalizationSuccess); Assert.AreEqual(polygon.CountPolygons, 1); Assert.IsNotNull(polygon.Loops.First()); Assert.AreEqual(polygon.Loops.First(), outer); Assert.AreEqual(polygon.CountLoops, 3); polygon.Clear(); }
public void NormalizeMultiPolygonAlreadyNormalized() { var verts1 = MakeGeoCoordArray(new decimal[, ] { { 0, 0 }, { 0, 1 }, { 1, 1 } }); var outer1 = CreateLinkedLoop(verts1); var verts2 = MakeGeoCoordArray(new decimal[, ] { { 2, 2 }, { 2, 3 }, { 3, 3 } }); var outer2 = CreateLinkedLoop(verts2); var polygon = new LinkedGeoPolygon(); polygon.AddLinkedLoop(outer1); var next = polygon.AddNewLinkedGeoPolygon(); next.AddLinkedLoop(outer2); // Should be a no-op int result; (result, polygon) = polygon.NormalizeMultiPolygon(); Assert.AreEqual(H3Lib.Constants.LinkedGeo.NormalizationErrMultiplePolygons, result); Assert.AreEqual(2, polygon.CountPolygons); Assert.AreEqual(1, polygon.CountLoops); Assert.IsNotNull(polygon.Loops.First()); Assert.AreEqual(outer1, polygon.Loops.First()); Assert.AreEqual(1, polygon.Next.CountLoops); Assert.IsNotNull(polygon.Next.Loops.First()); Assert.AreEqual(outer2, polygon.Next.Loops.First()); polygon.Clear(); }
/// <summary> /// Normalize a LinkedGeoPolygon in-place into a structure following GeoJSON /// MultiPolygon rules: Each polygon must have exactly one outer loop, which /// must be first in the list, followed by any holes. Holes in this algorithm /// are identified by winding order (holes are clockwise), which is guaranteed /// by the h3SetToVertexGraph algorithm. /// /// Input to this function is assumed to be a single polygon including all /// loops to normalize. It's assumed that a valid arrangement is possible. /// </summary> /// <param name="root">Root polygon including all loops</param> /// <returns> /// Tuple /// Item1 - 0 on success, or an error code > 0 for invalid input /// Item2 - Normalized LinkedGeoPolygon /// </returns> /// <!-- /// linkedGeo.c /// int normalizeMultiPolygon /// --> public static (int, LinkedGeoPolygon) NormalizeMultiPolygon(this LinkedGeoPolygon root) { // We assume that the input is a single polygon with loops; // if it has multiple polygons, don't touch it if (root.Next != null) { // TODO: Check the constant location and update return(Constants.LinkedGeo.NormalizationErrMultiplePolygons, root); } // Count loops, exiting early if there's only one int loopCount = root.CountLoops; if (loopCount <= 1) { return(Constants.LinkedGeo.NormalizationSuccess, root); } int resultCode = Constants.LinkedGeo.NormalizationSuccess; LinkedGeoPolygon polygon = null; var innerCount = 0; var outerCount = 0; // Create an array to hold all of the inner loops. Note that // this array will never be full, as there will always be fewer // inner loops than outer loops. var innerLoops = new List <LinkedGeoLoop>(); // Create an array to hold the bounding boxes for the outer loops var bboxes = new List <BBox>(); // Get the first loop and unlink it from root var testLoops = root.Loops;//.GeoLoopList.First; root = new LinkedGeoPolygon(); foreach (var geoLoop in testLoops) { if (geoLoop.IsClockwise()) { innerLoops.Add(geoLoop); innerCount++; } else { polygon = polygon == null ? root : polygon.AddNewLinkedGeoPolygon(); polygon.AddLinkedLoop(geoLoop); bboxes.Add(geoLoop.ToBBox()); outerCount++; } } // Find polygon for each inner loop and assign the hole to it for (var i = 0; i < innerCount; i++) { polygon = innerLoops[i].FindPolygonForHole(root, bboxes, outerCount); if (polygon != null) { polygon.AddLinkedLoop(innerLoops[i]); } else { // If we can't find a polygon (possible with invalid input), then // we need to release the memory for the hole, because the loop has // been unlinked from the root and the caller will no longer have // a way to destroy it with destroyLinkedPolygon. innerLoops[i].Clear(); resultCode = Constants.LinkedGeo.NormalizationErrUnassignedHoles; } } // Free allocated memory innerLoops.Clear(); bboxes.Clear(); return(resultCode, root); }