| 1 |
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
|
| 6 |
|
| 7 |
|
| 8 |
|
| 9 |
|
| 10 |
|
| 11 |
|
| 12 |
|
| 13 |
package org.locationtech.jts.linearref; |
| 14 |
|
| 15 |
import org.locationtech.jts.geom.Coordinate; |
| 16 |
import org.locationtech.jts.geom.Geometry; |
| 17 |
import org.locationtech.jts.geom.LineString; |
| 18 |
|
| 19 |
/** |
| 20 |
* Supports linear referencing along a linear {@link Geometry} |
| 21 |
* using the length along the line as the index. |
| 22 |
* Negative length values are taken as measured in the reverse direction |
| 23 |
* from the end of the geometry. |
| 24 |
* Out-of-range index values are handled by clamping |
| 25 |
* them to the valid range of values. |
| 26 |
* Non-simple lines (i.e. which loop back to cross or touch |
| 27 |
* themselves) are supported. |
| 28 |
*/ |
| 29 |
public class LengthIndexedLine |
| 30 |
{ |
| 31 |
private Geometry linearGeom; |
| 32 |
|
| 33 |
/** |
| 34 |
* Constructs an object which allows a linear {@link Geometry} |
| 35 |
* to be linearly referenced using length as an index. |
| 36 |
* |
| 37 |
* @param linearGeom the linear geometry to reference along |
| 38 |
*/ |
| 39 |
public LengthIndexedLine(Geometry linearGeom) { |
| 40 |
this.linearGeom = linearGeom; |
| 41 |
} |
| 42 |
|
| 43 |
/** |
| 44 |
* Computes the {@link Coordinate} for the point |
| 45 |
* on the line at the given index. |
| 46 |
* If the index is out of range the first or last point on the |
| 47 |
* line will be returned. |
| 48 |
* The Z-ordinate of the computed point will be interpolated from |
| 49 |
* the Z-ordinates of the line segment containing it, if they exist. |
| 50 |
* |
| 51 |
* @param index the index of the desired point |
| 52 |
* @return the Coordinate at the given index |
| 53 |
*/ |
| 54 |
public Coordinate extractPoint(double index) |
| 55 |
{ |
| 56 |
LinearLocation loc = LengthLocationMap.getLocation(linearGeom, index); |
| 57 |
return loc.getCoordinate(linearGeom); |
| 58 |
} |
| 59 |
|
| 60 |
/** |
| 61 |
* Computes the {@link Coordinate} for the point |
| 62 |
* on the line at the given index, offset by the given distance. |
| 63 |
* If the index is out of range the first or last point on the |
| 64 |
* line will be returned. |
| 65 |
* The computed point is offset to the left of the line if the offset distance is |
| 66 |
* positive, to the right if negative. |
| 67 |
* |
| 68 |
* The Z-ordinate of the computed point will be interpolated from |
| 69 |
* the Z-ordinates of the line segment containing it, if they exist. |
| 70 |
* |
| 71 |
* @param index the index of the desired point |
| 72 |
* @param offsetDistance the distance the point is offset from the segment |
| 73 |
* (positive is to the left, negative is to the right) |
| 74 |
* @return the Coordinate at the given index |
| 75 |
*/ |
| 76 |
public Coordinate extractPoint(double index, double offsetDistance) |
| 77 |
{ |
| 78 |
LinearLocation loc = LengthLocationMap.getLocation(linearGeom, index); |
| 79 |
LinearLocation locLow = loc.toLowest(linearGeom); |
| 80 |
return locLow.getSegment(linearGeom).pointAlongOffset(locLow.getSegmentFraction(), offsetDistance); |
| 81 |
} |
| 82 |
|
| 83 |
/** |
| 84 |
* Computes the {@link LineString} for the interval |
| 85 |
* on the line between the given indices. |
| 86 |
* If the endIndex lies before the startIndex, |
| 87 |
* the computed geometry is reversed. |
| 88 |
* |
| 89 |
* @param startIndex the index of the start of the interval |
| 90 |
* @param endIndex the index of the end of the interval |
| 91 |
* @return the linear interval between the indices |
| 92 |
*/ |
| 93 |
public Geometry extractLine(double startIndex, double endIndex) |
| 94 |
{ |
| 95 |
double startIndex2 = clampIndex(startIndex); |
| 96 |
double endIndex2 = clampIndex(endIndex); |
| 97 |
|
| 98 |
boolean resolveStartLower = startIndex2 == endIndex2; |
| 99 |
LinearLocation startLoc = locationOf(startIndex2, resolveStartLower); |
| 100 |
|
| 101 |
|
| 102 |
LinearLocation endLoc = locationOf(endIndex2); |
| 103 |
return ExtractLineByLocation.extract(linearGeom, startLoc, endLoc); |
| 104 |
} |
| 105 |
|
| 106 |
private LinearLocation locationOf(double index) |
| 107 |
{ |
| 108 |
return LengthLocationMap.getLocation(linearGeom, index); |
| 109 |
} |
| 110 |
|
| 111 |
private LinearLocation locationOf(double index, boolean resolveLower) |
| 112 |
{ |
| 113 |
return LengthLocationMap.getLocation(linearGeom, index, resolveLower); |
| 114 |
} |
| 115 |
|
| 116 |
/** |
| 117 |
* Computes the minimum index for a point on the line. |
| 118 |
* If the line is not simple (i.e. loops back on itself) |
| 119 |
* a single point may have more than one possible index. |
| 120 |
* In this case, the smallest index is returned. |
| 121 |
* |
| 122 |
* The supplied point does not <i>necessarily</i> have to lie precisely |
| 123 |
* on the line, but if it is far from the line the accuracy and |
| 124 |
* performance of this function is not guaranteed. |
| 125 |
* Use {@link #project} to compute a guaranteed result for points |
| 126 |
* which may be far from the line. |
| 127 |
* |
| 128 |
* @param pt a point on the line |
| 129 |
* @return the minimum index of the point |
| 130 |
* |
| 131 |
* @see #project(Coordinate) |
| 132 |
*/ |
| 133 |
public double indexOf(Coordinate pt) |
| 134 |
{ |
| 135 |
return LengthIndexOfPoint.indexOf(linearGeom, pt); |
| 136 |
} |
| 137 |
|
| 138 |
/** |
| 139 |
* Finds the index for a point on the line |
| 140 |
* which is greater than the given index. |
| 141 |
* If no such index exists, returns <tt>minIndex</tt>. |
| 142 |
* This method can be used to determine all indexes for |
| 143 |
* a point which occurs more than once on a non-simple line. |
| 144 |
* It can also be used to disambiguate cases where the given point lies |
| 145 |
* slightly off the line and is equidistant from two different |
| 146 |
* points on the line. |
| 147 |
* |
| 148 |
* The supplied point does not <i>necessarily</i> have to lie precisely |
| 149 |
* on the line, but if it is far from the line the accuracy and |
| 150 |
* performance of this function is not guaranteed. |
| 151 |
* Use {@link #project} to compute a guaranteed result for points |
| 152 |
* which may be far from the line. |
| 153 |
* |
| 154 |
* @param pt a point on the line |
| 155 |
* @param minIndex the value the returned index must be greater than |
| 156 |
* @return the index of the point greater than the given minimum index |
| 157 |
* |
| 158 |
* @see #project(Coordinate) |
| 159 |
*/ |
| 160 |
public double indexOfAfter(Coordinate pt, double minIndex) |
| 161 |
{ |
| 162 |
return LengthIndexOfPoint.indexOfAfter(linearGeom, pt, minIndex); |
| 163 |
} |
| 164 |
|
| 165 |
/** |
| 166 |
* Computes the indices for a subline of the line. |
| 167 |
* (The subline must <b>conform</b> to the line; that is, |
| 168 |
* all vertices in the subline (except possibly the first and last) |
| 169 |
* must be vertices of the line and occur in the same order). |
| 170 |
* |
| 171 |
* @param subLine a subLine of the line |
| 172 |
* @return a pair of indices for the start and end of the subline. |
| 173 |
*/ |
| 174 |
public double[] indicesOf(Geometry subLine) |
| 175 |
{ |
| 176 |
LinearLocation[] locIndex = LocationIndexOfLine.indicesOf(linearGeom, subLine); |
| 177 |
double[] index = new double[] { |
| 178 |
LengthLocationMap.getLength(linearGeom, locIndex[0]), |
| 179 |
LengthLocationMap.getLength(linearGeom, locIndex[1]) |
| 180 |
}; |
| 181 |
return index; |
| 182 |
} |
| 183 |
|
| 184 |
|
| 185 |
/** |
| 186 |
* Computes the index for the closest point on the line to the given point. |
| 187 |
* If more than one point has the closest distance the first one along the line |
| 188 |
* is returned. |
| 189 |
* (The point does not necessarily have to lie precisely on the line.) |
| 190 |
* |
| 191 |
* @param pt a point on the line |
| 192 |
* @return the index of the point |
| 193 |
*/ |
| 194 |
public double project(Coordinate pt) |
| 195 |
{ |
| 196 |
return LengthIndexOfPoint.indexOf(linearGeom, pt); |
| 197 |
} |
| 198 |
|
| 199 |
/** |
| 200 |
* Returns the index of the start of the line |
| 201 |
* @return the start index |
| 202 |
*/ |
| 203 |
public double getStartIndex() |
| 204 |
{ |
| 205 |
return 0.0; |
| 206 |
} |
| 207 |
|
| 208 |
/** |
| 209 |
* Returns the index of the end of the line |
| 210 |
* @return the end index |
| 211 |
*/ |
| 212 |
public double getEndIndex() |
| 213 |
{ |
| 214 |
return linearGeom.getLength(); |
| 215 |
} |
| 216 |
|
| 217 |
/** |
| 218 |
* Tests whether an index is in the valid index range for the line. |
| 219 |
* |
| 220 |
* @param index the index to test |
| 221 |
* @return <code>true</code> if the index is in the valid range |
| 222 |
*/ |
| 223 |
public boolean isValidIndex(double index) |
| 224 |
{ |
| 225 |
return (index >= getStartIndex() |
| 226 |
&& index <= getEndIndex()); |
| 227 |
} |
| 228 |
|
| 229 |
/** |
| 230 |
* Computes a valid index for this line |
| 231 |
* by clamping the given index to the valid range of index values |
| 232 |
* |
| 233 |
* @return a valid index value |
| 234 |
*/ |
| 235 |
public double clampIndex(double index) |
| 236 |
{ |
| 237 |
double posIndex = positiveIndex(index); |
| 238 |
double startIndex = getStartIndex(); |
| 239 |
if (posIndex < startIndex) return startIndex; |
| 240 |
|
| 241 |
double endIndex = getEndIndex(); |
| 242 |
if (posIndex > endIndex) return endIndex; |
| 243 |
|
| 244 |
return posIndex; |
| 245 |
} |
| 246 |
|
| 247 |
private double positiveIndex(double index) |
| 248 |
{ |
| 249 |
if (index >= 0.0) return index; |
| 250 |
return linearGeom.getLength() + index; |
| 251 |
} |
| 252 |
} |
| 253 |
|