Esempio n. 1
0
        /// <summary>
        /// Traverses edges from edgeStart which
        /// lie in a single line (have degree = 2).
        /// <para/>
        /// The direction of the linework is preserved as far as possible.
        /// Specifically, the direction of the line is determined
        /// by the start edge direction. This implies
        /// that if all edges are reversed, the created line
        /// will be reversed to match.
        /// (Other more complex strategies would be possible.
        /// E.g. using the direction of the majority of segments,
        /// or preferring the direction of the A edges.)
        /// </summary>
        private LineString BuildLine(OverlayEdge node)
        {
            var pts = new CoordinateList();

            pts.Add(node.Orig, false);

            bool isForward = node.IsForward;

            var e = node;

            do
            {
                e.MarkVisitedBoth();
                e.AddCoordinates(pts);

                // end line if next vertex is a node
                if (DegreeOfLines(e.SymOE) != 2)
                {
                    break;
                }
                e = NextLineEdgeUnvisited(e.SymOE);
                // e will be null if next edge has been visited, which indicates a ring
            }while (e != null);

            var ptsOut = pts.ToCoordinateArray(isForward);

            var line = _geometryFactory.CreateLineString(ptsOut);

            return(line);
        }
        /// <summary>
        /// Determines the <see cref="Location"/> for an edge within an Area geometry
        /// via point-in-polygon location.
        /// <para/>
        /// NOTE this is only safe to use for disconnected edges,
        /// since the test is carried out against the original input geometry,
        /// and precision reduction may cause incorrect results for edges
        /// which are close enough to a boundary to become connected.
        /// </summary>
        /// <param name="geomIndex">The parent geometry index</param>
        /// <param name="edge">The edge to locate</param>
        /// <returns>The location of the edge.</returns>
        private Location LocateEdge(int geomIndex, OverlayEdge edge)
        {
            var loc     = _inputGeometry.LocatePointInArea(geomIndex, edge.Orig);
            var edgeLoc = loc != Location.Exterior ? Location.Interior : Location.Exterior;

            return(edgeLoc);
        }
        /// <summary>
        /// Tests if an edge of the maximal edge ring is already linked into
        /// a minimal <see cref="OverlayEdgeRing"/>. If so, this node has already been processed
        /// earlier in the maximal edgering linking scan.
        /// </summary>
        /// <param name="edge">An edge of a maximal edgering</param>
        /// <param name="maxRing">The maximal edgering</param>
        /// <returns><c>true</c> if the edge has already been linked into a minimal edgering.</returns>
        private static bool IsAlreadyLinked(OverlayEdge edge, MaximalEdgeRing maxRing)
        {
            bool isLinked = edge.MaxEdgeRing == maxRing &&
                            edge.IsResultLinked;

            return(isLinked);
        }
        /// <summary>
        /// Determines the location of an edge relative to a target input geometry.
        /// The edge has no location information
        /// because it is disconnected from other
        /// edges that would provide that information.
        /// The location is determined by checking
        /// if the edge lies inside the target geometry area(if any).
        /// </summary>
        /// <param name="edge">The edge to label</param>
        /// <param name="geomIndex">The input geometry to label against</param>
        private void LabelDisconnectedEdge(OverlayEdge edge, int geomIndex)
        {
            var label = edge.Label;

            //Assert.isTrue(label.isNotPart(geomIndex));

            /*
             * if target geom is not an area then
             * edge must be EXTERIOR, since to be
             * INTERIOR it would have been labelled
             * when it was created.
             */
            if (!_inputGeometry.IsArea(geomIndex))
            {
                label.SetLocationAll(geomIndex, Location.Exterior);
                return;
            }
            ;

            //Debug.println("\n------  labelDisconnectedEdge - geomIndex= " + geomIndex);
            //Debug.print("BEFORE: " + edge.toStringNode());

            /*
             * Locate edge in input area using a Point-In-Poly check.
             * This should be safe even with precision reduction,
             * because since the edge has remained disconnected
             * its interior-exterior relationship
             * can be determined relative to the original input geometry.
             */
            //int edgeLoc = locateEdge(geomIndex, edge);
            var edgeLoc = LocateEdgeBothEnds(geomIndex, edge);

            label.SetLocationAll(geomIndex, edgeLoc);
            //Debug.print("AFTER: " + edge.toStringNode());
        }
 private static OverlayEdge SelectMaxOutEdge(OverlayEdge currOut, MaximalEdgeRing maxEdgeRing)
 {
     // select if currOut edge is part of this max ring
     if (currOut.MaxEdgeRing == maxEdgeRing)
     {
         return(currOut);
     }
     // otherwise skip this edge
     return(null);
 }
Esempio n. 6
0
        private LineString ToLine(OverlayEdge edge)
        {
            bool isForward = edge.IsForward;
            var  pts       = new CoordinateList();

            pts.Add(edge.Orig, false);
            edge.AddCoordinates(pts);

            var ptsOut = pts.ToCoordinateArray(isForward);
            var line   = _geometryFactory.CreateLineString(ptsOut);

            return(line);
        }
        /// <summary>
        /// Marks an edge which forms part of the boundary of the result area.
        /// This is determined by the overlay operation being executed,
        /// and the location of the edge.
        /// The relevant location is either the right side of a boundary edge,
        /// or the line location of a non-boundary edge.
        /// </summary>
        /// <param name="e">The edge to mark</param>
        /// <param name="overlayOpCode">The overlay operation</param>
        public void MarkInResultArea(OverlayEdge e, SpatialFunction overlayOpCode)
        {
            var label = e.Label;

            if (label.IsBoundaryEither &&
                OverlayNG.IsResultOfOp(
                    overlayOpCode,
                    label.GetLocationBoundaryOrLine(0, Position.Right, e.IsForward),
                    label.GetLocationBoundaryOrLine(1, Position.Right, e.IsForward)))
            {
                e.MarkInResultArea();
            }
            //Debug.println("markInResultArea: " + e);
        }
        /// <summary>
        /// Determines the {@link Location} for an edge within an Area geometry
        /// via point-in-polygon location,
        /// by checking that both endpoints are interior to the target geometry.
        /// Checking both endpoints ensures correct results in the presence of topology collapse.
        /// <para/>
        /// NOTE this is only safe to use for disconnected edges,
        /// since the test is carried out against the original input geometry,
        /// and precision reduction may cause incorrect results for edges
        /// which are close enough to a boundary to become connected.
        /// </summary>
        /// <param name="geomIndex">The parent geometry index</param>
        /// <param name="edge">The edge to locate</param>
        /// <returns>The location of the edge</returns>
        private Location LocateEdgeBothEnds(int geomIndex, OverlayEdge edge)
        {
            /*
             * To improve the robustness of the point location,
             * check both ends of the edge.
             * Edge is only labelled INTERIOR if both ends are.
             */
            var  locOrig = _inputGeometry.LocatePointInArea(geomIndex, edge.Orig);
            var  locDest = _inputGeometry.LocatePointInArea(geomIndex, edge.Dest);
            bool isInt   = locOrig != Location.Exterior && locDest != Location.Exterior;
            var  edgeLoc = isInt ? Location.Interior : Location.Exterior;

            return(edgeLoc);
        }
Esempio n. 9
0
        /// <summary>
        /// Computes the degree of the line edges incident on a node
        /// </summary>
        /// <param name="node">Node to compute degree for</param>
        /// <returns>Degree of the node line edges</returns>
        private static int DegreeOfLines(OverlayEdge node)
        {
            int degree = 0;
            var e      = node;

            do
            {
                if (e.IsInResultLine)
                {
                    degree++;
                }
                e = e.ONextOE;
            } while (e != node);
            return(degree);
        }
        /// <summary>
        /// Finds a boundary edge for this geom originating at the given
        /// node, if one exists.
        /// A boundary edge should exist if this is a node on the boundary
        /// of the parent area geometry.
        /// </summary>
        /// <param name="nodeEdge">An edge for this node</param>
        /// <param name="geomIndex">The parent geometry index</param>
        /// <returns>A boundary edge, or <c>null</c> if no boundary edge exists</returns>
        private static OverlayEdge FindPropagationStartEdge(OverlayEdge nodeEdge, int geomIndex)
        {
            var eStart = nodeEdge;

            do
            {
                var label = eStart.Label;
                if (label.IsBoundary(geomIndex))
                {
                    Assert.IsTrue(label.HasSides(geomIndex));
                    return(eStart);
                }
                eStart = eStart.ONextOE;
            } while (eStart != nodeEdge);
            return(null);
        }
Esempio n. 11
0
        /// <summary>
        /// Finds the next edge around a node which forms
        /// part of a result line.
        /// </summary>
        /// <param name="node">A line edge originating at the node to be scanned</param>
        /// <returns>The next line edge, or null if there is none
        /// </returns>
        private static OverlayEdge NextLineEdgeUnvisited(OverlayEdge node)
        {
            var e = node;

            do
            {
                e = e.ONextOE;
                if (e.IsVisited)
                {
                    continue;
                }
                if (e.IsInResultLine)
                {
                    return(e);
                }
            } while (e != node);
            return(null);
        }
        private static OverlayEdge LinkMaxInEdge(OverlayEdge currOut,
                                                 OverlayEdge currMaxRingOut,
                                                 MaximalEdgeRing maxEdgeRing)
        {
            var currIn = currOut.SymOE;

            // currIn is not in this max-edgering, so keep looking
            if (currIn.MaxEdgeRing != maxEdgeRing)
            {
                return(currMaxRingOut);
            }

            //Debug.println("Found result in-edge:  " + currIn);

            currIn.NextResult = currMaxRingOut;
            //Debug.println("Linked Min Edge:  " + currIn + " -> " + currMaxRingOut);
            // return null to indicate to scan for the next max-ring out-edge
            return(null);
        }
        private void LabelCollapsedEdge(OverlayEdge edge, int geomIndex)
        {
            //Debug.println("\n------  labelCollapsedEdge - geomIndex= " + geomIndex);
            //Debug.print("BEFORE: " + edge.toStringNode());
            var label = edge.Label;

            if (!label.IsCollapse(geomIndex))
            {
                return;
            }

            /*
             * This must be a collapsed edge which is disconnected
             * from any area edges (e.g. a fully collapsed shell or hole).
             * It can be labeled according to its parent source ring role.
             */
            label.SetLocationCollapse(geomIndex);
            //Debug.print("AFTER: " + edge.toStringNode());
        }
        private static void PropagateLinearLocationAtNode(OverlayEdge eNode, int geomIndex,
                                                          bool isInputLine, Stack <OverlayEdge> edgeStack)
        {
            // Note: edgeStack is a Deque<OverlayEdge> in JTS. It is used as a Stack.
            var lineLoc = eNode.Label.GetLineLocation(geomIndex);

            /*
             * If the parent geom is a Line
             * then only propagate EXTERIOR locations.
             */
            if (isInputLine && lineLoc != Location.Exterior)
            {
                return;
            }

            //Debug.println("check " + geomIndex + " from: " + eNode);
            var e = eNode.ONextOE;

            do
            {
                var label = e.Label;
                //Debug.println(""*** setting "+ geomIndex + ": " + e);
                if (label.IsLineLocationUnknown(geomIndex))
                {
                    /*
                     * If edge is not a boundary edge,
                     * its location is now known for this area
                     */
                    label.SetLocationLine(geomIndex, lineLoc);
                    //Debug.println("propagateLineLocationAtNode - setting "+ index + ": " + e);

                    /*
                     * Add sym edge to stack for graph traversal
                     * (Don't add e itself, since e origin node has now been scanned)
                     */
                    edgeStack.Push(e.SymOE);
                }
                e = e.ONextOE;
            } while (e != eNode);
        }
        private void AttachEdges(OverlayEdge startEdge)
        {
            var edge = startEdge;

            do
            {
                if (edge == null)
                {
                    throw new TopologyException("Ring edge is null");
                }
                if (edge.MaxEdgeRing == this)
                {
                    throw new TopologyException($"Ring edge visited twice at {edge.Coordinate}", edge.Coordinate);
                }
                if (edge.NextResultMax == null)
                {
                    throw new TopologyException($"Ring edge missing at {edge.Dest}", edge.Dest);
                }
                edge.MaxEdgeRing = this;
                edge             = edge.NextResultMax;
            } while (edge != startEdge);
        }
        public static string ToString(OverlayEdge nodeEdge)
        {
            var orig = nodeEdge.Orig;
            var sb   = new StringBuilder();

            sb.AppendFormat("Node( {0} )\n", WKTWriter.Format(orig));
            var e = nodeEdge;

            do
            {
                sb.Append($"  -> {e}");
                if (e.IsResultLinked)
                {
                    sb.Append(" Link: ");
                    sb.Append(e.NextResult);
                }
                sb.Append("\n");
                e = e.ONextOE;
            } while (e != nodeEdge);

            return(sb.ToString());
        }
        /// <summary>
        /// Links the edges of a <see cref="MaximalEdgeRing"/> around this node
        /// into minimal edge rings (<see cref="OverlayEdgeRing"/>s).
        /// Minimal ring edges are linked in the opposite orientation (CW)
        /// to the maximal ring.
        /// This changes self-touching rings into a two or more separate rings,
        /// as per the OGC SFS polygon topology semantics.
        /// This relinking must be done to each max ring separately,
        /// rather than all the node result edges, since there may be
        /// more than one max ring incident at the node.
        /// </summary>
        /// <param name="maxRing">The maximal ring to link</param>
        /// <param name="nodeEdge">An edge originating at this node</param>
        private static void LinkMinRingEdgesAtNode(OverlayEdge nodeEdge, MaximalEdgeRing maxRing)
        {
            //Assert.isTrue(nodeEdge.isInResult(), "Attempt to link non-result edge");

            /*
             * The node edge is an out-edge,
             * so it is the first edge linked
             * with the next CCW in-edge
             */
            var endOut         = nodeEdge;
            var currMaxRingOut = endOut;
            var currOut        = endOut.ONextOE;

            //Debug.println("\n------  Linking node MIN ring edges");
            //Debug.println("BEFORE: " + toString(nodeEdge));
            do
            {
                if (IsAlreadyLinked(currOut.SymOE, maxRing))
                {
                    return;
                }

                if (currMaxRingOut == null)
                {
                    currMaxRingOut = SelectMaxOutEdge(currOut, maxRing);
                }
                else
                {
                    currMaxRingOut = LinkMaxInEdge(currOut, currMaxRingOut, maxRing);
                }
                currOut = currOut.ONextOE;
            } while (currOut != endOut);
            //Debug.println("AFTER: " + toString(nodeEdge));
            if (currMaxRingOut != null)
            {
                throw new TopologyException("Unmatched edge found during min-ring linking", nodeEdge.Coordinate);
            }
        }
 private static string LabelForResult(OverlayEdge edge)
 {
     return(edge.Label.ToString(edge.IsForward)
            + (edge.IsInResultArea ? " Res" : ""));
 }
        /// <summary>
        /// Scans around a node CCW, propagating the side labels
        /// for a given area geometry to all edges (and their sym)
        /// with unknown locations for that geometry.
        /// </summary>
        /// <param name="nodeEdge"></param>
        /// <param name="geomIndex">The geometry to propagate locations for</param>
        public void PropagateAreaLocations(OverlayEdge nodeEdge, int geomIndex)
        {
            /*
             * Only propagate for area geometries
             */
            if (!_inputGeometry.IsArea(geomIndex))
            {
                return;
            }

            /*
             * No need to propagate if node has only one edge.
             * This handles dangling edges created by overlap limiting.
             */
            if (nodeEdge.Degree() == 1)
            {
                return;
            }

            var eStart = FindPropagationStartEdge(nodeEdge, geomIndex);

            // no labelled edge found, so nothing to propagate
            if (eStart == null)
            {
                return;
            }

            // initialize currLoc to location of L side
            var currLoc = eStart.GetLocation(geomIndex, Position.Left);
            var e       = eStart.ONextOE;

            //Debug.println("\npropagateSideLabels geomIndex = " + geomIndex + " : " + eStart);
            //Debug.print("BEFORE: " + toString(eStart));

            do
            {
                var label = e.Label;
                if (!label.IsBoundary(geomIndex))
                {
                    /*
                     * If this is not a Boundary edge for this input area,
                     * its location is now known relative to this input area
                     */
                    label.SetLocationLine(geomIndex, currLoc);
                }
                else
                {
                    // must be a boundary edge
                    Assert.IsTrue(label.HasSides(geomIndex));

                    /*
                     *  This is a boundary edge for the input area geom.
                     *  Update the current location from its labels.
                     *  Also check for topological consistency.
                     */
                    var locRight = e.GetLocation(geomIndex, Position.Right);
                    if (locRight != currLoc)
                    {
                        /*
                         * Debug.println("side location conflict: index= " + geomIndex + " R loc "
                         + Location.toLocationSymbol(locRight) + " <>  curr loc " + Location.toLocationSymbol(currLoc)
                         + " for " + e);
                         + //*/
                        throw new TopologyException("side location conflict: arg " + geomIndex, e.Coordinate);
                    }
                    var locLeft = e.GetLocation(geomIndex, Position.Left);
                    if (locLeft == Location.Null)
                    {
                        Assert.ShouldNeverReachHere("found single null side at " + e);
                    }
                    currLoc = locLeft;
                }
                e = e.ONextOE;
            } while (e != eStart);
            //Debug.print("AFTER: " + toString(eStart));
        }
        /// <summary>
        /// Traverses the star of edges originating at a node
        /// and links consecutive result edges together
        /// into <b>maximal</b> edge rings.
        /// To link two edges the <c>resultNextMax</c> pointer
        /// for an <b>incoming</b> result edge
        /// is set to the next <b>outgoing</b> result edge.
        /// <para/>
        /// Edges are linked when:
        /// <list type="bullet">
        /// <item><description>they belong to an area (i.e.they have sides)</description></item>
        /// <item><description>they are marked as being in the result</description></item>
        /// </list>
        /// <para/>
        /// Edges are linked in CCW order
        /// (which is the order they are linked in the underlying graph).
        /// This means that rings have their face on the Right
        /// (in other words,
        /// the topological location of the face is given by the RHS label of the DirectedEdge).
        /// This produces rings with CW orientation.
        /// <para/>
        /// PRECONDITIONS: <br/>
        /// - This edge is in the result<br/>
        /// - This edge is not yet linked<br/>
        /// - The edge and its sym are NOT both marked as being in the result
        /// </summary>
        public static void LinkResultAreaMaxRingAtNode(OverlayEdge nodeEdge)
        {
            Assert.IsTrue(nodeEdge.IsInResultArea, "Attempt to link non-result edge");
            // assertion is only valid if building a polygonal geometry (ie not a coverage)
            //Assert.isTrue(! nodeEdge.symOE().isInResultArea(), "Found both half-edges in result");

            /*
             * Since the node edge is an out-edge,
             * make it the last edge to be linked
             * by starting at the next edge.
             * The node edge cannot be an in-edge as well,
             * but the next one may be the first in-edge.
             */
            var endOut  = nodeEdge.ONextOE;
            var currOut = endOut;
            //Debug.println("\n------  Linking node MAX edges");
            //Debug.println("BEFORE: " + toString(nodeEdge));
            int         state        = STATE_FIND_INCOMING;
            OverlayEdge currResultIn = null;

            do
            {
                /**
                 * If an edge is linked this node has already been processed
                 * so can skip further processing
                 */
                if (currResultIn != null && currResultIn.IsResultMaxLinked)
                {
                    return;
                }

                switch (state)
                {
                case STATE_FIND_INCOMING:
                    var currIn = currOut.SymOE;
                    if (!currIn.IsInResultArea)
                    {
                        break;
                    }
                    currResultIn = currIn;
                    state        = STATE_LINK_OUTGOING;
                    //Debug.println("Found result in-edge:  " + currResultIn);
                    break;

                case STATE_LINK_OUTGOING:
                    if (!currOut.IsInResultArea)
                    {
                        break;
                    }
                    // link the in edge to the out edge
                    currResultIn.NextResultMax = currOut;
                    state = STATE_FIND_INCOMING;
                    //Debug.println("Linked Max edge:  " + currResultIn + " -> " + currOut);
                    break;
                }
                currOut = currOut.ONextOE;
            } while (currOut != endOut);
            //Debug.println("AFTER: " + toString(nodeEdge));
            if (state == STATE_LINK_OUTGOING)
            {
                //Debug.print(firstOut == null, this);
                throw new TopologyException("no outgoing edge found", nodeEdge.Coordinate);
            }
        }
 public MaximalEdgeRing(OverlayEdge e)
 {
     _startEdge = e;
     AttachEdges(e);
 }
Esempio n. 22
0
        private readonly List <OverlayEdgeRing> _holes = new List <OverlayEdgeRing>(); // a list of EdgeRings which are holes in this EdgeRing

        public OverlayEdgeRing(OverlayEdge start, GeometryFactory geometryFactory)
        {
            Edge        = start;
            Coordinates = ComputeRingPts(start);
            ComputeRing(Coordinates, geometryFactory);
        }