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(); }
/// <summary> /// Free all allocated memory for a linked geo structure. The caller is /// responsible for freeing memory allocated to input polygon struct. /// </summary> /// <param name="polygon">Pointer to the first polygon in the structure</param> /// <!-- Based off 3.1.1 --> public static void destroyLinkedPolygon(ref LinkedGeoPolygon polygon) { // flag to skip the input polygon bool skip = true; LinkedGeoPolygon nextPolygon; LinkedGeoLoop nextLoop; for (LinkedGeoPolygon currentPolygon = polygon; currentPolygon != null; currentPolygon = nextPolygon) { for (LinkedGeoLoop currentLoop = currentPolygon.first; currentLoop != null; currentLoop = nextLoop) { destroyLinkedGeoLoop(ref currentLoop); nextLoop = currentLoop.next; // ReSharper disable once RedundantAssignment currentLoop = null; } nextPolygon = currentPolygon.next; if (skip) { // do not free the input polygon skip = false; } else { // ReSharper disable once RedundantAssignment currentPolygon = null; } } }
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 CreateLinkedGeo() { var polygon = new LinkedGeoPolygon(); var loop = polygon.AddNewLinkedLoop(); Assert.IsNotNull(loop); var coord = loop.AddLinkedCoord(Vertex1); Assert.IsNotNull(coord); coord = loop.AddLinkedCoord(Vertex2); Assert.IsNotNull(coord); coord = loop.AddLinkedCoord(Vertex3); Assert.IsNotNull(coord); loop = polygon.AddNewLinkedLoop(); Assert.IsNotNull(loop); coord = loop.AddLinkedCoord(Vertex2); Assert.IsNotNull(coord); coord = loop.AddLinkedCoord(Vertex4); Assert.IsNotNull(coord); Assert.AreEqual(1, polygon.CountPolygons); Assert.AreEqual(2, polygon.CountLoops); Assert.AreEqual(3, polygon.First.Count); Assert.AreEqual(2, polygon.Last.Count); var nextPolygon = polygon.AddNewLinkedGeoPolygon(); Assert.IsNotNull(nextPolygon); Assert.AreEqual(2, polygon.CountPolygons); 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 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(); }
/// <summary> /// Given a list of nested containers, find the one most deeply nested. /// </summary> /// <param name="polygons">Polygon containers to check</param> /// <param name="bboxes">Bounding boxes for polygons, used in point-in-poly check</param> /// <param name="polygonCount">Number of polygons in the list</param> /// <returns>Deepest container, or null if list is empty</returns> /// <!-- Based off 3.1.1 --> public static LinkedGeoPolygon findDeepestContainer( ref List <LinkedGeoPolygon> polygons, ref List <BBox> bboxes, int polygonCount) { // Set the initial return value to the first candidate LinkedGeoPolygon parent = polygonCount > 0 ? polygons[0] : null; // If we have multiple polygons, they must be nested inside each other. // Find the innermost polygon by taking the one with the most containers // in the list. if (polygonCount <= 1) { return(parent); } int max = -1; for (int i = 0; i < polygonCount; i++) { int count = countContainers(polygons[i].first, polygons, bboxes, polygonCount); if (count <= max) { continue; } parent = polygons[i]; max = count; } return(parent); }
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(); }
/// <summary> /// Add a new linked loop to the current polygon /// </summary> /// <param name="polygon">Polygon to add loop to</param> /// <returns>Pointer to loop</returns> /// <!-- Based off 3.1.1 --> public static LinkedGeoLoop addNewLinkedLoop(ref LinkedGeoPolygon polygon) { LinkedGeoLoop loop = new LinkedGeoLoop(); if (loop == null) { throw new Exception("FAIL: assert(loop != NULL)"); } return(addLinkedLoop(ref polygon, ref loop)); }
/// <summary> /// Add a linked polygon to the current polygon /// </summary> /// <param name="polygon">Polygon to add link to</param> /// <returns>Pointer to new polygon</returns> /// <!-- Based off 3.1.1 --> public static LinkedGeoPolygon addNewLinkedPolygon(ref LinkedGeoPolygon polygon) { if (polygon.next != null) { throw new Exception("assert(polygon->next == NULL);"); } LinkedGeoPolygon next = new LinkedGeoPolygon(); polygon.next = next; return(next); }
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(); }
/// <summary> /// Count the number of linked loops in a polygon /// </summary> /// <param name="polygon">Polygon to count loops for</param> /// <returns>Count</returns> /// <!-- Based off 3.1.1 --> public static int countLinkedLoops(ref LinkedGeoPolygon polygon) { LinkedGeoLoop loop = polygon.first; int count = 0; while (loop != null) { count++; loop = loop.next; } return(count); }
/// <summary> /// Find the polygon to which a given hole should be allocated. Note that this /// function will return null if no parent is found. /// </summary> /// <param name="loop">Inner loop describing a hole</param> /// <param name="polygon">Head of a linked list of polygons to check</param> /// <param name="bboxes">Bounding boxes for polygons, used in point-in-poly check</param> /// <param name="polygonCount">Number of polygons to check</param> /// <returns>Pointer to parent polygon, or null if not found</returns> /// <!-- Based off 3.1.1 --> public static LinkedGeoPolygon findPolygonForHole( ref LinkedGeoLoop loop, ref LinkedGeoPolygon polygon, ref List <BBox> bboxes, int polygonCount) { // Early exit with no polygons if (polygonCount == 0) { return(null); } // Initialize arrays for candidate loops and their bounding boxes List <LinkedGeoPolygon> candidates = new List <LinkedGeoPolygon>(polygonCount); List <BBox> candidateBBoxes = new List <BBox>(polygonCount); for (var k = 0; k < polygonCount; k++) { candidates.Add(new LinkedGeoPolygon()); candidateBBoxes.Add(new BBox()); } // Find all polygons that contain the loop int candidateCount = 0; int index = 0; var polygonReference = polygon; while (polygonReference != null) { // We are guaranteed not to overlap, so just test the first point var bb = bboxes[index]; if ( pointInsideLinkedGeoLoop( ref polygonReference.first, ref bb, ref loop.first.vertex ) ) { candidates[candidateCount] = polygonReference; candidateBBoxes[candidateCount] = bboxes[index]; candidateCount++; } polygonReference = polygonReference.next; index++; } // The most deeply nested container is the immediate parent LinkedGeoPolygon parent = findDeepestContainer(ref candidates, ref candidateBBoxes, candidateCount); // Free allocated memory candidates = null; candidateBBoxes = null; return(parent); }
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> /// Count the number of polygons in a linked list /// </summary> /// <param name="polygon">Starting polygon</param> /// <returns>Count</returns> /// <!-- Based off 3.1.1 --> public static int countLinkedPolygons(ref LinkedGeoPolygon polygon) { var polyIndex = polygon; int count = 0; while (polyIndex != null) { count++; polyIndex = polyIndex.next; } return(count); }
public void Contiguous2() { List <H3Index> set = new List <H3Index> { 0x8928308291bffff, 0x89283082957ffff }; LinkedGeoPolygon polygon = set.ToLinkedGeoPolygon(); Assert.AreEqual(1, polygon.CountLoops); Assert.IsNotNull(polygon.Loops.First()); Assert.AreEqual(10, polygon.Loops.First().Count); polygon.Clear(); }
public LinkedGeoPolygon(Code.LinkedGeo.LinkedGeoPolygon codeLinkedGeoPolygon) { if (codeLinkedGeoPolygon.first != null) { First = new LinkedGeoLoop(codeLinkedGeoPolygon.first); } if (codeLinkedGeoPolygon.last != null) { Last = new LinkedGeoLoop(codeLinkedGeoPolygon.last); } if (codeLinkedGeoPolygon.next != null) { Next = new LinkedGeoPolygon(codeLinkedGeoPolygon.next); } }
/// <summary> /// Add an existing linked loop to the current polygon /// </summary> /// <param name="polygon">Polygon to add loop to</param> /// <returns>Pointer to loop</returns> /// <!-- Based off 3.1.1 --> public static LinkedGeoLoop addLinkedLoop(ref LinkedGeoPolygon polygon, ref LinkedGeoLoop loop) { LinkedGeoLoop last = polygon.last; if (last == null) { if (polygon.first != null) { throw new Exception("FAIL: assert(polygon->first == NULL)"); } polygon.first = loop; } else { last.next = loop; } polygon.last = loop; return(loop); }
/// <summary> /// Find the polygon to which a given hole should be allocated. Note that this /// function will return null if no parent is found. /// </summary> /// <param name="loop">Inner loop describing a hole</param> /// <param name="polygon">Head of a linked list of polygons to check</param> /// <param name="boxes">Bounding boxes for polygons, used in point-in-poly check</param> /// <param name="polygonCount">Number of polygons to check</param> /// <returns>Pointer to parent polygon, or null if not found</returns> /// <!-- /// linkedGeo.c /// static const LinkedGeoPolygon* findPolygonForHole /// --> private static LinkedGeoPolygon FindPolygonForHole( this LinkedGeoLoop loop, LinkedGeoPolygon polygon, List <BBox> boxes, int polygonCount ) { // Early exit with no polygons if (polygonCount == 0) { return(null); } // Initialize arrays for candidate loops and their bounding boxes var candidates = new List <LinkedGeoPolygon>(); var candidateBoxes = new List <BBox>(); // Find all polygons that contain the loop var index = 0; while (polygon != null) { // We are guaranteed not to overlap, so just test the first point if (polygon.Loops != null && loop.Nodes != null) { if (polygon.Loops.First().PointInside(boxes[index], loop.Nodes.First().Vertex)) { candidates.Add(polygon); candidateBoxes.Add(boxes[index]); } } polygon = polygon.Next; index++; } // The most deeply nested container is the immediate parent var parent = candidates.FindDeepestContainer(candidateBoxes); // Free allocated memory candidates.Clear(); candidateBoxes.Clear(); return((parent == null || parent.CountLoops == 0) ? null : parent); }
public void Clear() { if (First != null) { First.Clear(); First = null; } if (Last != null) { Last.Clear(); Last = null; } if (Next == null) { return; } Next.Clear(); Next = null; }
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(); }
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(); }
/// <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>0 on success, or an error code > 0 for invalid input</returns> /// <!-- Based off 3.1.1 --> public static int normalizeMultiPolygon(ref 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) { return(NORMALIZATION_ERR_MULTIPLE_POLYGONS); } // Count loops, exiting early if there's only one int loopCount = countLinkedLoops(ref root); if (loopCount <= 1) { return(NORMALIZATION_SUCCESS); } int resultCode = NORMALIZATION_SUCCESS; LinkedGeoPolygon polygon = null; LinkedGeoLoop next = new LinkedGeoLoop(); int innerCount = 0; int 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. List <LinkedGeoLoop> innerLoops = new List <LinkedGeoLoop>(loopCount); for (var k = 0; k < loopCount; k++) { innerLoops.Add(new LinkedGeoLoop()); } // Create an array to hold the bounding boxes for the outer loops List <BBox> bboxes = new List <BBox>(loopCount); for (var k = 0; k < loopCount; k++) { bboxes.Add(new BBox()); } // Get the first loop and unlink it from root LinkedGeoLoop loop = root.first; root = new LinkedGeoPolygon(); // Iterate over all loops, moving inner loops into an array and // assigning outer loops to new polygons while (loop != null) { if (isClockwiseLinkedGeoLoop(loop)) { innerLoops[innerCount] = loop; innerCount++; } else { polygon = polygon == null ? root : addNewLinkedPolygon(ref polygon); addLinkedLoop(ref polygon, ref loop); var bb = bboxes[outerCount]; bboxFromLinkedGeoLoop(ref loop, ref bb); bboxes[outerCount] = bb; outerCount++; } // get the next loop and unlink it from this one next = loop.next; loop.next = null; loop = next; } // Find polygon for each inner loop and assign the hole to it for (int i = 0; i < innerCount; i++) { var inner1 = innerLoops[i]; polygon = findPolygonForHole(ref inner1, ref root, ref bboxes, outerCount); if (polygon != null) { var inner2 = innerLoops[i]; addLinkedLoop(ref polygon, ref inner2); innerLoops[i] = inner2; } 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. var inner2 = innerLoops[i]; destroyLinkedGeoLoop(ref inner2); innerLoops[i] = null; resultCode = NORMALIZATION_ERR_UNASSIGNED_HOLES; } } return(resultCode); }
/// <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); }
public static void DestroyLinkedPolygon(ref LinkedGeoPolygon polygon) { polygon.Clear(); polygon = null; }