001: package com.vividsolutions.jump.algorithm;
002:
003: import com.vividsolutions.jts.geom.*;
004: import com.vividsolutions.jts.util.Assert;
005:
006: /**
007: * Computes a substring of a {@link LineString}
008: * between given distances along the line.
009: * <ul>
010: * <li>The distances are clipped to the actual line length
011: * <li>If the start distance is equal to the end distance,
012: * a zero-length line with two identical points is returned
013: * <li>FUTURE: If the start distance is greater than the end distance,
014: * an inverted section of the line is returned
015: * </ul>
016: * <p>
017: * FUTURE: should handle startLength > endLength, and flip the returned
018: * linestring. Also should handle negative lengths (they are measured from end
019: * of line backwards).
020: */
021: // Martin made a decision to create this duplicate of a class from JCS.
022: // [Jon Aquino 2004-10-25]
023: public class LengthSubstring {
024: public static LineString getSubstring(LineString line,
025: double startLength, double endLength) {
026: LengthSubstring ls = new LengthSubstring(line);
027: return ls.getSubstring(startLength, endLength);
028: }
029:
030: private LineString line;
031:
032: public LengthSubstring(LineString line) {
033: this .line = line;
034: }
035:
036: public LineString getSubstring(double startDistance,
037: double endDistance) {
038: // future: if start > end, flip values and return an inverted line
039: Assert.isTrue(startDistance <= endDistance,
040: "inverted distances not currently supported");
041:
042: Coordinate[] coordinates = line.getCoordinates();
043: // check for a zero-length segment and handle appropriately
044: if (endDistance <= 0.0) {
045: return line.getFactory()
046: .createLineString(
047: new Coordinate[] { coordinates[0],
048: coordinates[0] });
049: }
050: if (startDistance >= line.getLength()) {
051: return line.getFactory().createLineString(
052: new Coordinate[] {
053: coordinates[coordinates.length - 1],
054: coordinates[coordinates.length - 1] });
055: }
056: if (startDistance < 0.0) {
057: startDistance = 0.0;
058: }
059: return computeSubstring(startDistance, endDistance);
060: }
061:
062: /**
063: * Assumes input is strictly valid (e.g. startDist < endDistance)
064: *
065: * @param startDistance
066: * @param endDistance
067: * @return
068: */
069: private LineString computeSubstring(double startDistance,
070: double endDistance) {
071: Coordinate[] coordinates = line.getCoordinates();
072: CoordinateList newCoordinates = new CoordinateList();
073: double segmentStartDistance = 0.0;
074: double segmentEndDistance = 0.0;
075: boolean started = false;
076: int i = 0;
077: LineSegment segment = new LineSegment();
078: while (i < coordinates.length - 1
079: && endDistance > segmentEndDistance) {
080: segment.p0 = coordinates[i];
081: segment.p1 = coordinates[i + 1];
082: i++;
083: segmentStartDistance = segmentEndDistance;
084: segmentEndDistance = segmentStartDistance
085: + segment.getLength();
086:
087: if (startDistance > segmentEndDistance)
088: continue;
089: if (startDistance >= segmentStartDistance
090: && startDistance < segmentEndDistance) {
091: newCoordinates.add(LocatePoint.pointAlongSegment(
092: segment.p0, segment.p1, startDistance
093: - segmentStartDistance), false);
094: }
095: /*
096: if (startDistance >= segmentStartDistance
097: && startDistance == segmentEndDistance) {
098: newCoordinates.add(new Coordinate(segment.p1), false);
099: }
100: */
101: if (endDistance >= segmentEndDistance) {
102: newCoordinates.add(new Coordinate(segment.p1), false);
103: }
104: if (endDistance >= segmentStartDistance
105: && endDistance < segmentEndDistance) {
106: newCoordinates.add(LocatePoint.pointAlongSegment(
107: segment.p0, segment.p1, endDistance
108: - segmentStartDistance), false);
109: }
110: }
111: Coordinate[] newCoordinateArray = newCoordinates
112: .toCoordinateArray();
113: /**
114: * Ensure there is enough coordinates to build a valid line. Make a
115: * 2-point line with duplicate coordinates, if necessary There will
116: * always be at least one coordinate in the coordList.
117: */
118: if (newCoordinateArray.length <= 1) {
119: newCoordinateArray = new Coordinate[] {
120: newCoordinateArray[0], newCoordinateArray[0] };
121: }
122: return line.getFactory().createLineString(newCoordinateArray);
123: }
124: }
|