Class LengthIndexedLine

  1 /*
  2  * Copyright (c) 2016 Vivid Solutions.
  3  *
  4  * All rights reserved. This program and the accompanying materials
  5  * are made available under the terms of the Eclipse Public License 2.0
  6  * and Eclipse Distribution License v. 1.0 which accompanies this distribution.
  7  * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html
  8  * and the Eclipse Distribution License is available at
  9  *
 10  * http://www.eclipse.org/org/documents/edl-v10.php.
 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     // if extracted line is zero-length, resolve start lower as well to ensure they are equal
 98     boolean resolveStartLower = startIndex2 == endIndex2;
 99     LinearLocation startLoc = locationOf(startIndex2, resolveStartLower);
100 //    LinearLocation endLoc = locationOf(endIndex2, true);
101 //    LinearLocation startLoc = locationOf(startIndex2);
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.0return index;
250     return linearGeom.getLength() + index;
251   }
252 }
253