001: package com.vividsolutions.jts.linearref;
002:
003: import com.vividsolutions.jts.geom.*;
004: import com.vividsolutions.jts.util.Assert;
005:
006: /**
007: * Extracts the subline of a linear {@link Geometry} between
008: * two {@link LinearLocation}s on the line.
009: */
010: class ExtractLineByLocation {
011: /**
012: * Computes the subline of a {@link LineString} between
013: * two {@link LineStringLocation}s on the line.
014: * If the start location is after the end location,
015: * the computed geometry is reversed.
016: *
017: * @param line the line to use as the baseline
018: * @param start the start location
019: * @param end the end location
020: * @return the extracted subline
021: */
022: public static Geometry extract(Geometry line, LinearLocation start,
023: LinearLocation end) {
024: ExtractLineByLocation ls = new ExtractLineByLocation(line);
025: return ls.extract(start, end);
026: }
027:
028: private Geometry line;
029:
030: public ExtractLineByLocation(Geometry line) {
031: this .line = line;
032: }
033:
034: /**
035: * Extracts a subline of the input.
036: * If <code>end < start</code> the linear geometry computed will be reversed.
037: *
038: * @param start the start location
039: * @param end the end location
040: * @return a linear geometry
041: */
042: public Geometry extract(LinearLocation start, LinearLocation end) {
043: if (end.compareTo(start) < 0) {
044: return reverse(computeLinear(end, start));
045: }
046: return computeLinear(start, end);
047: }
048:
049: private Geometry reverse(Geometry linear) {
050: if (linear instanceof LineString)
051: return ((LineString) linear).reverse();
052: if (linear instanceof MultiLineString)
053: return ((MultiLineString) linear).reverse();
054: Assert.shouldNeverReachHere("non-linear geometry encountered");
055: return null;
056: }
057:
058: /**
059: * Assumes input is valid (e.g. start <= end)
060: *
061: * @param start
062: * @param end
063: * @return a linear geometry
064: */
065: private LineString computeLine(LinearLocation start,
066: LinearLocation end) {
067: Coordinate[] coordinates = line.getCoordinates();
068: CoordinateList newCoordinates = new CoordinateList();
069:
070: int startSegmentIndex = start.getSegmentIndex();
071: if (start.getSegmentFraction() > 0.0)
072: startSegmentIndex += 1;
073: int lastSegmentIndex = end.getSegmentIndex();
074: if (end.getSegmentFraction() == 1.0)
075: lastSegmentIndex += 1;
076: if (lastSegmentIndex >= coordinates.length)
077: lastSegmentIndex = coordinates.length - 1;
078: // not needed - LinearLocation values should always be correct
079: //Assert.isTrue(end.getSegmentFraction() <= 1.0, "invalid segment fraction value");
080:
081: if (!start.isVertex())
082: newCoordinates.add(start.getCoordinate(line));
083: for (int i = startSegmentIndex; i <= lastSegmentIndex; i++) {
084: newCoordinates.add(coordinates[i]);
085: }
086: if (!end.isVertex())
087: newCoordinates.add(end.getCoordinate(line));
088:
089: // ensure there is at least one coordinate in the result
090: if (newCoordinates.size() <= 0)
091: newCoordinates.add(start.getCoordinate(line));
092:
093: Coordinate[] newCoordinateArray = newCoordinates
094: .toCoordinateArray();
095: /**
096: * Ensure there is enough coordinates to build a valid line.
097: * Make a 2-point line with duplicate coordinates, if necessary.
098: * There will always be at least one coordinate in the coordList.
099: */
100: if (newCoordinateArray.length <= 1) {
101: newCoordinateArray = new Coordinate[] {
102: newCoordinateArray[0], newCoordinateArray[0] };
103: }
104: return line.getFactory().createLineString(newCoordinateArray);
105: }
106:
107: /**
108: * Assumes input is valid (e.g. start <= end)
109: *
110: * @param start
111: * @param end
112: * @return a linear geometry
113: */
114: private Geometry computeLinear(LinearLocation start,
115: LinearLocation end) {
116: LinearGeometryBuilder builder = new LinearGeometryBuilder(line
117: .getFactory());
118: builder.setFixInvalidLines(true);
119:
120: if (!start.isVertex())
121: builder.add(start.getCoordinate(line));
122:
123: for (LinearIterator it = new LinearIterator(line, start); it
124: .hasNext(); it.next()) {
125: if (end.compareLocationValues(it.getComponentIndex(), it
126: .getVertexIndex(), 0.0) < 0)
127: break;
128:
129: Coordinate pt = it.getSegmentStart();
130: builder.add(pt);
131: if (it.isEndOfLine())
132: builder.endLine();
133: }
134: if (!end.isVertex())
135: builder.add(end.getCoordinate(line));
136:
137: return builder.getGeometry();
138: }
139:
140: /**
141: * Computes a valid and normalized location
142: * compatible with the values in a LinearIterator.
143: * (I.e. segmentFractions of 1.0 are converted to the next highest coordinate index)
144: */
145: /*
146: private LinearLocation normalize(LinearLocation loc)
147: {
148: int componentIndex = loc.getComponentIndex();
149: int segmentIndex = loc.getSegmentIndex();
150: double segmentFraction = loc.getSegmentFraction();
151:
152: if (segmentFraction < 0.0) {
153: segmentFraction = 0.0;
154: }
155: if (segmentFraction > 1.0) {
156: segmentFraction = 1.0;
157: }
158:
159: if (componentIndex < 0) {
160: componentIndex = 0;
161: segmentIndex = 0;
162: segmentFraction = 0.0;
163: }
164: if (segmentIndex < 0) {
165: segmentIndex = 0;
166: segmentFraction = 0.0;
167: }
168:
169: if (segmentFraction == 1.0) {
170: segmentFraction = 0.0;
171: segmentIndex += 1;
172: }
173:
174: return new LinearLocation(componentIndex, segmentIndex, segmentFraction);
175: }
176: */
177: }
|