001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2004-2006, Geotools Project Managment Committee (PMC)
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or (at your option) any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: */
016: package org.geotools.geometry.jts;
017:
018: import java.awt.geom.AffineTransform;
019: import java.util.NoSuchElementException;
020:
021: import org.geotools.referencing.ReferencingFactoryFinder;
022: import org.opengis.referencing.FactoryException;
023: import org.opengis.referencing.operation.MathTransform;
024: import org.opengis.referencing.operation.NoninvertibleTransformException;
025: import org.opengis.referencing.operation.TransformException;
026:
027: import com.vividsolutions.jts.geom.CoordinateSequence;
028: import com.vividsolutions.jts.geom.LineString;
029: import com.vividsolutions.jts.geom.LinearRing;
030:
031: /**
032: * A path iterator for the LiteShape class, specialized to iterate over
033: * LineString object.
034: *
035:
036: * @author Andrea Aime
037: * @author simone giannecchini
038: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/main/src/main/java/org/geotools/geometry/jts/LineIterator.java $
039: * @version $Id: LineIterator.java 25075 2007-04-09 19:20:46Z desruisseaux $
040: */
041: public final class LineIterator extends AbstractLiteIterator {
042: /** Transform applied on the coordinates during iteration */
043: private AffineTransform at;
044:
045: /** The array of coordinates that represents the line geometry */
046: private CoordinateSequence coordinates = null;
047:
048: /** Current line coordinate */
049: private int currentCoord = 0;
050:
051: /** The previous coordinate (during iteration) */
052: private float oldX = Float.NaN;
053: private float oldY = Float.NaN;
054:
055: /** True when the iteration is terminated */
056: private boolean done = false;
057:
058: /** True if the line is a ring */
059: private boolean isClosed;
060:
061: /** If true, apply simple distance based generalization */
062: private boolean generalize = false;
063:
064: /** Maximum distance for point elision when generalizing */
065: private float maxDistance = 1.0f;
066:
067: /** Horizontal scale, got from the affine transform and cached */
068: private float xScale;
069:
070: /** Vertical scale, got from the affine transform and cached */
071: private float yScale;
072:
073: private int coordinateCount;
074:
075: private static final AffineTransform NO_TRANSFORM = new AffineTransform();
076:
077: /**
078: *
079: */
080: public LineIterator() {
081: }
082:
083: /**
084: * Creates a new instance of LineIterator
085: *
086: * @param ls The line string the iterator will use
087: * @param at The affine transform applied to coordinates during iteration
088: */
089: public LineIterator(LineString ls, AffineTransform at,
090: boolean generalize, float maxDistance) {
091: init(ls, at, generalize, maxDistance);
092: }
093:
094: /**
095: * Creates a new instance of LineIterator
096: *
097: * @param ls The line string the iterator will use
098: * @param at The affine transform applied to coordinates during iteration
099: * @param generalize if true apply simple distance based generalization
100: */
101: // public LineIterator(LineString ls, AffineTransform at, boolean generalize) {
102: // this(ls, at);
103: //
104: // }
105: /**
106: * Creates a new instance of LineIterator
107: *
108: * @param ls The line string the iterator will use
109: * @param at The affine transform applied to coordinates during iteration
110: * @param generalize if true apply simple distance based generalization
111: * @param maxDistance during iteration, a point will be skipped if it's
112: * distance from the previous is less than maxDistance
113: */
114: // public LineIterator(
115: // LineString ls, AffineTransform at, boolean generalize,
116: // double maxDistance) {
117: // this(ls, at, generalize);
118: //
119: // }
120: /**
121: * @param ls a LineString
122: * @param at
123: * @param generalize
124: * @param maxDistance
125: * @param xScale
126: * @param yScale
127: */
128: public void init(LineString ls, AffineTransform at,
129: boolean generalize, float maxDistance, float xScale,
130: float yScale) {
131: this .xScale = xScale;
132: this .yScale = yScale;
133:
134: _init(ls, at, generalize, maxDistance);
135: }
136:
137: /**
138: * @param ls
139: * @param at
140: * @param generalize
141: * @param maxDistance
142: */
143: public void init(LineString ls, AffineTransform at,
144: boolean generalize, float maxDistance) {
145: if (at == null)
146: at = new AffineTransform();
147: _init(ls, at, generalize, maxDistance);
148:
149: xScale = (float) Math.sqrt((at.getScaleX() * at.getScaleX())
150: + (at.getShearX() * at.getShearX()));
151: yScale = (float) Math.sqrt((at.getScaleY() * at.getScaleY())
152: + (at.getShearY() * at.getShearY()));
153:
154: }
155:
156: /**
157: * @param ls
158: * @param at
159: * @param generalize
160: * @param maxDistance
161: */
162: private void _init(LineString ls, AffineTransform at,
163: boolean generalize, float maxDistance) {
164: if (at == null) {
165: at = NO_TRANSFORM;
166: }
167:
168: this .at = at;
169: coordinates = ls.getCoordinateSequence();
170: coordinateCount = coordinates.size();
171: isClosed = ls instanceof LinearRing;
172:
173: this .generalize = generalize;
174: this .maxDistance = maxDistance;
175: done = false;
176: currentCoord = 0;
177:
178: oldX = Float.NaN;
179: oldY = Float.NaN;
180: }
181:
182: /**
183: * Sets the distance limit for point skipping during distance based
184: * generalization
185: *
186: * @param distance the maximum distance for point skipping
187: */
188: public void setMaxDistance(float distance) {
189: maxDistance = distance;
190: }
191:
192: /**
193: * Returns the distance limit for point skipping during distance based
194: * generalization
195: *
196: * @return the maximum distance for distance based generalization
197: */
198: public double getMaxDistance() {
199: return maxDistance;
200: }
201:
202: // /**
203: // * Returns the coordinates and type of the current path segment in the
204: // * iteration. The return value is the path-segment type: SEG_MOVETO,
205: // * SEG_LINETO, SEG_QUADTO, SEG_CUBICTO, or SEG_CLOSE. A double array of
206: // * length 6 must be passed in and can be used to store the coordinates of
207: // * the point(s). Each point is stored as a pair of double x,y coordinates.
208: // * SEG_MOVETO and SEG_LINETO types returns one point, SEG_QUADTO returns
209: // * two points, SEG_CUBICTO returns 3 points and SEG_CLOSE does not return
210: // * any points.
211: // *
212: // * @param coords an array that holds the data returned from this method
213: // *
214: // * @return the path-segment type of the current path segment.
215: // *
216: // * @see #SEG_MOVETO
217: // * @see #SEG_LINETO
218: // * @see #SEG_QUADTO
219: // * @see #SEG_CUBICTO
220: // * @see #SEG_CLOSE
221: // */
222: // public int currentSegment(float[] coords) {
223: // if (currentCoord == 0) {
224: // coords[0] = (float) coordinates.getX(0);
225: // coords[1] = (float) coordinates.getY(0);
226: // at.transform(coords, 0, coords, 0, 1);
227: //
228: // return SEG_MOVETO;
229: // } else if ((currentCoord == coordinateCount) && isClosed) {
230: // return SEG_CLOSE;
231: // } else {
232: // coords[0] = oldX; // (float) coordinates.getX(currentCoord);
233: // coords[1] = oldY; // (float) coordinates.getY(currentCoord);
234: // at.transform(coords, 0, coords, 0, 1);
235: //
236: // return SEG_LINETO;
237: // }
238: // }
239:
240: // /**
241: // * Returns the coordinates and type of the current path segment in the
242: // * iteration. The return value is the path-segment type: SEG_MOVETO,
243: // * SEG_LINETO, SEG_QUADTO, SEG_CUBICTO, or SEG_CLOSE. A float array of
244: // * length 6 must be passed in and can be used to store the coordinates of
245: // * the point(s). Each point is stored as a pair of float x,y coordinates.
246: // * SEG_MOVETO and SEG_LINETO types returns one point, SEG_QUADTO returns
247: // * two points, SEG_CUBICTO returns 3 points and SEG_CLOSE does not return
248: // * any points.
249: // *
250: // * @param coords an array that holds the data returned from this method
251: // *
252: // * @return the path-segment type of the current path segment.
253: // *
254: // * @see #SEG_MOVETO
255: // * @see #SEG_LINETO
256: // * @see #SEG_QUADTO
257: // * @see #SEG_CUBICTO
258: // * @see #SEG_CLOSE
259: // */
260: // public int currentSegment(float[] coords) {
261: // double[] dcoords = new double[2];
262: // int result = currentSegment(dcoords);
263: // coords[0] = (float) dcoords[0];
264: // coords[1] = (float) dcoords[1];
265: //
266: // return result;
267: // }
268:
269: /**
270: * Returns the winding rule for determining the interior of the path.
271: *
272: * @return the winding rule.
273: *
274: * @see #WIND_EVEN_ODD
275: * @see #WIND_NON_ZERO
276: */
277: public int getWindingRule() {
278: return WIND_NON_ZERO;
279: }
280:
281: /**
282: * Tests if the iteration is complete.
283: *
284: * @return <code>true</code> if all the segments have been read;
285: * <code>false</code> otherwise.
286: */
287: public boolean isDone() {
288: return done;
289: }
290:
291: /**
292: * Moves the iterator to the next segment of the path forwards along the
293: * primary direction of traversal as long as there are more points in that
294: * direction.
295: */
296: public void next() {
297: if (((currentCoord == (coordinateCount - 1)) && !isClosed)
298: || ((currentCoord == coordinateCount) && isClosed)) {
299: done = true;
300: } else {
301: if (generalize) {
302: if (Float.isNaN(oldX)) {
303: currentCoord++;
304: oldX = (float) coordinates.getX(currentCoord);
305: oldY = (float) coordinates.getY(currentCoord);
306: } else {
307: float distx = 0;
308: float disty = 0;
309: float x = 0;
310: float y = 0;
311:
312: do {
313: currentCoord++;
314: x = (float) coordinates.getX(currentCoord);
315: y = (float) coordinates.getY(currentCoord);
316:
317: if (currentCoord < coordinateCount) {
318: distx = Math.abs(x - oldX);
319: disty = Math.abs(y - oldY);
320: }
321: } while (((distx * xScale) < maxDistance)
322: && ((disty * yScale) < maxDistance)
323: && ((!isClosed && (currentCoord < (coordinateCount - 1))) || (isClosed && (currentCoord < coordinateCount))));
324:
325: oldX = x;
326: oldY = y;
327: }
328: } else {
329: currentCoord++;
330: }
331: }
332: }
333:
334: /**
335: * @see java.awt.geom.PathIterator#currentSegment(double[])
336: */
337: public int currentSegment(double[] coords) {
338: if (currentCoord == 0) {
339: coords[0] = (double) coordinates.getX(0);
340: coords[1] = (double) coordinates.getY(0);
341: at.transform(coords, 0, coords, 0, 1);
342: return SEG_MOVETO;
343: } else if ((currentCoord == coordinateCount) && isClosed) {
344: return SEG_CLOSE;
345: } else {
346: coords[0] = coordinates.getX(currentCoord);
347: coords[1] = coordinates.getY(currentCoord);
348: at.transform(coords, 0, coords, 0, 1);
349:
350: return SEG_LINETO;
351: }
352: }
353:
354: }
|