using System; using System.IO; using System.Text; using Core.Gis.GeoApi.Geometries; using Core.GIS.NetTopologySuite.Algorithm; using Core.GIS.NetTopologySuite.Geometries; using Core.GIS.NetTopologySuite.GeometriesGraph.Index; namespace Core.GIS.NetTopologySuite.GeometriesGraph { /// /// /// public class Edge : GraphComponent { private IEnvelope env; private readonly EdgeIntersectionList eiList = null; private MonotoneChainEdge mce; private bool isIsolated = true; private readonly Depth depth = new Depth(); /// /// /// /// /// public Edge(ICoordinate[] pts, Label label) { DepthDelta = 0; eiList = new EdgeIntersectionList(this); Points = pts; this.label = label; } /// /// /// /// public Edge(ICoordinate[] pts) : this(pts, null) {} /// /// /// public override ICoordinate Coordinate { get { return Points.Length > 0 ? Points[0] : null; } } /// /// /// public override bool IsIsolated { get { return isIsolated; } } /// /// /// public ICoordinate[] Points { get; set; } /// /// /// public int NumPoints { get { return Points.Length; } } /// /// /// public string Name { get; set; } /// /// /// public ICoordinate[] Coordinates { get { return Points; } } /// /// /// public IEnvelope Envelope { get { // compute envelope lazily if (env == null) { env = new Envelope(); for (var i = 0; i < Points.Length; i++) { env.ExpandToInclude(Points[i]); } } return env; } } /// /// /// public Depth Depth { get { return depth; } } /// /// The depthDelta is the change in depth as an edge is crossed from R to L. /// /// The change in depth as the edge is crossed from R to L. public int DepthDelta { get; set; } /// /// /// public int MaximumSegmentIndex { get { return Points.Length - 1; } } /// /// /// public EdgeIntersectionList EdgeIntersectionList { get { return eiList; } } /// /// /// public MonotoneChainEdge MonotoneChainEdge { get { if (mce == null) { mce = new MonotoneChainEdge(this); } return mce; } } /// /// /// public bool IsClosed { get { return Points[0].Equals(Points[Points.Length - 1]); } } /// /// An Edge is collapsed if it is an Area edge and it consists of /// two segments which are equal and opposite (eg a zero-width V). /// public bool IsCollapsed { get { if (!label.IsArea()) { return false; } if (Points.Length != 3) { return false; } if (Points[0].Equals(Points[2])) { return true; } return false; } } /// /// /// public Edge CollapsedEdge { get { var newPts = new ICoordinate[2]; newPts[0] = Points[0]; newPts[1] = Points[1]; var newe = new Edge(newPts, Label.ToLineLabel(label)); return newe; } } /// /// /// public bool Isolated { get { return isIsolated; } set { isIsolated = value; } } /// /// Updates an IM from the label for an edge. /// Handles edges from both L and A geometries. /// /// /// public static void UpdateIM(Label label, IntersectionMatrix im) { im.SetAtLeastIfValid(label.GetLocation(0, Positions.On), label.GetLocation(1, Positions.On), Dimensions.Curve); if (label.IsArea()) { im.SetAtLeastIfValid(label.GetLocation(0, Positions.Left), label.GetLocation(1, Positions.Left), Dimensions.Surface); im.SetAtLeastIfValid(label.GetLocation(0, Positions.Right), label.GetLocation(1, Positions.Right), Dimensions.Surface); } } /// /// /// /// /// public ICoordinate GetCoordinate(int i) { try { return Points[i]; } catch (Exception ex) { Console.WriteLine(ex); throw; } } /// /// Adds EdgeIntersections for one or both /// intersections found for a segment of an edge to the edge intersection list. /// /// /// /// public void AddIntersections(LineIntersector li, int segmentIndex, int geomIndex) { for (var i = 0; i < li.IntersectionNum; i++) { AddIntersection(li, segmentIndex, geomIndex, i); } } /// /// Add an EdgeIntersection for intersection intIndex. /// An intersection that falls exactly on a vertex of the edge is normalized /// to use the higher of the two possible segmentIndexes. /// /// /// /// /// public void AddIntersection(LineIntersector li, int segmentIndex, int geomIndex, int intIndex) { ICoordinate intPt = new Coordinate(li.GetIntersection(intIndex)); var normalizedSegmentIndex = segmentIndex; var dist = li.GetEdgeDistance(geomIndex, intIndex); // normalize the intersection point location var nextSegIndex = normalizedSegmentIndex + 1; if (nextSegIndex < Points.Length) { var nextPt = Points[nextSegIndex]; // Normalize segment index if intPt falls on vertex // The check for point equality is 2D only - Z values are ignored if (intPt.Equals2D(nextPt)) { normalizedSegmentIndex = nextSegIndex; dist = 0.0; } // Add the intersection point to edge intersection list. EdgeIntersectionList.Add(intPt, normalizedSegmentIndex, dist); } } /// /// /// /// /// /// public static bool operator ==(Edge obj1, Edge obj2) { return Equals(obj1, obj2); } /// /// /// /// /// /// public static bool operator !=(Edge obj1, Edge obj2) { return !(obj1 == obj2); } /// /// true if the coordinate sequences of the Edges are identical. /// /// public bool IsPointwiseEqual(Edge e) { if (Points.Length != e.Points.Length) { return false; } for (var i = 0; i < Points.Length; i++) { if (!Points[i].Equals2D(e.Points[i])) { return false; } } return true; } /// /// /// /// public void Write(TextWriter outstream) { outstream.Write("edge " + Name + ": "); outstream.Write("LINESTRING ("); for (var i = 0; i < Points.Length; i++) { if (i > 0) { outstream.Write(","); } outstream.Write(Points[i].X + " " + Points[i].Y); } outstream.Write(") " + label + " " + DepthDelta); } /// /// /// /// public void WriteReverse(TextWriter outstream) { outstream.Write("edge " + Name + ": "); for (var i = Points.Length - 1; i >= 0; i--) { outstream.Write(Points[i] + " "); } outstream.WriteLine(String.Empty); } /// /// Update the IM with the contribution for this component. /// A component only contributes if it has a labelling for both parent geometries. /// /// public override void ComputeIM(IntersectionMatrix im) { UpdateIM(label, im); } /// /// Equals is defined to be: /// e1 equals e2 /// iff /// the coordinates of e1 are the same or the reverse of the coordinates in e2. /// /// public override bool Equals(object o) { if (o == null) { return false; } if (!(o is Edge)) { return false; } return Equals(o as Edge); } /// /// /// /// public override int GetHashCode() { return base.GetHashCode(); } /// /// /// /// public override string ToString() { var sb = new StringBuilder(); sb.Append("edge " + Name + ": "); sb.Append("LINESTRING ("); for (var i = 0; i < Points.Length; i++) { if (i > 0) { sb.Append(","); } sb.Append(Points[i].X + " " + Points[i].Y); } sb.Append(") " + label + " " + DepthDelta); return sb.ToString(); } /// /// Equals is defined to be: /// e1 equals e2 /// iff /// the coordinates of e1 are the same or the reverse of the coordinates in e2. /// /// protected bool Equals(Edge e) { if (Points.Length != e.Points.Length) { return false; } var isEqualForward = true; var isEqualReverse = true; var iRev = Points.Length; for (var i = 0; i < Points.Length; i++) { if (!Points[i].Equals2D(e.Points[i])) { isEqualForward = false; } if (!Points[i].Equals2D(e.Points[--iRev])) { isEqualReverse = false; } if (!isEqualForward && !isEqualReverse) { return false; } } return true; } } }