private static List <Linestring> RemoveContainedExistingIslands <T>( [NotNull] T fromPolygon, [NotNull] Linestring cookieCutter, double tolerance) where T : MultiLinestring { List <Linestring> existingInnerRings = fromPolygon .GetLinestrings() .Where(l => l.ClockwiseOriented == false) .ToList(); Assert.NotNull(cookieCutter.ClockwiseOriented); var containedExistingIslands = new List <Linestring>(); foreach (Linestring existingInnerRing in existingInnerRings) { bool?islandWithinCookie = GeomRelationUtils.AreaContainsXY( cookieCutter, existingInnerRing.StartPoint, tolerance, true); // TODO: handle the case where the inner ring touches cutter in StartPoint if (islandWithinCookie == true) { containedExistingIslands.Add(existingInnerRing); fromPolygon.RemoveLinestring(existingInnerRing); } } return(containedExistingIslands); }
/// <summary> /// Gets the segment intersections between the specified path and multiline-string. /// </summary> /// <param name="segmentList1"></param> /// <param name="segmentList2"></param> /// <param name="tolerance"></param> /// <param name="optimizeLinearIntersections">If true, an optimiziation that improves the /// performance when linear intersections are present. If a segmentList1 segment is equal /// to a linestring2 segment it will directly be used. Other intersections will be missed. /// </param> /// <returns>The segment intersections with the index of the cut part of multiLinestring2</returns> public static IEnumerable <SegmentIntersection> GetSegmentIntersectionsXY( [NotNull] ISegmentList segmentList1, [NotNull] ISegmentList segmentList2, double tolerance, bool optimizeLinearIntersections = false) { if (GeomRelationUtils.AreBoundsDisjoint(segmentList1, segmentList2, tolerance)) { yield break; } SegmentIntersection previousLinearIntersection = null; for (var lineIdx = 0; lineIdx < segmentList1.SegmentCount; lineIdx++) { Line3D line = segmentList1.GetSegment(lineIdx); foreach (SegmentIntersection result in GetSegmentIntersectionsXY( lineIdx, line, segmentList2, tolerance, previousLinearIntersection)) { if (optimizeLinearIntersections && result.SegmentsAreEqualInXy) { previousLinearIntersection = result; } yield return(result); } } }
public bool ExtentIntersectsXY( double xMin, double yMin, double xMax, double yMax, double tolerance) { return(!GeomRelationUtils.AreBoundsDisjoint(XMin, YMin, XMax, YMax, xMin, yMin, xMax, yMax, tolerance)); }
public bool ExtentsIntersectXY(double otherXMin, double otherYMin, double otherXMax, double otherYMax, double tolerance) { return(!GeomRelationUtils.AreBoundsDisjoint(XMin, YMin, XMax, YMax, otherXMin, otherYMin, otherXMax, otherYMax, tolerance)); }
private static int AssignInteriorRing([NotNull] Linestring interiorRing, IEnumerable <RingGroup> resultPolys, double tolerance) { int assignmentCount = 0; foreach (RingGroup resultPoly in resultPolys) { if (GeomRelationUtils.PolycurveContainsXY( resultPoly.ExteriorRing, interiorRing.StartPoint, tolerance)) { resultPoly.AddInteriorRing(interiorRing); assignmentCount++; } } return(assignmentCount); }
/// <summary> /// Returns source rings disjoint from target and target rings disjoint from source. /// </summary> /// <param name="ringPredicate"></param> /// <returns></returns> private IEnumerable <Linestring> GetRingsDisjointFromOtherPoly( Predicate <Linestring> ringPredicate) { var result = new List <Linestring>(); // W.r.t. AreaContainsXY returning null: if the start point is on the boundary there // are duplicate rings which need to be identified elsewhere. ISegmentList target = _subcurveNavigator.Target; foreach (Linestring ring in _subcurveNavigator.GetNonIntersectedSourceRings() .Where(r => ringPredicate(r))) { bool?contains = GeomRelationUtils.AreaContainsXY( target, ring.StartPoint, _subcurveNavigator.Tolerance); if (contains == false) { // disjoint result.Add(ring); } } ISegmentList source = _subcurveNavigator.Source; foreach (Linestring ring in _subcurveNavigator.GetNonIntersectedTargets() .Where(r => ringPredicate(r))) { bool?contains = GeomRelationUtils.AreaContainsXY( source, ring.StartPoint, _subcurveNavigator.Tolerance); if (contains == false) { result.Add(ring); } } return(result); }
/// <summary> /// Creates the list of result ring groups from the processed inner/outer rings and the /// unprocessed outer rings by: /// - Adding processed outer rings to the output and remove them from the input collection /// - For each processed inner rings, that is contained in an un-processed outer ring: /// - Add the unprocessed outer ring together with the inner ring to the result. /// - Remove both the unprocessed outer and the inner ring from the respective input collection. /// </summary> /// <param name="processedResultRings"></param> /// <param name="unprocessedOuterRings"></param> /// <returns></returns> private IList <RingGroup> AssignToResultRingGroups( ICollection <Linestring> processedResultRings, ICollection <Linestring> unprocessedOuterRings) { var result = new List <RingGroup>(); foreach (Linestring processedResultRing in processedResultRings.ToList()) { if (processedResultRing.ClockwiseOriented != false) { result.Add(new RingGroup(processedResultRing)); processedResultRings.Remove(processedResultRing); } else { // Intersected (processed) inner rings: // Find the containing un-cut outer ring, assign and remove from un-processed list Linestring containing = unprocessedOuterRings.FirstOrDefault( o => o.ClockwiseOriented == true && GeomRelationUtils.PolycurveContainsXY( o, processedResultRing.StartPoint, _subcurveNavigator.Tolerance)); if (containing != null) { unprocessedOuterRings.Remove(containing); // Add at the beginning to boost performance, assuming a few (or one) large rings contains everything result.Insert(0, new RingGroup(containing, new[] { processedResultRing })); // remove from the list, the remaining inner rings will be assigned afterwards; processedResultRings.Remove(processedResultRing); } } } return(result); }
private static int GetContainingRingIndex([NotNull] MultiLinestring polygon, [NotNull] Linestring containedRing, double tolerance, out bool ringsAreEqual, out IntersectionPoint3D touchPoint) { IList <IntersectionPoint3D> intersectionPoints = GeomTopoOpUtils.GetIntersectionPoints(polygon, containedRing, tolerance); ringsAreEqual = false; touchPoint = null; // Equal to outer ring -> removes outer ring in original // or equal to inner ring -> ignore cookie cutter if (intersectionPoints.Count == 2 && intersectionPoints[0].SourcePartIndex == intersectionPoints[1].SourcePartIndex && intersectionPoints[0].Point.Equals(intersectionPoints[1].Point) && intersectionPoints[0].Type == IntersectionPointType.LinearIntersectionStart && intersectionPoints[1].Type == IntersectionPointType.LinearIntersectionEnd) { ringsAreEqual = true; return(intersectionPoints[0].SourcePartIndex); } var outerRingIntersections = intersectionPoints .Where(i => polygon.GetLinestring(i.SourcePartIndex).ClockwiseOriented == true) .ToList(); // Touching outer ring in one point -> boundary loop in original if (outerRingIntersections.Count > 0) { Assert.True(outerRingIntersections.Count < 2, "Unexpected number of touching points."); touchPoint = outerRingIntersections[0]; return(touchPoint.SourcePartIndex); } // Inside an inner ring -> ignore cookie cutter for (int i = 0; i < polygon.PartCount; i++) { Linestring ring = polygon.GetLinestring(i); if (ring.ClockwiseOriented == true) { continue; } int currentIdx = i; bool?areaContainsXY = GeomRelationUtils.AreaContainsXY( ring, containedRing, intersectionPoints.Where(ip => ip.SourcePartIndex == currentIdx), tolerance, true); if (areaContainsXY == true) { return(i); } } // Inside an outer ring but not an inner ring: Add as island for (int i = 0; i < polygon.PartCount; i++) { Linestring ring = polygon.GetLinestring(i); if (ring.ClockwiseOriented == false) { continue; } if (GeomRelationUtils.AreaContainsXY(ring, containedRing.StartPoint, tolerance) == true) { return(i); } } return(-1); }
private static bool TryCutCookie <T>([NotNull] T polygon, [NotNull] Linestring cookieCutter, double tolerance, out RingGroup resultCookie, bool allowEmptyResults = false) where T : MultiLinestring { resultCookie = null; if (!GeomRelationUtils.PolycurveContainsXY( polygon, cookieCutter, tolerance)) { return(false); } // Remove pre-existing interior rings that are completely within cookie cutter List <Linestring> containedExistingIslands = RemoveContainedExistingIslands(polygon, cookieCutter, tolerance); Linestring interiorRing = cookieCutter.Clone(); Assert.True(interiorRing.IsClosed, "Interior ring is not closed"); interiorRing.TryOrientAnticlockwise(); IntersectionPoint3D outerRingIntersection; bool ringsAreEqual; int parentRingIdx = GetContainingRingIndex( polygon, interiorRing, tolerance, out ringsAreEqual, out outerRingIntersection); Assert.False(parentRingIdx < 0, "No parent ring found"); Linestring containingRing = polygon.GetLinestring(parentRingIdx); if (containingRing.ClockwiseOriented == false) { // The cutter is completely within an existing island -> ignore (keep existing ring) return(false); } if (ringsAreEqual) { // The cutter is equal to the found ring. Positive rings cancel each other out: if (!allowEmptyResults) { return(false); } polygon.RemoveLinestring(containingRing); } else if (outerRingIntersection == null) { // The cutter is completely within an existing outer ring -> add as island polygon.AddLinestring(interiorRing); } else { // create boundary loop: polygon.RemoveLinestring(containingRing); Linestring withBoundaryLoop = CreateWithBoundaryLoop(containingRing, interiorRing, outerRingIntersection, tolerance); polygon.InsertLinestring(parentRingIdx, withBoundaryLoop); } resultCookie = RingGroup.CreateProperlyOriented(cookieCutter.Clone()); foreach (Linestring unusedCutRing in containedExistingIslands) { RingGroup cookieInCookie; Assert.True(TryCutCookie(resultCookie, unusedCutRing, tolerance, out cookieInCookie), "Inner ring in cookie cutter cannot be cut from result cookie"); } return(true); }