Пример #1
0
        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);
        }
Пример #2
0
        /// <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);
                }
            }
        }
Пример #3
0
 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));
 }
Пример #4
0
 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));
 }
Пример #5
0
        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);
        }
Пример #6
0
        /// <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);
        }
Пример #7
0
        /// <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);
        }
Пример #8
0
        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);
        }
Пример #9
0
        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);
        }