using System;
using System.Text;
using Core.Gis.GeoApi.Geometries;
using Core.GIS.NetTopologySuite.Algorithm;
namespace Core.GIS.NetTopologySuite.Geometries
{
///
/// Represents a line segment defined by two Coordinates.
/// Provides methods to compute various geometric properties
/// and relationships of line segments.
/// This class is designed to be easily mutable (to the extent of
/// having its contained points public).
/// This supports a common pattern of reusing a single LineSegment
/// object as a way of computing segment properties on the
/// segments defined by arrays or lists of Coordinates.
///
[Serializable]
public class LineSegment : IComparable
{
private ICoordinate p0 = null, p1 = null;
///
///
///
///
///
public LineSegment(ICoordinate p0, ICoordinate p1)
{
this.p0 = p0;
this.p1 = p1;
}
///
///
///
///
public LineSegment(LineSegment ls) : this(ls.p0, ls.p1) {}
///
///
///
public LineSegment() : this(new Coordinate(), new Coordinate()) {}
///
///
///
public ICoordinate P1
{
get
{
return p1;
}
set
{
p1 = value;
}
}
///
///
///
public ICoordinate P0
{
get
{
return p0;
}
set
{
p0 = value;
}
}
///
/// Computes the length of the line segment.
///
/// The length of the line segment.
public double Length
{
get
{
return P0.Distance(P1);
}
}
///
/// Tests whether the segment is horizontal.
///
/// true if the segment is horizontal.
public bool IsHorizontal
{
get
{
return P0.Y == P1.Y;
}
}
///
/// Tests whether the segment is vertical.
///
/// true if the segment is vertical.
public bool IsVertical
{
get
{
return P0.X == P1.X;
}
}
///
/// The angle this segment makes with the x-axis (in radians).
///
public double Angle
{
get
{
return Math.Atan2(P1.Y - P0.Y, P1.X - P0.X);
}
}
///
///
///
///
///
public ICoordinate GetCoordinate(int i)
{
return i == 0 ? P0 : P1;
}
///
///
///
///
public void SetCoordinates(LineSegment ls)
{
SetCoordinates(ls.P0, ls.P1);
}
///
///
///
///
///
public void SetCoordinates(ICoordinate p0, ICoordinate p1)
{
P0.X = p0.X;
P0.Y = p0.Y;
P1.X = p1.X;
P1.Y = p1.Y;
}
///
/// Computes the that lies a given
/// fraction along the line defined by this segment.
///
///
/// A fraction of 0.0 returns the start point of the segment;
/// A fraction of 1.0 returns the end point of the segment.
/// If the fraction is < 0.0 or > 1.0 the point returned
/// will lie before the start or beyond the end of the segment.
///
/// the fraction of the segment length along the line
/// the point at that distance
public Coordinate PointAlong(double segmentLengthFraction)
{
var coord = new Coordinate();
coord.X = p0.X + segmentLengthFraction*(p1.X - p0.X);
coord.Y = p0.Y + segmentLengthFraction*(p1.Y - p0.Y);
return coord;
}
///
/// Determines the orientation of a LineSegment relative to this segment.
/// The concept of orientation is specified as follows:
/// Given two line segments A and L,
/// A is to the left of a segment L if A lies wholly in the
/// closed half-plane lying to the left of L
/// A is to the right of a segment L if A lies wholly in the
/// closed half-plane lying to the right of L
/// otherwise, A has indeterminate orientation relative to L. This
/// happens if A is collinear with L or if A crosses the line determined by L.
///
/// The LineSegment to compare.
///
/// 1 if seg is to the left of this segment,
/// -1 if seg is to the right of this segment,
/// 0 if seg has indeterminate orientation relative to this segment.
///
public int OrientationIndex(LineSegment seg)
{
var orient0 = CGAlgorithms.OrientationIndex(P0, P1, seg.P0);
var orient1 = CGAlgorithms.OrientationIndex(P0, P1, seg.P1);
// this handles the case where the points are Curve or collinear
if (orient0 >= 0 && orient1 >= 0)
{
return Math.Max(orient0, orient1);
}
// this handles the case where the points are R or collinear
if (orient0 <= 0 && orient1 <= 0)
{
return Math.Max(orient0, orient1);
}
// points lie on opposite sides ==> indeterminate orientation
return 0;
}
///
/// Reverses the direction of the line segment.
///
public void Reverse()
{
var temp = P0;
P0 = P1;
P1 = temp;
}
///
/// Puts the line segment into a normalized form.
/// This is useful for using line segments in maps and indexes when
/// topological equality rather than exact equality is desired.
///
public void Normalize()
{
if (P1.CompareTo(P0) < 0)
{
Reverse();
}
}
///
/// Computes the distance between this line segment and another one.
///
///
///
public double Distance(LineSegment ls)
{
return CGAlgorithms.DistanceLineLine(P0, P1, ls.P0, ls.P1);
}
///
/// Computes the distance between this line segment and a point.
///
public double Distance(ICoordinate p)
{
return CGAlgorithms.DistancePointLine(p, P0, P1);
}
///
/// Computes the perpendicular distance between the (infinite) line defined
/// by this line segment and a point.
///
///
///
public double DistancePerpendicular(ICoordinate p)
{
return CGAlgorithms.DistancePointLinePerpendicular(p, P0, P1);
}
///
/// Compute the projection factor for the projection of the point p
/// onto this LineSegment. The projection factor is the constant k
/// by which the vector for this segment must be multiplied to
/// equal the vector for the projection of p.
///
///
///
public double ProjectionFactor(ICoordinate p)
{
if (p.Equals(P0))
{
return 0.0;
}
if (p.Equals(P1))
{
return 1.0;
}
// Otherwise, use comp.graphics.algorithms Frequently Asked Questions method
/* AC dot AB
r = ------------
||AB||^2
r has the following meaning:
r=0 Point = A
r=1 Point = B
r<0 Point is on the backward extension of AB
r>1 Point is on the forward extension of AB
0
/// Compute the projection of a point onto the line determined
/// by this line segment.
/// Note that the projected point may lie outside the line segment.
/// If this is the case, the projection factor will lie outside the range [0.0, 1.0].
///
///
///
public ICoordinate Project(ICoordinate p)
{
if (p.Equals(P0) || p.Equals(P1))
{
return new Coordinate(p);
}
var r = ProjectionFactor(p);
ICoordinate coord = new Coordinate
{
X = P0.X + r*(P1.X - P0.X), Y = P0.Y + r*(P1.Y - P0.Y)
};
return coord;
}
///
/// Project a line segment onto this line segment and return the resulting
/// line segment. The returned line segment will be a subset of
/// the target line line segment. This subset may be null, if
/// the segments are oriented in such a way that there is no projection.
/// Note that the returned line may have zero length (i.e. the same endpoints).
/// This can happen for instance if the lines are perpendicular to one another.
///
/// The line segment to project.
/// The projected line segment, or null if there is no overlap.
public LineSegment Project(LineSegment seg)
{
var pf0 = ProjectionFactor(seg.P0);
var pf1 = ProjectionFactor(seg.P1);
// check if segment projects at all
if (pf0 >= 1.0 && pf1 >= 1.0)
{
return null;
}
if (pf0 <= 0.0 && pf1 <= 0.0)
{
return null;
}
var newp0 = Project(seg.P0);
if (pf0 < 0.0)
{
newp0 = P0;
}
if (pf0 > 1.0)
{
newp0 = P1;
}
var newp1 = Project(seg.P1);
if (pf1 < 0.0)
{
newp1 = P0;
}
if (pf1 > 1.0)
{
newp1 = P1;
}
return new LineSegment(newp0, newp1);
}
///
/// Computes the closest point on this line segment to another point.
///
/// The point to find the closest point to.
///
/// A Coordinate which is the closest point on the line segment to the point p.
///
public ICoordinate ClosestPoint(ICoordinate p)
{
var factor = ProjectionFactor(p);
if (factor > 0 && factor < 1)
{
return Project(p);
}
var dist0 = P0.Distance(p);
var dist1 = P1.Distance(p);
return dist0 < dist1 ? P0 : P1;
}
///
/// Computes the closest points on a line segment.
///
///
///
/// A pair of Coordinates which are the closest points on the line segments.
///
public ICoordinate[] ClosestPoints(LineSegment line)
{
// test for intersection
var intPt = Intersection(line);
if (intPt != null)
{
return new ICoordinate[]
{
intPt,
intPt
};
}
/*
* if no intersection closest pair contains at least one endpoint.
* Test each endpoint in turn.
*/
var closestPt = new ICoordinate[2];
var minDistance = Double.MaxValue;
var close00 = ClosestPoint(line.P0);
minDistance = close00.Distance(line.P0);
closestPt[0] = close00;
closestPt[1] = line.P0;
var close01 = ClosestPoint(line.P1);
double dist = close01.Distance(line.P1);
if (dist < minDistance)
{
minDistance = dist;
closestPt[0] = close01;
closestPt[1] = line.P1;
}
var close10 = line.ClosestPoint(P0);
dist = close10.Distance(P0);
if (dist < minDistance)
{
minDistance = dist;
closestPt[0] = P0;
closestPt[1] = close10;
}
var close11 = line.ClosestPoint(P1);
dist = close11.Distance(P1);
if (dist < minDistance)
{
minDistance = dist;
closestPt[0] = P1;
closestPt[1] = close11;
}
return closestPt;
}
///
/// Computes an intersection point between two segments, if there is one.
/// There may be 0, 1 or many intersection points between two segments.
/// If there are 0, null is returned. If there is 1 or more, a single one
/// is returned (chosen at the discretion of the algorithm). If
/// more information is required about the details of the intersection,
/// the {RobustLineIntersector} class should be used.
///
///
/// An intersection point, or null if there is none.
public ICoordinate Intersection(LineSegment line)
{
LineIntersector li = new RobustLineIntersector();
li.ComputeIntersection(P0, P1, line.P0, line.P1);
if (li.HasIntersection)
{
return li.GetIntersection(0);
}
return null;
}
///
///
///
///
///
///
public static bool operator ==(LineSegment obj1, LineSegment obj2)
{
return Equals(obj1, obj2);
}
///
///
///
///
///
///
public static bool operator !=(LineSegment obj1, LineSegment obj2)
{
return !(obj1 == obj2);
}
///
/// Returns true if other is
/// topologically equal to this LineSegment (e.g. irrespective
/// of orientation).
///
///
/// A LineSegment with which to do the comparison.
///
///
/// true if other is a LineSegment
/// with the same values for the x and y ordinates.
///
public bool EqualsTopologically(LineSegment other)
{
return P0.Equals(other.P0) && P1.Equals(other.P1) ||
P0.Equals(other.P1) && P1.Equals(other.P0);
}
///
/// Returns true if o has the same values for its points.
///
/// A LineSegment with which to do the comparison.
///
/// true if o is a LineSegment
/// with the same values for the x and y ordinates.
///
public override bool Equals(object o)
{
if (o == null)
{
return false;
}
if (!(o is LineSegment))
{
return false;
}
var other = (LineSegment) o;
return p0.Equals(other.p0) && p1.Equals(other.p1);
}
///
///
///
///
public override string ToString()
{
var sb = new StringBuilder("LINESTRING( ");
sb.Append(P0.X).Append(" ");
sb.Append(P0.Y).Append(", ");
sb.Append(P1.X).Append(" ");
sb.Append(P1.Y).Append(")");
return sb.ToString();
}
///
/// Return HashCode.
///
public override int GetHashCode()
{
return base.GetHashCode();
}
///
/// Compares this object with the specified object for order.
/// Uses the standard lexicographic ordering for the points in the LineSegment.
///
///
/// The LineSegment with which this LineSegment
/// is being compared.
///
///
/// A negative integer, zero, or a positive integer as this LineSegment
/// is less than, equal to, or greater than the specified LineSegment.
///
public int CompareTo(object o)
{
var other = (LineSegment) o;
var comp0 = P0.CompareTo(other.P0);
return comp0 != 0 ? comp0 : P1.CompareTo(other.P1);
}
}
}