Class LineString

  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 package org.locationtech.jts.geom;
 13  
 14 import org.locationtech.jts.algorithm.Length;
 15 import org.locationtech.jts.operation.BoundaryOp;
 16  
 17 /**
 18  *  Models an OGC-style <code>LineString</code>.
 19  *  A LineString consists of a sequence of two or more vertices,
 20  *  along with all points along the linearly-interpolated curves
 21  *  (line segments) between each
 22  *  pair of consecutive vertices.
 23  *  Consecutive vertices may be equal.
 24  *  The line segments in the line may intersect each other (in other words,
 25  *  the linestring may "curl back" in itself and self-intersect.
 26  *  Linestrings with exactly two identical points are invalid.
 27  *  <p>
 28  * A linestring must have either 0 or 2 or more points.
 29  * If these conditions are not met, the constructors throw
 30  * an {@link IllegalArgumentException}
 31  *
 32  *@version 1.7
 33  */
 34 public class LineString
 35     extends Geometry
 36     implements Lineal
 37 {
 38   private static final long serialVersionUID = 3110669828065365560L;
 39   /**
 40    *  The points of this <code>LineString</code>.
 41    */
 42   protected CoordinateSequence points;
 43  
 44   /**
 45    *  Constructs a <code>LineString</code> with the given points.
 46    *
 47    *@param  points the points of the linestring, or <code>null</code>
 48    *      to create the empty geometry. This array must not contain <code>null</code>
 49    *      elements. Consecutive points may be equal.
 50    *@param  precisionModel  the specification of the grid of allowable points
 51    *      for this <code>LineString</code>
 52    *@param  SRID            the ID of the Spatial Reference System used by this
 53    *      <code>LineString</code>
 54    * @throws IllegalArgumentException if too few points are provided
 55    */
 56   /** @deprecated Use GeometryFactory instead */
 57   public LineString(Coordinate points[], PrecisionModel precisionModel, int SRID)
 58   {
 59     super(new GeometryFactory(precisionModel, SRID));
 60     init(getFactory().getCoordinateSequenceFactory().create(points));
 61   }
 62  
 63   /**
 64    * Constructs a <code>LineString</code> with the given points.
 65    *
 66    *@param  points the points of the linestring, or <code>null</code>
 67    *      to create the empty geometry.
 68    * @throws IllegalArgumentException if too few points are provided
 69    */
 70   public LineString(CoordinateSequence points, GeometryFactory factory) {
 71     super(factory);
 72     init(points);
 73   }
 74  
 75   private void init(CoordinateSequence points)
 76   {
 77     if (points == null) {
 78       points = getFactory().getCoordinateSequenceFactory().create(new Coordinate[]{});
 79     }
 80     if (points.size() == 1) {
 81       throw new IllegalArgumentException("Invalid number of points in LineString (found "
 82               + points.size() + " - must be 0 or >= 2)");
 83     }
 84     this.points = points;
 85   }
 86   public Coordinate[] getCoordinates() {
 87     return points.toCoordinateArray();
 88   }
 89  
 90   public CoordinateSequence getCoordinateSequence() {
 91       return points;
 92   }
 93  
 94   public Coordinate getCoordinateN(int n) {
 95       return points.getCoordinate(n);
 96   }
 97  
 98   public Coordinate getCoordinate()
 99   {
100     if (isEmpty()) return null;
101     return points.getCoordinate(0);
102   }
103  
104   public int getDimension() {
105     return 1;
106   }
107  
108   public int getBoundaryDimension() {
109     if (isClosed()) {
110       return Dimension.FALSE;
111     }
112     return 0;
113   }
114  
115   public boolean isEmpty() {
116       return points.size() == 0;
117   }
118  
119   public int getNumPoints() {
120       return points.size();
121   }
122  
123   public Point getPointN(int n) {
124       return getFactory().createPoint(points.getCoordinate(n));
125   }
126  
127   public Point getStartPoint() {
128     if (isEmpty()) {
129       return null;
130     }
131     return getPointN(0);
132   }
133  
134   public Point getEndPoint() {
135     if (isEmpty()) {
136       return null;
137     }
138     return getPointN(getNumPoints() - 1);
139   }
140  
141   public boolean isClosed() {
142     if (isEmpty()) {
143       return false;
144     }
145     return getCoordinateN(0).equals2D(getCoordinateN(getNumPoints() - 1));
146   }
147  
148   public boolean isRing() {
149     return isClosed() && isSimple();
150   }
151  
152   public String getGeometryType() {
153     return Geometry.TYPENAME_LINESTRING;
154   }
155  
156   /**
157    *  Returns the length of this <code>LineString</code>
158    *
159    *@return the length of the linestring
160    */
161   public double getLength()
162   {
163    return Length.ofLine(points);
164   }
165  
166   /**
167    * Gets the boundary of this geometry.
168    * The boundary of a lineal geometry is always a zero-dimensional geometry (which may be empty).
169    *
170    * @return the boundary geometry
171    * @see Geometry#getBoundary
172    */
173   public Geometry getBoundary() {
174     return (new BoundaryOp(this)).getBoundary();
175   }
176  
177   /**
178    * Creates a {@link LineString} whose coordinates are in the reverse
179    * order of this objects
180    *
181    * @return a {@link LineString} with coordinates in the reverse order
182    */
183   public LineString reverse() {
184     return (LineString) super.reverse();
185   }
186  
187   protected LineString reverseInternal()
188   {
189     CoordinateSequence seq = points.copy();
190     CoordinateSequences.reverse(seq);
191     return getFactory().createLineString(seq);
192   }
193  
194   /**
195    *  Returns true if the given point is a vertex of this <code>LineString</code>.
196    *
197    *@param  pt  the <code>Coordinate</code> to check
198    *@return     <code>true</code> if <code>pt</code> is one of this <code>LineString</code>
199    *      's vertices
200    */
201   public boolean isCoordinate(Coordinate pt) {
202       for (int i = 0; i < points.size(); i++) {
203         if (points.getCoordinate(i).equals(pt)) {
204           return true;
205         }
206       }
207     return false;
208   }
209  
210   protected Envelope computeEnvelopeInternal() {
211     if (isEmpty()) {
212       return new Envelope();
213     }
214     return points.expandEnvelope(new Envelope());
215   }
216  
217   public boolean equalsExact(Geometry other, double tolerance) {
218     if (!isEquivalentClass(other)) {
219       return false;
220     }
221     LineString otherLineString = (LineString) other;
222     if (points.size() != otherLineString.points.size()) {
223       return false;
224     }
225     for (int i = 0; i < points.size(); i++) {
226       if (!equal(points.getCoordinate(i), otherLineString.points.getCoordinate(i), tolerance)) {
227         return false;
228       }
229     }
230     return true;
231   }
232  
233   public void apply(CoordinateFilter filter) {
234       for (int i = 0; i < points.size(); i++) {
235         filter.filter(points.getCoordinate(i));
236       }
237   }
238  
239   public void apply(CoordinateSequenceFilter filter)
240   {
241     if (points.size() == 0)
242       return;
243     for (int i = 0; i < points.size(); i++) {
244       filter.filter(points, i);
245       if (filter.isDone())
246         break;
247     }
248     if (filter.isGeometryChanged())
249       geometryChanged();
250   }
251  
252   public void apply(GeometryFilter filter) {
253     filter.filter(this);
254   }
255  
256   public void apply(GeometryComponentFilter filter) {
257     filter.filter(this);
258   }
259  
260   /**
261    * Creates and returns a full copy of this {@link LineString} object.
262    * (including all coordinates contained by it).
263    *
264    * @return a clone of this instance
265    * @deprecated
266    */
267   public Object clone() {
268     return copy();
269   }
270  
271   protected LineString copyInternal() {
272     return new LineString(points.copy(), factory);
273   }
274  
275   /**
276    * Normalizes a LineString.  A normalized linestring
277    * has the first point which is not equal to it's reflected point
278    * less than the reflected point.
279    */
280   public void normalize()
281   {
282       for (int i = 0; i < points.size() / 2; i++) {
283         int j = points.size() - 1 - i;
284         // skip equal points on both ends
285         if (!points.getCoordinate(i).equals(points.getCoordinate(j))) {
286           if (points.getCoordinate(i).compareTo(points.getCoordinate(j)) > 0) {
287             CoordinateSequence copy = points.copy();
288             CoordinateSequences.reverse(copy);
289             points = copy;
290           }
291           return;
292         }
293       }
294   }
295  
296   protected boolean isEquivalentClass(Geometry other) {
297     return other instanceof LineString;
298   }
299  
300   protected int compareToSameClass(Object o)
301   {
302     LineString line = (LineString) o;
303     // MD - optimized implementation
304     int i = 0;
305     int j = 0;
306     while (i < points.size() && j < line.points.size()) {
307       int comparison = points.getCoordinate(i).compareTo(line.points.getCoordinate(j));
308       if (comparison != 0) {
309         return comparison;
310       }
311       i++;
312       j++;
313     }
314     if (i < points.size()) {
315       return 1;
316     }
317     if (j < line.points.size()) {
318       return -1;
319     }
320     return 0;
321   }
322  
323   protected int compareToSameClass(Object o, CoordinateSequenceComparator comp)
324   {
325     LineString line = (LineString) o;
326     return comp.compare(this.points, line.points);
327   }
328   
329   protected int getTypeCode() {
330     return Geometry.TYPECODE_LINESTRING;
331   }
332  
333 }
334