using System.Collections; using System.IO; using Core.GIS.GeoAPI.Geometries; using Core.GIS.NetTopologySuite.Geometries; using Core.GIS.NetTopologySuite.Utilities; namespace Core.GIS.NetTopologySuite.GeometriesGraph { /// /// A DirectedEdgeStar is an ordered list of outgoing DirectedEdges around a node. /// It supports labelling the edges as well as linking the edges to form both /// MaximalEdgeRings and MinimalEdgeRings. /// public class DirectedEdgeStar : EdgeEndStar { private const int ScanningForIncoming = 1; private const int LinkingToOutgoing = 2; /// /// A list of all outgoing edges in the result, in CCW order. /// private IList resultAreaEdgeList; /// /// /// public Label Label { get; private set; } /// /// /// /// public int GetOutgoingDegree() { int degree = 0; for (IEnumerator it = GetEnumerator(); it.MoveNext();) { DirectedEdge de = (DirectedEdge) it.Current; if (de.IsInResult) { degree++; } } return degree; } /// /// /// /// /// public int GetOutgoingDegree(EdgeRing er) { int degree = 0; for (IEnumerator it = GetEnumerator(); it.MoveNext();) { DirectedEdge de = (DirectedEdge) it.Current; if (de.EdgeRing == er) { degree++; } } return degree; } /// /// /// /// public DirectedEdge GetRightmostEdge() { IList edges = Edges; int size = edges.Count; if (size < 1) { return null; } DirectedEdge de0 = (DirectedEdge) edges[0]; if (size == 1) { return de0; } DirectedEdge deLast = (DirectedEdge) edges[size - 1]; int quad0 = de0.Quadrant; int quad1 = deLast.Quadrant; if (QuadrantOp.IsNorthern(quad0) && QuadrantOp.IsNorthern(quad1)) { return de0; } else if (!QuadrantOp.IsNorthern(quad0) && !QuadrantOp.IsNorthern(quad1)) { return deLast; } else { // edges are in different hemispheres - make sure we return one that is non-horizontal if (de0.Dy != 0) { return de0; } else if (deLast.Dy != 0) { return deLast; } } Assert.ShouldNeverReachHere("found two horizontal edges incident on node"); return null; } /// /// For each dirEdge in the star, merge the label . /// public void MergeSymLabels() { for (IEnumerator it = GetEnumerator(); it.MoveNext();) { DirectedEdge de = (DirectedEdge) it.Current; Label label = de.Label; label.Merge(de.Sym.Label); } } /// /// Update incomplete dirEdge labels from the labelling for the node. /// /// public void UpdateLabelling(Label nodeLabel) { for (IEnumerator it = GetEnumerator(); it.MoveNext();) { DirectedEdge de = (DirectedEdge) it.Current; Label label = de.Label; label.SetAllLocationsIfNull(0, nodeLabel.GetLocation(0)); label.SetAllLocationsIfNull(1, nodeLabel.GetLocation(1)); } } /// /// Traverse the star of DirectedEdges, linking the included edges together. /// To link two dirEdges, the next pointer for an incoming dirEdge /// is set to the next outgoing edge. /// DirEdges are only linked if: /// they belong to an area (i.e. they have sides) /// they are marked as being in the result /// Edges are linked in CCW order (the order they are stored). /// 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). /// PRECONDITION: No pair of dirEdges are both marked as being in the result. /// public void LinkResultDirectedEdges() { // make sure edges are copied to resultAreaEdges list GetResultAreaEdges(); // find first area edge (if any) to start linking at DirectedEdge firstOut = null; DirectedEdge incoming = null; int state = ScanningForIncoming; // link edges in CCW order for (int i = 0; i < resultAreaEdgeList.Count; i++) { DirectedEdge nextOut = (DirectedEdge) resultAreaEdgeList[i]; DirectedEdge nextIn = nextOut.Sym; // skip de's that we're not interested in if (!nextOut.Label.IsArea()) { continue; } // record first outgoing edge, in order to link the last incoming edge if (firstOut == null && nextOut.IsInResult) { firstOut = nextOut; } switch (state) { case ScanningForIncoming: if (!nextIn.IsInResult) { continue; } incoming = nextIn; state = LinkingToOutgoing; break; case LinkingToOutgoing: if (!nextOut.IsInResult) { continue; } incoming.Next = nextOut; state = ScanningForIncoming; break; default: break; } } if (state == LinkingToOutgoing) { if (firstOut == null) { throw new TopologyException("no outgoing dirEdge found", Coordinate); } Assert.IsTrue(firstOut.IsInResult, "unable to link last incoming dirEdge"); incoming.Next = firstOut; } } /// /// /// /// public void LinkMinimalDirectedEdges(EdgeRing er) { // find first area edge (if any) to start linking at DirectedEdge firstOut = null; DirectedEdge incoming = null; int state = ScanningForIncoming; // link edges in CW order for (int i = resultAreaEdgeList.Count - 1; i >= 0; i--) { DirectedEdge nextOut = (DirectedEdge) resultAreaEdgeList[i]; DirectedEdge nextIn = nextOut.Sym; // record first outgoing edge, in order to link the last incoming edge if (firstOut == null && nextOut.EdgeRing == er) { firstOut = nextOut; } switch (state) { case ScanningForIncoming: if (nextIn.EdgeRing != er) { continue; } incoming = nextIn; state = LinkingToOutgoing; break; case LinkingToOutgoing: if (nextOut.EdgeRing != er) { continue; } incoming.NextMin = nextOut; state = ScanningForIncoming; break; default: break; } } if (state == LinkingToOutgoing) { Assert.IsTrue(firstOut != null, "found null for first outgoing dirEdge"); Assert.IsTrue(firstOut.EdgeRing == er, "unable to link last incoming dirEdge"); incoming.NextMin = firstOut; } } /// /// /// public void LinkAllDirectedEdges() { IList temp = Edges; temp = null; //Hack // find first area edge (if any) to start linking at DirectedEdge prevOut = null; DirectedEdge firstIn = null; // link edges in CW order for (int i = edgeList.Count - 1; i >= 0; i--) { DirectedEdge nextOut = (DirectedEdge) edgeList[i]; DirectedEdge nextIn = nextOut.Sym; if (firstIn == null) { firstIn = nextIn; } if (prevOut != null) { nextIn.Next = prevOut; } // record outgoing edge, in order to link the last incoming edge prevOut = nextOut; } firstIn.Next = prevOut; } /// /// Traverse the star of edges, maintaing the current location in the result /// area at this node (if any). /// If any L edges are found in the interior of the result, mark them as covered. /// public void FindCoveredLineEdges() { // Since edges are stored in CCW order around the node, // as we move around the ring we move from the right to the left side of the edge /* * Find first DirectedEdge of result area (if any). * The interior of the result is on the RHS of the edge, * so the start location will be: * - Interior if the edge is outgoing * - Exterior if the edge is incoming */ Locations startLoc = Locations.Null; for (IEnumerator it = GetEnumerator(); it.MoveNext();) { DirectedEdge nextOut = (DirectedEdge) it.Current; DirectedEdge nextIn = nextOut.Sym; if (!nextOut.IsLineEdge) { if (nextOut.IsInResult) { startLoc = Locations.Interior; break; } if (nextIn.IsInResult) { startLoc = Locations.Exterior; break; } } } // no A edges found, so can't determine if Curve edges are covered or not if (startLoc == Locations.Null) { return; } /* * move around ring, keeping track of the current location * (Interior or Exterior) for the result area. * If Curve edges are found, mark them as covered if they are in the interior */ Locations currLoc = startLoc; for (IEnumerator it = GetEnumerator(); it.MoveNext();) { DirectedEdge nextOut = (DirectedEdge) it.Current; DirectedEdge nextIn = nextOut.Sym; if (nextOut.IsLineEdge) { nextOut.Edge.Covered = (currLoc == Locations.Interior); } else { // edge is an Area edge if (nextOut.IsInResult) { currLoc = Locations.Exterior; } if (nextIn.IsInResult) { currLoc = Locations.Interior; } } } } /// /// /// /// public void ComputeDepths(DirectedEdge de) { int edgeIndex = FindIndex(de); int startDepth = de.GetDepth(Positions.Left); int targetLastDepth = de.GetDepth(Positions.Right); // compute the depths from this edge up to the end of the edge array int nextDepth = ComputeDepths(edgeIndex + 1, edgeList.Count, startDepth); // compute the depths for the initial part of the array int lastDepth = ComputeDepths(0, edgeIndex, nextDepth); if (lastDepth != targetLastDepth) { throw new TopologyException("depth mismatch at " + de.Coordinate); } } /// /// Insert a directed edge in the list. /// /// public override void Insert(EdgeEnd ee) { DirectedEdge de = (DirectedEdge) ee; InsertEdgeEnd(de, de); } /// /// Compute the labelling for all dirEdges in this star, as well /// as the overall labelling. /// /// public override void ComputeLabelling(GeometryGraph[] geom) { base.ComputeLabelling(geom); // determine the overall labelling for this DirectedEdgeStar // (i.e. for the node it is based at) Label = new Label(Locations.Null); IEnumerator it = GetEnumerator(); while (it.MoveNext()) { EdgeEnd ee = (EdgeEnd) it.Current; Edge e = ee.Edge; Label eLabel = e.Label; for (int i = 0; i < 2; i++) { Locations eLoc = eLabel.GetLocation(i); if (eLoc == Locations.Interior || eLoc == Locations.Boundary) { Label.SetLocation(i, Locations.Interior); } } } } /// /// /// /// public override void Write(StreamWriter outstream) { for (IEnumerator it = GetEnumerator(); it.MoveNext();) { DirectedEdge de = (DirectedEdge) it.Current; outstream.Write("out "); de.Write(outstream); outstream.WriteLine(); outstream.Write("in "); de.Sym.Write(outstream); outstream.WriteLine(); } } /// /// /// /// private IList GetResultAreaEdges() { if (resultAreaEdgeList != null) { return resultAreaEdgeList; } resultAreaEdgeList = new ArrayList(); for (IEnumerator it = GetEnumerator(); it.MoveNext();) { DirectedEdge de = (DirectedEdge) it.Current; if (de.IsInResult || de.Sym.IsInResult) { resultAreaEdgeList.Add(de); } } return resultAreaEdgeList; } /// /// Compute the DirectedEdge depths for a subsequence of the edge array. /// /// The last depth assigned (from the R side of the last edge visited). private int ComputeDepths(int startIndex, int endIndex, int startDepth) { int currDepth = startDepth; for (int i = startIndex; i < endIndex; i++) { DirectedEdge nextDe = (DirectedEdge) edgeList[i]; nextDe.SetEdgeDepths(Positions.Right, currDepth); currDepth = nextDe.GetDepth(Positions.Left); } return currDepth; } } }