Example #1
0
        /// <summary>
        /// Determines whether the specified closed polycurve contains (including the boundary) the
        /// specified test geometry. This method ignores the orientation.
        /// </summary>
        /// <param name="closedPolycurve"></param>
        /// <param name="targetSegments"></param>
        /// <param name="tolerance"></param>
        /// <param name="knownIntersections"></param>
        /// <returns></returns>
        public static bool PolycurveContainsXY(
            [NotNull] ISegmentList closedPolycurve,
            [NotNull] Linestring targetSegments,
            double tolerance,
            IEnumerable <SegmentIntersection> knownIntersections = null)
        {
            if (AreBoundsDisjoint(closedPolycurve, targetSegments, tolerance))
            {
                return(false);
            }

            if (knownIntersections == null)
            {
                knownIntersections =
                    SegmentIntersectionUtils.GetSegmentIntersectionsXY(
                        closedPolycurve, targetSegments, tolerance);
            }

            // TODO: GeomUtils.IEnumerable<IntersectionPoint3D> GetIntersectionPointsWithDeviation() for better performance
            var intersectionPoints =
                GeomTopoOpUtils.GetIntersectionPoints(closedPolycurve, targetSegments, tolerance,
                                                      knownIntersections, false);

            Pnt3D nonIntersectingTargetPnt =
                GetNonIntersectingTargetPoint(targetSegments, intersectionPoints);

            IEnumerable <Pnt3D> checkPoints = nonIntersectingTargetPnt != null
                                                                 ? new[] { nonIntersectingTargetPnt }
                                                                 : targetSegments.GetPoints();

            return(checkPoints.All(p => PolycurveContainsXY(closedPolycurve, p, tolerance)));

            // The check points are assumed not to be on the boundary!
        }
Example #2
0
 public byte[] WriteMultipolygon(MultiPolycurve multipolygon,
                                 Ordinates ordinates = Ordinates.Xyz)
 {
     return(WriteMultipolygon(
                GeomTopoOpUtils.GetConnectedComponents(multipolygon, double.Epsilon).ToList(),
                ordinates));
 }
Example #3
0
        /// <summary>
        /// Determines whether this linestring has linear self-intersections, e.g. because it is
        /// partially vertical.
        /// </summary>
        /// <param name="tolerance"></param>
        /// <returns></returns>
        public bool HasLinearSelfIntersections(double tolerance)
        {
            // Some segment must be covered by other segments.
            for (var i = 0; i < _segments.Count; i++)
            {
                Line3D segment = _segments[i];

                if (segment.StartPoint.EqualsXY(segment.EndPoint, tolerance))
                {
                    // vertical
                    continue;
                }

                var segmentIntersectingLines =
                    GeomTopoOpUtils.GetLinearSelfIntersectionsXY(this, i, tolerance);

                if (segmentIntersectingLines.Count == 0)
                {
                    continue;
                }

                return(true);
            }

            return(false);
        }
Example #4
0
        /// <summary>
        /// Returns the subcurve consisting of the points between the specified start location
        /// along the linestring and the end. If this linestring is closed and
        /// <paramref name="fullRing"/> is true, the resulting linestring will be a closed
        /// linestring that crosses the null point and ends at the start location.
        /// </summary>
        /// <param name="startSegmentIdx"></param>
        /// <param name="startRatioAlong"></param>
        /// <param name="clonePoints"></param>
        /// <param name="fullRing"></param>
        /// <returns></returns>
        public Linestring GetSubcurve(int startSegmentIdx, double startRatioAlong,
                                      bool clonePoints,
                                      bool fullRing = false)
        {
            if (startSegmentIdx < SegmentCount - 1 || startRatioAlong < 1)
            {
                Linestring toEndSubcurve =
                    GetSubcurve(startSegmentIdx, startRatioAlong, SegmentCount - 1, 1, clonePoints);

                if (IsClosed && fullRing)
                {
                    double epsilon = MathUtils.GetDoubleSignificanceEpsilon(XMax, YMax);

                    Linestring fromStartSubcurve =
                        GetSubcurve(0, 0, startSegmentIdx, startRatioAlong, clonePoints);

                    return(GeomTopoOpUtils.MergeConnectedLinestrings(
                               new List <Linestring> {
                        toEndSubcurve, fromStartSubcurve
                    }, null, epsilon));
                }

                return(toEndSubcurve);
            }

            // The specified start is at the end point
            if (IsClosed)
            {
                return(new Linestring(GetPoints(0, null, clonePoints)));
            }

            throw new ArgumentOutOfRangeException(nameof(startSegmentIdx),
                                                  "Start must be before the last point for un-closed linestring.");
        }
Example #5
0
        private static Linestring CreateWithBoundaryLoop(Linestring containingSourceRing,
                                                         Linestring touchingInteriorRing,
                                                         IntersectionPoint3D touchIntersection,
                                                         double tolerance)
        {
            double sourceTouchPointRatio;
            int    sourceTouchSegmentIdx =
                touchIntersection.GetLocalSourceIntersectionSegmentIdx(
                    containingSourceRing, out sourceTouchPointRatio);
            List <Linestring> subcurves = new List <Linestring>();

            subcurves.Add(containingSourceRing.GetSubcurve(
                              0, 0, sourceTouchSegmentIdx, sourceTouchPointRatio,
                              false));

            double targetTouchPointRatio;
            int    targetTouchSegmentIdx =
                touchIntersection.GetLocalTargetIntersectionSegmentIdx(
                    touchingInteriorRing, out targetTouchPointRatio);

            subcurves.Add(touchingInteriorRing.GetSubcurve(
                              targetTouchSegmentIdx, targetTouchPointRatio, false,
                              true));

            subcurves.Add(containingSourceRing.GetSubcurve(
                              sourceTouchSegmentIdx, sourceTouchPointRatio,
                              containingSourceRing.SegmentCount - 1, 1, false));

            Linestring withBoundaryLoop =
                GeomTopoOpUtils.MergeConnectedLinestrings(subcurves, null, tolerance);

            return(withBoundaryLoop);
        }
        private static IEnumerable <Pnt3DIntersectionPoint> GetIntersections(
            Linestring lines1,
            Linestring lines2,
            ISpatialReference spatialReference,
            double xyTolerance)
        {
            // Increase tolerance by epsilon to find intersections that differ exactly by tolerance
            xyTolerance +=
                MathUtils.GetDoubleSignificanceEpsilon(lines1.XMax, lines1.YMax);

            var intersectionPoints =
                GeomTopoOpUtils.GetIntersectionPoints(lines1, lines2, xyTolerance);
            var nanTargetVertices =
                intersectionPoints.Where(p => double.IsNaN(p.VirtualTargetVertex));

            foreach (var nanVertex in nanTargetVertices)
            {
                _msg.Warn(
                    $"Target vertex is NaN: {nanVertex}; linestring1: {lines1}; linestring2: {lines2}");
            }

            return(intersectionPoints
                   .Where(p => !double.IsNaN(p.VirtualTargetVertex))
                   .Where(p => p.Type == IntersectionPointType.Crossing)
                   .Select(p => new Pnt3DIntersectionPoint(p.Point,
                                                           spatialReference,
                                                           GetZ(p, lines2))));
        }
Example #7
0
        private static IList <RingGroup> CutRingGroupPlanar(
            [NotNull] RingGroup ringGroup,
            [NotNull] IPolyline cutLine,
            double tolerance,
            ChangeAlongZSource zSource,
            double zTolerance)
        {
            cutLine = GeometryFactory.Clone(cutLine);

            if (GeometryUtils.IsZAware(cutLine) &&
                zSource != ChangeAlongZSource.Target)
            {
                ((IZAware)cutLine).DropZs();
            }

            Plane3D plane = null;

            if (zSource == ChangeAlongZSource.SourcePlane)
            {
                plane = ChangeAlongZUtils.GetSourcePlane(
                    ringGroup.ExteriorRing.GetPoints().ToList(), zTolerance);
            }

            GeometryUtils.Simplify(cutLine, true, true);

            MultiPolycurve cutLinestrings = new MultiPolycurve(
                GeometryUtils.GetPaths(cutLine).Select(
                    cutPath => GeometryConversionUtils.CreateLinestring(cutPath)));

            IList <RingGroup> resultGroups =
                GeomTopoOpUtils.CutPlanar(ringGroup, cutLinestrings, tolerance);

            foreach (RingGroup resultPoly in resultGroups)
            {
                resultPoly.Id = ringGroup.Id;

                if (plane != null)
                {
                    resultPoly.AssignUndefinedZs(plane);
                }
                else
                {
                    resultPoly.InterpolateUndefinedZs();
                }
            }

            Marshal.ReleaseComObject(cutLine);

            if (resultGroups.Count == 0)
            {
                // Return uncut original
                resultGroups.Add(ringGroup);
            }

            return(resultGroups);
        }
Example #8
0
        public bool IsOnTheRightSide([NotNull] ISegmentList source,
                                     [NotNull] Pnt3D testPoint,
                                     bool disregardOrientation = false)
        {
            Assert.True(source.IsClosed, "Source must be closed ring(s)");

            Linestring sourceRing = source.GetPart(SourcePartIndex);

            double sourceRatio;
            int    sourceSegmentIdx =
                GetLocalSourceIntersectionSegmentIdx(sourceRing, out sourceRatio);

            Line3D sourceSegment = sourceRing[sourceSegmentIdx];

            if (sourceRatio > 0 && sourceRatio < 1)
            {
                // The intersection is on the source segment's interior
                return(sourceSegment.IsLeftXY(testPoint) < 0);
            }

            Line3D previousSegment, nextSegment;

            // Intersection at source vertex 0 or 1 -> get the 2 adjacent segments
            // ReSharper disable once CompareOfFloatsByEqualityOperator
            if (sourceRatio == 0)
            {
                previousSegment =
                    sourceRing.PreviousSegmentInRing(sourceSegmentIdx, true);

                nextSegment = SegmentIntersection.IsSourceZeroLength2D
                                                      ? sourceRing.NextSegmentInRing(sourceSegmentIdx, true)
                                                      : sourceSegment;
            }
            else             // sourceRatio == 1
            {
                previousSegment = SegmentIntersection.IsSourceZeroLength2D
                                                          ? sourceRing.PreviousSegmentInRing(
                    sourceSegmentIdx, true)
                                                          : sourceSegment;
                nextSegment = sourceRing.NextSegmentInRing(sourceSegmentIdx, true);
            }

            bool result = GeomTopoOpUtils.IsOnTheRightSide(previousSegment.StartPoint, Point,
                                                           nextSegment.EndPoint, testPoint);

            if (!disregardOrientation && sourceRing.ClockwiseOriented == false)
            {
                result = !result;
            }

            return(result);
        }
Example #9
0
        public bool IsVerticalRing(double tolerance)
        {
            // NOTE: Just returning ClockwiseOriented == null reports incorrect results if the bottom
            // right has a cut-back.

            if (!IsClosed)
            {
                return(false);
            }

            // Optimization using 2D area
            double area2D = Math.Abs(GetArea2D());

            double areaTolerance = GetLength2D() * tolerance;

            if (area2D > areaTolerance)
            {
                return(false);
            }

            // Each segment must be vertical or completely covered by other segments.
            for (var i = 0; i < _segments.Count; i++)
            {
                Line3D segment = _segments[i];

                if (segment.StartPoint.EqualsXY(segment.EndPoint, tolerance))
                {
                    // vertical
                    continue;
                }

                var segmentIntersectingLines =
                    GeomTopoOpUtils.GetLinearSelfIntersectionsXY(this, i, tolerance);

                if (segmentIntersectingLines.Count != 1)
                {
                    return(false);
                }

                Linestring segmentIntersectingLine = segmentIntersectingLines[0];

                if (segmentIntersectingLine.StartPoint.EqualsXY(segment.StartPoint, tolerance) &&
                    segmentIntersectingLine.EndPoint.EqualsXY(segment.EndPoint, tolerance))
                {
                    continue;
                }

                return(false);
            }

            return(true);
        }
Example #10
0
            public bool Equals(Linestring x, Linestring y)
            {
                if (x == null && y == null)
                {
                    return(true);
                }

                if (x == null || y == null)
                {
                    return(false);
                }

                return(GeomTopoOpUtils.AreEqualXY(x, y, _tolerance));
            }
Example #11
0
        /// <summary>
        /// Determines whether the provided rings touch in xy. If available, the spatial index of the second ring is used.
        /// </summary>
        /// <param name="ring1"></param>
        /// <param name="ring2"></param>
        /// <param name="tolerance"></param>
        /// <param name="ringsAreDisjoint">Whether the two rings are disjoint.</param>
        /// <param name="disregardRingOrientation">Whether the ring orientation can be used to determine
        /// the inside/outside. Use true if the rings are known not to be simple in terms of ring orientation.</param>
        /// <param name="ring2CanHaveLinearSelfIntersections">Whether the second ring can have self-intersections,
        /// for example because it is vertical. If true, it will be reported as touching if it does not intersect
        /// the interior of ring1, even if there are linear intersections in both directions. This does not exactly
        /// conform with Clementini logic.</param>
        /// <returns></returns>
        public static bool TouchesXY([NotNull] Linestring ring1,
                                     [NotNull] Linestring ring2,
                                     double tolerance,
                                     out bool ringsAreDisjoint,
                                     bool disregardRingOrientation            = false,
                                     bool ring2CanHaveLinearSelfIntersections = false)
        {
            Assert.ArgumentCondition(ring1.IsClosed && ring2.IsClosed,
                                     "Both rings must be closed.");

            IEnumerable <SegmentIntersection> segmentIntersections =
                SegmentIntersectionUtils.GetSegmentIntersectionsXY(
                    ring1, ring2, tolerance);

            ringsAreDisjoint = true;

            var allIntersections = new List <SegmentIntersection>();

            // Quick checks and list collection
            bool?linearIntersectionsInverted = null;

            foreach (SegmentIntersection intersection in segmentIntersections)
            {
                ringsAreDisjoint = false;

                if (intersection.SingleInteriorIntersectionFactor != null)
                {
                    return(false);
                }

                if (intersection.HasLinearIntersection)
                {
                    //// TODO/Experimental: Test with more exception cases
                    //if (intersection.IsPotentialPseudoLinearIntersection(
                    //	    ring1[intersection.SourceIndex], ring2[intersection.TargetIndex],
                    //	    tolerance))
                    //{
                    //	continue;
                    //}
                    if (!intersection.IsSegmentZeroLength2D)
                    {
                        if (!disregardRingOrientation &&
                            !intersection.LinearIntersectionInOppositeDirection)
                        {
                            // Optimization if the ring orientation is known to be correct
                            return(false);
                        }

                        if (linearIntersectionsInverted == null)
                        {
                            linearIntersectionsInverted =
                                intersection.LinearIntersectionInOppositeDirection;
                        }
                        else if (linearIntersectionsInverted.Value !=
                                 intersection.LinearIntersectionInOppositeDirection &&
                                 !ring2CanHaveLinearSelfIntersections)
                        {
                            return(false);
                        }
                    }
                }

                allIntersections.Add(intersection);
            }

            IList <IntersectionPoint3D> intersectionPoints = GeomTopoOpUtils.GetIntersectionPoints(
                ring1, ring2, tolerance, allIntersections, false);

            if (HasSourceCrossingIntersections(ring1, ring2, intersectionPoints))
            {
                return(false);
            }

            // No intersection or no deviation of target from source or all deviations to the same side
            bool contained = RingsContainEachOther(ring1, ring2, intersectionPoints, tolerance,
                                                   disregardRingOrientation,
                                                   ring2CanHaveLinearSelfIntersections);

            ringsAreDisjoint = ringsAreDisjoint && !contained;

            return(!contained);
        }
Example #12
0
        private static IList <IGeometry> TryCutXY(
            IPolygon inputPolygon,
            IPolyline cutPolyline,
            ChangeAlongZSource zSource)
        {
            // TODO:
            // In order to avoid the arbitrary grouping of multipart polygons, try to apply left/right logic
            // provided by GeomTopoOpUtils

            double tolerance  = GeometryUtils.GetXyTolerance(inputPolygon);
            double zTolerance = GeometryUtils.GetZTolerance(inputPolygon);

            MultiPolycurve inputMultipoly =
                GeometryConversionUtils.CreateMultiPolycurve(inputPolygon);

            var cutLine = GeometryFactory.Clone(cutPolyline);

            if (GeometryUtils.IsZAware(cutLine) &&
                zSource != ChangeAlongZSource.Target)
            {
                ((IZAware)cutLine).DropZs();
            }

            Plane3D plane = null;

            if (zSource == ChangeAlongZSource.SourcePlane)
            {
                plane = ChangeAlongZUtils.GetSourcePlane(
                    inputMultipoly.GetPoints().ToList(), zTolerance);
            }

            GeometryUtils.Simplify(cutLine, true, true);

            MultiPolycurve cutLinestrings = GeometryConversionUtils.CreateMultiPolycurve(cutLine);

            bool isMultipart = GeometryUtils.GetExteriorRingCount(inputPolygon) > 1;

            IList <MultiLinestring> resultGeoms =
                GeomTopoOpUtils.CutXY(inputMultipoly, cutLinestrings, tolerance, !isMultipart);

            var result = new List <IGeometry>();

            foreach (MultiLinestring resultPoly in resultGeoms)
            {
                if (plane != null)
                {
                    resultPoly.AssignUndefinedZs(plane);
                }
                else
                {
                    resultPoly.InterpolateUndefinedZs();
                }

                result.Add(GeometryConversionUtils.CreatePolygon(inputPolygon,
                                                                 resultPoly.GetLinestrings()));
            }

            Marshal.ReleaseComObject(cutLine);

            return(result.Count == 0 ? null : result);
        }
Example #13
0
        /// <summary>
        /// Moves from one intersection to the next by
        /// - first following the source
        /// - at each intersection taking the right-most (alternatively, the lef-most, depending
        /// on <see cref="PreferredTurnDirection"/>) turn until reaching the start again.
        /// </summary>
        /// <param name="startIntersections"></param>
        /// <returns></returns>
        public IList <Linestring> FollowSubcurvesClockwise(
            [NotNull] ICollection <IntersectionPoint3D> startIntersections)
        {
            IList <Linestring> result = new List <Linestring>();
            var subcurveInfos         = new List <IntersectionRun>();

            while (startIntersections.Count > 0)
            {
                subcurveInfos.Clear();
                bool onlyFollowingSource = true;

                IntersectionPoint3D startIntersection = startIntersections.First();
                startIntersections.Remove(startIntersection);

                IntersectionPoint3D previousIntersection = startIntersection;

                Pnt3D ringStart = null;
                foreach (IntersectionRun next in NavigateSubcurves(startIntersection))
                {
                    IntersectionPoint3D nextIntersection = next.NextIntersection;
                    subcurveInfos.Add(next);

                    Pnt3D startPoint;
                    if (next.ContainsSourceStart(out startPoint))
                    {
                        ringStart = startPoint;
                    }

                    if (next.ContinuingOnSource)
                    {
                        if (startIntersections.Contains(previousIntersection))
                        {
                            // Remove, if we follow the source through other start. This happens with vertical rings.
                            //startIntersections.Remove(previousIntersection);
                        }
                    }
                    else
                    {
                        onlyFollowingSource = false;
                    }

                    previousIntersection = nextIntersection;
                }

                // At some point the result must deviate from source otherwise the target does not cut it
                if (!onlyFollowingSource)
                {
                    // Finish ring
                    result.Add(GeomTopoOpUtils.MergeConnectedLinestrings(
                                   subcurveInfos.Select(i => i.Subcurve).ToList(), ringStart,
                                   Tolerance));

                    foreach (int sourceIdx in subcurveInfos.Select(
                                 i => i.NextIntersection.SourcePartIndex))
                    {
                        IntersectedSourcePartIndexes.Add(sourceIdx);
                    }

                    foreach (int targetIdx in subcurveInfos.Select(
                                 i => i.NextIntersection.TargetPartIndex))
                    {
                        IntersectedTargetPartIndexes.Add(targetIdx);
                    }
                }
            }

            return(result);
        }
Example #14
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);
        }