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 import org.locationtech.jts.geom.MultiLineString;
19 
20 /**
21  * Supports linear referencing
22  * along a linear {@link Geometry}
23  * using {@link LinearLocation}s as the index.
24  */
25 public class LocationIndexedLine
26 {
27   private Geometry linearGeom;
28 
29   /**
30    * Constructs an object which allows linear referencing along
31    * a given linear {@link Geometry}.
32    *
33    * @param linearGeom the linear geometry to reference along
34    */
LocationIndexedLine(Geometry linearGeom)35   public LocationIndexedLine(Geometry linearGeom)
36   {
37     this.linearGeom = linearGeom;
38     checkGeometryType();
39   }
40 
checkGeometryType()41   private void checkGeometryType()
42   {
43     if (! (linearGeom instanceof LineString || linearGeom instanceof MultiLineString))
44       throw new IllegalArgumentException("Input geometry must be linear");
45   }
46   /**
47    * Computes the {@link Coordinate} for the point
48    * on the line at the given index.
49    * If the index is out of range the first or last point on the
50    * line will be returned.
51    * The Z-ordinate of the computed point will be interpolated from
52    * the Z-ordinates of the line segment containing it, if they exist.
53    *
54    * @param index the index of the desired point
55    * @return the Coordinate at the given index
56    */
extractPoint(LinearLocation index)57   public Coordinate extractPoint(LinearLocation index)
58   {
59     return index.getCoordinate(linearGeom);
60   }
61 
62   /**
63    * Computes the {@link Coordinate} for the point
64    * on the line at the given index, offset by the given distance.
65    * If the index is out of range the first or last point on the
66    * line will be returned.
67    * The computed point is offset to the left of the line if the offset distance is
68    * positive, to the right if negative.
69    *
70    * The Z-ordinate of the computed point will be interpolated from
71    * the Z-ordinates of the line segment containing it, if they exist.
72    *
73    * @param index the index of the desired point
74    * @param offsetDistance the distance the point is offset from the segment
75    *    (positive is to the left, negative is to the right)
76    * @return the Coordinate at the given index
77    */
extractPoint(LinearLocation index, double offsetDistance)78   public Coordinate extractPoint(LinearLocation index, double offsetDistance)
79   {
80     LinearLocation indexLow = index.toLowest(linearGeom);
81     return indexLow.getSegment(linearGeom).pointAlongOffset(indexLow.getSegmentFraction(), offsetDistance);
82   }
83 
84   /**
85    * Computes the {@link LineString} for the interval
86    * on the line between the given indices.
87    * If the start location is after the end location,
88    * the computed linear geometry has reverse orientation to the input line.
89    *
90    * @param startIndex the index of the start of the interval
91    * @param endIndex the index of the end of the interval
92    * @return the linear geometry between the indices
93    */
extractLine(LinearLocation startIndex, LinearLocation endIndex)94   public Geometry extractLine(LinearLocation startIndex, LinearLocation endIndex)
95   {
96     return ExtractLineByLocation.extract(linearGeom, startIndex, endIndex);
97   }
98 
99   /**
100    * Computes the index for a given point on the line.
101    * <p>
102    * The supplied point does not <i>necessarily</i> have to lie precisely
103    * on the line, but if it is far from the line the accuracy and
104    * performance of this function is not guaranteed.
105    * Use {@link #project} to compute a guaranteed result for points
106    * which may be far from the line.
107    *
108    * @param pt a point on the line
109    * @return the index of the point
110    * @see #project(Coordinate)
111    */
indexOf(Coordinate pt)112   public LinearLocation indexOf(Coordinate pt)
113   {
114     return LocationIndexOfPoint.indexOf(linearGeom, pt);
115   }
116 
117   /**
118    * Finds the index for a point on the line
119    * which is greater than the given index.
120    * If no such index exists, returns <tt>minIndex</tt>.
121    * This method can be used to determine all indexes for
122    * a point which occurs more than once on a non-simple line.
123    * It can also be used to disambiguate cases where the given point lies
124    * slightly off the line and is equidistant from two different
125    * points on the line.
126    *
127    * The supplied point does not <i>necessarily</i> have to lie precisely
128    * on the line, but if it is far from the line the accuracy and
129    * performance of this function is not guaranteed.
130    * Use {@link #project} to compute a guaranteed result for points
131    * which may be far from the line.
132    *
133    * @param pt a point on the line
134    * @param minIndex the value the returned index must be greater than
135    * @return the index of the point greater than the given minimum index
136    *
137    * @see #project(Coordinate)
138    */
indexOfAfter(Coordinate pt, LinearLocation minIndex)139   public LinearLocation indexOfAfter(Coordinate pt, LinearLocation minIndex)
140   {
141     return LocationIndexOfPoint.indexOfAfter(linearGeom, pt, minIndex);
142   }
143 
144 
145   /**
146    * Computes the indices for a subline of the line.
147    * (The subline must <i>conform</i> to the line; that is,
148    * all vertices in the subline (except possibly the first and last)
149    * must be vertices of the line and occur in the same order).
150    *
151    * @param subLine a subLine of the line
152    * @return a pair of indices for the start and end of the subline.
153    */
indicesOf(Geometry subLine)154   public LinearLocation[] indicesOf(Geometry subLine)
155   {
156     return LocationIndexOfLine.indicesOf(linearGeom, subLine);
157   }
158 
159   /**
160    * Computes the index for the closest point on the line to the given point.
161    * If more than one point has the closest distance the first one along the line
162    * is returned.
163    * (The point does not necessarily have to lie precisely on the line.)
164    *
165    * @param pt a point on the line
166    * @return the index of the point
167    */
project(Coordinate pt)168   public LinearLocation project(Coordinate pt)
169   {
170     return LocationIndexOfPoint.indexOf(linearGeom, pt);
171   }
172 
173   /**
174    * Returns the index of the start of the line
175    * @return the location index
176    */
getStartIndex()177   public LinearLocation getStartIndex()
178   {
179     return new LinearLocation();
180   }
181 
182   /**
183    * Returns the index of the end of the line
184    * @return the location index
185    */
getEndIndex()186   public LinearLocation getEndIndex()
187   {
188     return LinearLocation.getEndLocation(linearGeom);
189   }
190 
191   /**
192    * Tests whether an index is in the valid index range for the line.
193    *
194    * @param index the index to test
195    * @return <code>true</code> if the index is in the valid range
196    */
isValidIndex(LinearLocation index)197   public boolean isValidIndex(LinearLocation index)
198   {
199     return index.isValid(linearGeom);
200   }
201 
202   /**
203    * Computes a valid index for this line
204    * by clamping the given index to the valid range of index values
205    *
206    * @return a valid index value
207    */
clampIndex(LinearLocation index)208   public LinearLocation clampIndex(LinearLocation index)
209   {
210     LinearLocation loc = index.copy();
211     loc.clamp(linearGeom);
212     return loc;
213   }
214 }
215