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