Class LinearIterator

  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.Lineal;
 19 import org.locationtech.jts.geom.MultiLineString;
 20  
 21 /**
 22  * An iterator over the components and coordinates of a linear geometry
 23  * ({@link LineString}s and {@link MultiLineString}s.
 24  *
 25  * The standard usage pattern for a {@link LinearIterator} is:
 26  *
 27  * <pre>
 28  * for (LinearIterator it = new LinearIterator(...); it.hasNext(); it.next()) {
 29  *   ...
 30  *   int ci = it.getComponentIndex();   // for example
 31  *   int vi = it.getVertexIndex();      // for example
 32  *   ...
 33  * }
 34  * </pre>
 35  *
 36  * @version 1.7
 37  */
 38 public class LinearIterator
 39 {
 40   private static int segmentEndVertexIndex(LinearLocation loc)
 41   {
 42     if (loc.getSegmentFraction() > 0.0)
 43       return loc.getSegmentIndex() + 1;
 44     return loc.getSegmentIndex();
 45   }
 46  
 47   private Geometry linearGeom;
 48   private final int numLines;
 49  
 50   /**
 51    * Invariant: currentLine <> null if the iterator is pointing at a valid coordinate
 52    */
 53   private LineString currentLine;
 54   private int componentIndex = 0;
 55   private int vertexIndex = 0;
 56  
 57   /**
 58    * Creates an iterator initialized to the start of a linear {@link Geometry}
 59    *
 60    * @param linear the linear geometry to iterate over
 61    * @throws IllegalArgumentException if linearGeom is not lineal
 62    */
 63   public LinearIterator(Geometry linear) {
 64     this(linear, 00);
 65   }
 66  
 67   /**
 68    * Creates an iterator starting at
 69    * a {@link LinearLocation} on a linear {@link Geometry}
 70    *
 71    * @param linear the linear geometry to iterate over
 72    * @param start the location to start at
 73    * @throws IllegalArgumentException if linearGeom is not lineal
 74    */
 75   public LinearIterator(Geometry linear, LinearLocation start) {
 76     this(linear, start.getComponentIndex(), segmentEndVertexIndex(start));
 77   }
 78  
 79   /**
 80    * Creates an iterator starting at
 81    * a specified component and vertex in a linear {@link Geometry}
 82    *
 83    * @param linearGeom the linear geometry to iterate over
 84    * @param componentIndex the component to start at
 85    * @param vertexIndex the vertex to start at
 86    * @throws IllegalArgumentException if linearGeom is not lineal
 87    */
 88   public LinearIterator(Geometry linearGeom, int componentIndex, int vertexIndex) 
 89   {
 90       if (! (linearGeom instanceof Lineal))
 91               throw new IllegalArgumentException("Lineal geometry is required");
 92     this.linearGeom = linearGeom;
 93     numLines = linearGeom.getNumGeometries();
 94     this.componentIndex = componentIndex;
 95     this.vertexIndex = vertexIndex;
 96     loadCurrentLine();
 97   }
 98  
 99   private void loadCurrentLine()
100   {
101     if (componentIndex >= numLines) {
102       currentLine = null;
103       return;
104     }
105     currentLine = (LineString) linearGeom.getGeometryN(componentIndex);
106   }
107  
108   /**
109    * Tests whether there are any vertices left to iterator over.
110    * Specifically, hasNext() return <tt>true</tt> if the
111    * current state of the iterator represents a valid location
112    * on the linear geometry. 
113    * 
114    * @return <code>true</code> if there are more vertices to scan
115    */
116   public boolean hasNext()
117   {
118     if (componentIndex >= numLines) return false;
119     if (componentIndex == numLines - 1
120         && vertexIndex >= currentLine.getNumPoints())
121       return false;
122     return true;
123   }
124  
125   /**
126    * Moves the iterator ahead to the next vertex and (possibly) linear component.
127    */
128   public void next()
129   {
130     if (! hasNext()) return;
131  
132     vertexIndex++;
133     if (vertexIndex >= currentLine.getNumPoints()) {
134       componentIndex++;
135       loadCurrentLine();
136       vertexIndex = 0;
137     }
138   }
139  
140   /**
141    * Checks whether the iterator cursor is pointing to the
142    * endpoint of a component {@link LineString}.
143    *
144    * @return <code>true</code> if the iterator is at an endpoint
145    */
146   public boolean isEndOfLine() {
147     if (componentIndex >= numLines) return false;
148     //LineString currentLine = (LineString) linear.getGeometryN(componentIndex);
149     if (vertexIndex < currentLine.getNumPoints() - 1)
150       return false;
151     return true;
152   }
153  
154   /**
155    * The component index of the vertex the iterator is currently at.
156    * @return the current component index
157    */
158   public int getComponentIndex() { return componentIndex; }
159  
160   /**
161    * The vertex index of the vertex the iterator is currently at.
162    * @return the current vertex index
163    */
164   public int getVertexIndex() { return vertexIndex; }
165  
166   /**
167    * Gets the {@link LineString} component the iterator is current at.
168    * @return a linestring
169    */
170   public LineString getLine()  {    return currentLine;  }
171  
172   /**
173    * Gets the first {@link Coordinate} of the current segment.
174    * (the coordinate of the current vertex).
175    * @return a {@link Coordinate}
176    */
177   public Coordinate getSegmentStart() { return currentLine.getCoordinateN(vertexIndex); }
178  
179   /**
180    * Gets the second {@link Coordinate} of the current segment.
181    * (the coordinate of the next vertex).
182    * If the iterator is at the end of a line, <code>null</code> is returned.
183    *
184    * @return a {@link Coordinate} or <code>null</code>
185    */
186   public Coordinate getSegmentEnd()
187   {
188     if (vertexIndex < getLine().getNumPoints() - 1)
189       return currentLine.getCoordinateN(vertexIndex + 1);
190     return null;
191   }
192 }
193