using System; using GeoAPI.Geometries; using GisSharpBlog.NetTopologySuite.Geometries; namespace GisSharpBlog.NetTopologySuite.LinearReferencing { /// /// Represents a location along a or . /// The referenced geometry is not maintained within this location, /// but must be provided for operations which require it. /// Various methods are provided to manipulate the location value /// and query the geometry it references. /// public class LinearLocation : IComparable, IComparable, ICloneable { /// /// Initializes a new instance of the class: /// creates a location referring to the start of a linear geometry. /// public LinearLocation() { SegmentFraction = 0.0; SegmentIndex = 0; ComponentIndex = 0; } /// /// Initializes a new instance of the class: /// creates a location referring to the start of a linear geometry. /// /// Index of the segment. /// The segment fraction. public LinearLocation(int segmentIndex, double segmentFraction) : this(0, segmentIndex, segmentFraction) {} /// /// Initializes a new instance of the class: /// creates a location referring to the start of a linear geometry. /// /// Index of the component. /// Index of the segment. /// The segment fraction. public LinearLocation(int componentIndex, int segmentIndex, double segmentFraction) { ComponentIndex = componentIndex; SegmentIndex = segmentIndex; SegmentFraction = segmentFraction; Normalize(); } /// /// Gets the component index for this location. /// public int ComponentIndex { get; private set; } /// /// Gets the segment index for this location. /// public int SegmentIndex { get; private set; } /// /// Gets the segment fraction for this location. /// public double SegmentFraction { get; private set; } /// /// Tests whether this location refers to a vertex: /// returns true if the location is a vertex. /// public bool IsVertex { get { return SegmentFraction <= 0.0 || SegmentFraction >= 1.0; } } /// /// Gets a location which refers to the end of a linear . /// /// The linear geometry. /// A new LinearLocation. public static LinearLocation GetEndLocation(IGeometry linear) { if (!(linear is ILineString || linear is IMultiLineString)) { string message = String.Format("Expected {0} or {1}, but was {2}", typeof(ILineString), typeof(IMultiLineString), linear.GetType()); throw new ArgumentException(message, "linear"); } LinearLocation loc = new LinearLocation(); loc.SetToEnd(linear); return loc; } /// /// Computes the of a point a given fraction /// along the line segment (p0, p1). /// If the fraction is greater than 1.0 the last /// point of the segment is returned. /// If the fraction is less than or equal to 0.0 the first point /// of the segment is returned. /// /// The first point of the line segment. /// The last point of the line segment. /// The length to the desired point. /// public static ICoordinate PointAlongSegmentByFraction(ICoordinate p0, ICoordinate p1, double fraction) { if (fraction <= 0.0) { return p0; } if (fraction >= 1.0) { return p1; } double x = (p1.X - p0.X)*fraction + p0.X; double y = (p1.Y - p0.Y)*fraction + p0.Y; // interpolate Z value. If either input Z is NaN, result z will be NaN as well. double z = (p1.Z - p0.Z)*fraction + p0.Z; return new Coordinate(x, y, z); } /// /// Ensures the indexes are valid for a given linear . /// /// A linear geometry. public void Clamp(IGeometry linear) { if (ComponentIndex >= linear.NumGeometries) { SetToEnd(linear); return; } if (SegmentIndex >= linear.NumPoints) { ILineString line = (ILineString) linear.GetGeometryN(ComponentIndex); SegmentIndex = line.NumPoints - 1; SegmentFraction = 1.0; } } /// /// Snaps the value of this location to /// the nearest vertex on the given linear , /// if the vertex is closer than . /// /// A linear geometry. /// The minimum allowable distance to a vertex. public void SnapToVertex(IGeometry linearGeom, double minDistance) { if (SegmentFraction <= 0.0 || SegmentFraction >= 1.0) { return; } double segLen = GetSegmentLength(linearGeom); double lenToStart = SegmentFraction*segLen; double lenToEnd = segLen - lenToStart; if (lenToStart <= lenToEnd && lenToStart < minDistance) { SegmentFraction = 0.0; } else if (lenToEnd <= lenToStart && lenToEnd < minDistance) { SegmentFraction = 1.0; } } /// /// Gets the length of the segment in the given /// Geometry containing this location. /// /// A linear geometry. /// The length of the segment. public double GetSegmentLength(IGeometry linearGeom) { ILineString lineComp = (ILineString) linearGeom.GetGeometryN(ComponentIndex); // ensure segment index is valid int segIndex = SegmentIndex; if (SegmentIndex >= lineComp.NumPoints - 1) { segIndex = lineComp.NumPoints - 2; } ICoordinate p0 = lineComp.GetCoordinateN(segIndex); ICoordinate p1 = lineComp.GetCoordinateN(segIndex + 1); return p0.Distance(p1); } /// /// Sets the value of this location to /// refer the end of a linear geometry. /// /// The linear geometry to set. public void SetToEnd(IGeometry linear) { ComponentIndex = linear.NumGeometries - 1; ILineString lastLine = (ILineString) linear.GetGeometryN(ComponentIndex); SegmentIndex = lastLine.NumPoints - 1; SegmentFraction = 1.0; } /// /// Gets the along the /// given linear which is /// referenced by this location. /// /// A linear geometry. /// The at the location. public ICoordinate GetCoordinate(IGeometry linearGeom) { ILineString lineComp = (ILineString) linearGeom.GetGeometryN(ComponentIndex); ICoordinate p0 = lineComp.GetCoordinateN(SegmentIndex); if (SegmentIndex >= lineComp.NumPoints - 1) { return p0; } ICoordinate p1 = lineComp.GetCoordinateN(SegmentIndex + 1); return PointAlongSegmentByFraction(p0, p1, SegmentFraction); } /// /// Tests whether this location refers to a valid /// location on the given linear . /// /// A linear geometry. /// true if this location is valid. public bool IsValid(IGeometry linearGeom) { if (ComponentIndex < 0 || ComponentIndex >= linearGeom.NumGeometries) { return false; } ILineString lineComp = (ILineString) linearGeom.GetGeometryN(ComponentIndex); if (SegmentIndex < 0 || SegmentIndex > lineComp.NumPoints) { return false; } if (SegmentIndex == lineComp.NumPoints && SegmentFraction != 0.0) { return false; } if (SegmentFraction < 0.0 || SegmentFraction > 1.0) { return false; } return true; } /// /// Compares this object with the specified index values for order. /// /// The component index. /// The segment index. /// The segment fraction. /// /// A negative integer, zero, or a positive integer as this LineStringLocation /// is less than, equal to, or greater than the specified locationValues. /// public int CompareLocationValues(int componentIndex1, int segmentIndex1, double segmentFraction1) { // compare component indices if (ComponentIndex < componentIndex1) { return -1; } if (ComponentIndex > componentIndex1) { return 1; } // compare segments if (SegmentIndex < segmentIndex1) { return -1; } if (SegmentIndex > segmentIndex1) { return 1; } // same segment, so compare segment fraction if (SegmentFraction < segmentFraction1) { return -1; } if (SegmentFraction > segmentFraction1) { return 1; } // same location return 0; } /// /// Compares two sets of location values for order. /// /// The first component index. /// The first segment index. /// The first segment fraction. /// The second component index. /// The second segment index. /// The second segment fraction. /// /// A negative integer, zero, or a positive integer /// as the first set of location values is less than, equal to, /// or greater than the second set of locationValues. /// public static int CompareLocationValues( int componentIndex0, int segmentIndex0, double segmentFraction0, int componentIndex1, int segmentIndex1, double segmentFraction1) { // compare component indices if (componentIndex0 < componentIndex1) { return -1; } if (componentIndex0 > componentIndex1) { return 1; } // compare segments if (segmentIndex0 < segmentIndex1) { return -1; } if (segmentIndex0 > segmentIndex1) { return 1; } // same segment, so compare segment fraction if (segmentFraction0 < segmentFraction1) { return -1; } if (segmentFraction0 > segmentFraction1) { return 1; } // same location return 0; } /// /// Copies this location. /// /// A copy of this location. public object Clone() { return new LinearLocation(SegmentIndex, SegmentFraction); } /// /// Compares the current instance with another object of the same type. /// /// /// The LineStringLocation with which this /// Coordinate is being compared. /// /// /// A negative integer, zero, or a positive integer as this /// LineStringLocation is less than, equal to, /// or greater than the specified LineStringLocation. /// /// /// is not the same type as this instance. /// public int CompareTo(object obj) { LinearLocation other = (LinearLocation) obj; return CompareTo(other); } /// /// Compares the current instance with another object of the same type. /// /// /// The LineStringLocation with which this /// Coordinate is being compared. /// /// /// A negative integer, zero, or a positive integer as this /// LineStringLocation is less than, equal to, /// or greater than the specified LineStringLocation. /// public int CompareTo(LinearLocation other) { // compare component indices if (ComponentIndex < other.ComponentIndex) { return -1; } if (ComponentIndex > other.ComponentIndex) { return 1; } // compare segments if (SegmentIndex < other.SegmentIndex) { return -1; } if (SegmentIndex > other.SegmentIndex) { return 1; } // same segment, so compare segment fraction if (SegmentFraction < other.SegmentFraction) { return -1; } if (SegmentFraction > other.SegmentFraction) { return 1; } // same location return 0; } /// /// Ensures the individual values are locally valid. /// Does not ensure that the indexes are valid for /// a particular linear geometry. /// private void Normalize() { if (SegmentFraction < 0.0) { SegmentFraction = 0.0; } if (SegmentFraction > 1.0) { SegmentFraction = 1.0; } if (ComponentIndex < 0) { ComponentIndex = 0; SegmentIndex = 0; SegmentFraction = 0.0; } if (SegmentIndex < 0) { SegmentIndex = 0; SegmentFraction = 0.0; } if (SegmentFraction == 1.0) { SegmentFraction = 0.0; SegmentIndex += 1; } } } }