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;
}
}
}
}