package com.vividsolutions.jump.algorithm;
import com.vividsolutions.jts.geom.*;
import com.vividsolutions.jts.util.Assert;
/**
* Computes a substring of a {@link LineString}
* between given distances along the line.
* <ul>
* <li>The distances are clipped to the actual line length
* <li>If the start distance is equal to the end distance,
* a zero-length line with two identical points is returned
* <li>FUTURE: If the start distance is greater than the end distance,
* an inverted section of the line is returned
* </ul>
* <p>
* FUTURE: should handle startLength > endLength, and flip the returned
* linestring. Also should handle negative lengths (they are measured from end
* of line backwards).
*/
// Martin made a decision to create this duplicate of a class from JCS.
// [Jon Aquino 2004-10-25]
public class LengthSubstring
{
public static LineString getSubstring(LineString line, double startLength,
double endLength) {
LengthSubstring ls = new LengthSubstring(line);
return ls.getSubstring(startLength, endLength);
}
private LineString line;
public LengthSubstring(LineString line) {
this.line = line;
}
public LineString getSubstring(double startDistance, double endDistance)
{
// future: if start > end, flip values and return an inverted line
Assert.isTrue(startDistance <= endDistance, "inverted distances not currently supported");
Coordinate[] coordinates = line.getCoordinates();
// check for a zero-length segment and handle appropriately
if (endDistance <= 0.0) {
return line.getFactory().createLineString(
new Coordinate[] { coordinates[0], coordinates[0]});
}
if (startDistance >= line.getLength()) { return line.getFactory()
.createLineString(
new Coordinate[] { coordinates[coordinates.length - 1],
coordinates[coordinates.length - 1]}); }
if (startDistance < 0.0) {
startDistance = 0.0;
}
return computeSubstring(startDistance, endDistance);
}
/**
* Assumes input is strictly valid (e.g. startDist < endDistance)
*
* @param startDistance
* @param endDistance
* @return
*/
private LineString computeSubstring(double startDistance, double endDistance)
{
Coordinate[] coordinates = line.getCoordinates();
CoordinateList newCoordinates = new CoordinateList();
double segmentStartDistance = 0.0;
double segmentEndDistance = 0.0;
boolean started = false;
int i = 0;
LineSegment segment = new LineSegment();
while (i < coordinates.length - 1 && endDistance > segmentEndDistance) {
segment.p0 = coordinates[i];
segment.p1 = coordinates[i + 1];
i++;
segmentStartDistance = segmentEndDistance;
segmentEndDistance = segmentStartDistance + segment.getLength();
if (startDistance > segmentEndDistance)
continue;
if (startDistance >= segmentStartDistance
&& startDistance < segmentEndDistance) {
newCoordinates.add(LocatePoint.pointAlongSegment(segment.p0, segment.p1,
startDistance - segmentStartDistance), false);
}
/*
if (startDistance >= segmentStartDistance
&& startDistance == segmentEndDistance) {
newCoordinates.add(new Coordinate(segment.p1), false);
}
*/
if (endDistance >= segmentEndDistance) {
newCoordinates.add(new Coordinate(segment.p1), false);
}
if (endDistance >= segmentStartDistance
&& endDistance < segmentEndDistance) {
newCoordinates.add(LocatePoint.pointAlongSegment(segment.p0, segment.p1,
endDistance - segmentStartDistance), false);
}
}
Coordinate[] newCoordinateArray = newCoordinates.toCoordinateArray();
/**
* Ensure there is enough coordinates to build a valid line. Make a
* 2-point line with duplicate coordinates, if necessary There will
* always be at least one coordinate in the coordList.
*/
if (newCoordinateArray.length <= 1) {
newCoordinateArray = new Coordinate[] { newCoordinateArray[0], newCoordinateArray[0]};
}
return line.getFactory().createLineString(newCoordinateArray);
}
}