using System; using System.Collections; using System.IO; using GeoAPI.Geometries; using GisSharpBlog.NetTopologySuite.Geometries; using GisSharpBlog.NetTopologySuite.Utilities; using Wintellect.PowerCollections; namespace GisSharpBlog.NetTopologySuite.Noding { /// /// A list of the s present along a noded . /// public class SegmentNodeList : IEnumerable { private readonly IDictionary nodeMap = new OrderedDictionary(); private readonly SegmentString edge; // the parent edge /// /// Initializes a new instance of the class. /// /// The edge. public SegmentNodeList(SegmentString edge) { this.edge = edge; } /// /// /// /// public SegmentString Edge { get { return edge; } } /// /// Adds an intersection into the list, if it isn't already there. /// The input segmentIndex and dist are expected to be normalized. /// /// /// /// The SegmentIntersection found or added. public SegmentNode Add(ICoordinate intPt, int segmentIndex) { var eiNew = new SegmentNode(edge, intPt, segmentIndex, edge.GetSegmentOctant(segmentIndex)); var ei = (SegmentNode) nodeMap[eiNew]; if (ei != null) { // debugging sanity check Assert.IsTrue(ei.Coordinate.Equals2D(intPt), "Found equal nodes with different coordinates"); return ei; } // node does not exist, so create it nodeMap.Add(eiNew, eiNew); return eiNew; } /// /// Creates new edges for all the edges that the intersections in this /// list split the parent edge into. /// Adds the edges to the provided argument list /// (this is so a single list can be used to accumulate all split edges /// for a set of s). /// /// public void AddSplitEdges(IList edgeList) { // ensure that the list has entries for the first and last point of the edge AddEndPoints(); AddCollapsedNodes(); // there should always be at least two entries in the list, since the endpoints are nodes var ie = GetEnumerator(); ie.MoveNext(); var eiPrev = (SegmentNode) ie.Current; while (ie.MoveNext()) { var ei = (SegmentNode) ie.Current; var newEdge = CreateSplitEdge(eiPrev, ei); edgeList.Add(newEdge); eiPrev = ei; } } /// /// /// /// public void Write(StreamWriter outstream) { outstream.Write("Intersections:"); foreach (var obj in this) { var ei = (SegmentNode) obj; ei.Write(outstream); } } /// /// Returns an iterator of SegmentNodes. /// /// An iterator of SegmentNodes. public IEnumerator GetEnumerator() { return nodeMap.Values.GetEnumerator(); } /// /// Adds nodes for the first and last points of the edge. /// private void AddEndPoints() { var maxSegIndex = edge.Count - 1; Add(edge.GetCoordinate(0), 0); Add(edge.GetCoordinate(maxSegIndex), maxSegIndex); } /// /// Adds nodes for any collapsed edge pairs. /// Collapsed edge pairs can be caused by inserted nodes, or they can be /// pre-existing in the edge vertex list. /// In order to provide the correct fully noded semantics, /// the vertex at the base of a collapsed pair must also be added as a node. /// private void AddCollapsedNodes() { IList collapsedVertexIndexes = new ArrayList(); FindCollapsesFromInsertedNodes(collapsedVertexIndexes); FindCollapsesFromExistingVertices(collapsedVertexIndexes); // node the collapses foreach (var obj in collapsedVertexIndexes) { var vertexIndex = (int) obj; Add(edge.GetCoordinate(vertexIndex), vertexIndex); } } /// /// Adds nodes for any collapsed edge pairs /// which are pre-existing in the vertex list. /// /// private void FindCollapsesFromExistingVertices(IList collapsedVertexIndexes) { for (var i = 0; i < edge.Count - 2; i++) { var p0 = edge.GetCoordinate(i); var p1 = edge.GetCoordinate(i + 1); var p2 = edge.GetCoordinate(i + 2); if (p0.Equals2D(p2)) // add base of collapse as node { collapsedVertexIndexes.Add(i + 1); } } } /// /// Adds nodes for any collapsed edge pairs caused by inserted nodes /// Collapsed edge pairs occur when the same coordinate is inserted as a node /// both before and after an existing edge vertex. /// To provide the correct fully noded semantics, /// the vertex must be added as a node as well. /// /// private void FindCollapsesFromInsertedNodes(IList collapsedVertexIndexes) { var collapsedVertexIndex = new int[1]; var ie = GetEnumerator(); ie.MoveNext(); // there should always be at least two entries in the list, since the endpoints are nodes var eiPrev = (SegmentNode) ie.Current; while (ie.MoveNext()) { var ei = (SegmentNode) ie.Current; var isCollapsed = FindCollapseIndex(eiPrev, ei, collapsedVertexIndex); if (isCollapsed) { collapsedVertexIndexes.Add(collapsedVertexIndex[0]); } eiPrev = ei; } } /// /// /// /// /// /// /// private bool FindCollapseIndex(SegmentNode ei0, SegmentNode ei1, int[] collapsedVertexIndex) { // only looking for equal nodes if (!ei0.Coordinate.Equals2D(ei1.Coordinate)) { return false; } var numVerticesBetween = ei1.SegmentIndex - ei0.SegmentIndex; if (!ei1.IsInterior) { numVerticesBetween--; } // if there is a single vertex between the two equal nodes, this is a collapse if (numVerticesBetween == 1) { collapsedVertexIndex[0] = ei0.SegmentIndex + 1; return true; } return false; } /// /// /// /// private void CheckSplitEdgesCorrectness(IList splitEdges) { var edgePts = edge.Coordinates; // check that first and last points of split edges are same as endpoints of edge var split0 = (SegmentString) splitEdges[0]; var pt0 = split0.GetCoordinate(0); if (!pt0.Equals2D(edgePts[0])) { throw new Exception("bad split edge start point at " + pt0); } var splitn = (SegmentString) splitEdges[splitEdges.Count - 1]; var splitnPts = splitn.Coordinates; var ptn = splitnPts[splitnPts.Length - 1]; if (!ptn.Equals2D(edgePts[edgePts.Length - 1])) { throw new Exception("bad split edge end point at " + ptn); } } /// /// Create a new "split edge" with the section of points between /// (and including) the two intersections. /// The label for the new edge is the same as the label for the parent edge. /// /// /// /// private SegmentString CreateSplitEdge(SegmentNode ei0, SegmentNode ei1) { var npts = ei1.SegmentIndex - ei0.SegmentIndex + 2; var lastSegStartPt = edge.GetCoordinate(ei1.SegmentIndex); // if the last intersection point is not equal to the its segment start pt, add it to the points list as well. // (This check is needed because the distance metric is not totally reliable!) // The check for point equality is 2D only - Z values are ignored var useIntPt1 = ei1.IsInterior || !ei1.Coordinate.Equals2D(lastSegStartPt); if (!useIntPt1) { npts--; } var pts = new ICoordinate[npts]; var ipt = 0; pts[ipt++] = new Coordinate(ei0.Coordinate); for (var i = ei0.SegmentIndex + 1; i <= ei1.SegmentIndex; i++) { pts[ipt++] = edge.GetCoordinate(i); } if (useIntPt1) { pts[ipt] = ei1.Coordinate; } return new SegmentString(pts, edge.Data); } } /// /// /// internal class NodeVertexIterator : IEnumerator { private readonly IEnumerator nodeIt; private SegmentNodeList nodeList; private SegmentString edge; private SegmentNode currNode = null; private SegmentNode nextNode = null; private int currSegIndex = 0; /// /// /// /// private NodeVertexIterator(SegmentNodeList nodeList) { this.nodeList = nodeList; edge = nodeList.Edge; nodeIt = nodeList.GetEnumerator(); } /// /// /// public object Current { get { return currNode; } } /// /// Not implemented. /// /// This method is not implemented. [Obsolete("Not implemented!")] public void Remove() { throw new NotSupportedException(GetType().Name); } /// /// /// /// public bool MoveNext() { if (currNode == null) { currNode = nextNode; currSegIndex = currNode.SegmentIndex; ReadNextNode(); return true; } // check for trying to read too far if (nextNode == null) { return false; } if (nextNode.SegmentIndex == currNode.SegmentIndex) { currNode = nextNode; currSegIndex = currNode.SegmentIndex; ReadNextNode(); return true; } if (nextNode.SegmentIndex > currNode.SegmentIndex) {} return false; } /// /// /// public void Reset() { nodeIt.Reset(); } /// /// /// private void ReadNextNode() { if (nodeIt.MoveNext()) { nextNode = (SegmentNode) nodeIt.Current; } else { nextNode = null; } } } }