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:
020: import org.opengis.referencing.operation.MathTransform;
021: import org.opengis.referencing.operation.MathTransform2D;
022: import org.opengis.referencing.operation.TransformException;
023:
024: import com.vividsolutions.jts.geom.Coordinate;
025: import com.vividsolutions.jts.geom.CoordinateSequence;
026: import com.vividsolutions.jts.geom.LineString;
027: import com.vividsolutions.jts.geom.Polygon;
028:
029: /**
030: * A path iterator for the LiteShape class, specialized to iterate over Polygon
031: * objects.
032: *
033: * @author Andrea Aime
034: * @author simone giannecchini
035: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/main/src/main/java/org/geotools/geometry/jts/PolygonIterator.java $
036: * @version $Id: PolygonIterator.java 25086 2007-04-10 08:54:25Z aaime $
037: */
038: public final class PolygonIterator extends AbstractLiteIterator {
039: /** Transform applied on the coordinates during iteration */
040: private AffineTransform at;
041:
042: /** The rings describing the polygon geometry */
043: private LineString[] rings;
044:
045: /** The current ring during iteration */
046: private int currentRing = 0;
047:
048: /** Current line coordinate */
049: private int currentCoord = 0;
050:
051: /** The array of coordinates that represents the line geometry */
052: private CoordinateSequence coords = null;
053:
054: /** The previous coordinate (during iteration) */
055: private Coordinate oldCoord = null;
056:
057: /** True when the iteration is terminated */
058: private boolean done = false;
059:
060: /** If true, apply simple distance based generalization */
061: private boolean generalize = false;
062:
063: /** Maximum distance for point elision when generalizing */
064: private double maxDistance = 1.0;
065:
066: /** Horizontal scale, got from the affine transform and cached */
067: private double xScale;
068:
069: /** Vertical scale, got from the affine transform and cached */
070: private double yScale;
071:
072: /**
073: * Creates a new PolygonIterator object.
074: *
075: * @param p The polygon
076: * @param at The affine transform applied to coordinates during iteration
077: */
078: public PolygonIterator(Polygon p, AffineTransform at) {
079: int numInteriorRings = p.getNumInteriorRing();
080: rings = new LineString[numInteriorRings + 1];
081: rings[0] = p.getExteriorRing();
082:
083: for (int i = 0; i < numInteriorRings; i++) {
084: rings[i + 1] = p.getInteriorRingN(i);
085: }
086:
087: if (at == null) {
088: at = new AffineTransform();
089: }
090:
091: this .at = at;
092: xScale = Math.sqrt((at.getScaleX() * at.getScaleX())
093: + (at.getShearX() * at.getShearX()));
094: yScale = Math.sqrt((at.getScaleY() * at.getScaleY())
095: + (at.getShearY() * at.getShearY()));
096:
097: coords = rings[0].getCoordinateSequence();
098: }
099:
100: /**
101: * Creates a new PolygonIterator object.
102: *
103: * @param p The polygon
104: * @param at The affine transform applied to coordinates during iteration
105: * @param generalize if true apply simple distance based generalization
106: */
107: public PolygonIterator(Polygon p, AffineTransform at,
108: boolean generalize) {
109: this (p, at);
110: this .generalize = generalize;
111: }
112:
113: /**
114: * Creates a new PolygonIterator object.
115: *
116: * @param p The polygon
117: * @param at The affine transform applied to coordinates during iteration
118: * @param generalize if true apply simple distance based generalization
119: * @param maxDistance during iteration, a point will be skipped if it's
120: * distance from the previous is less than maxDistance
121: */
122: public PolygonIterator(Polygon p, AffineTransform at,
123: boolean generalize, double maxDistance) {
124: this (p, at, generalize);
125: this .maxDistance = maxDistance;
126: }
127:
128: /**
129: * Sets the distance limit for point skipping during distance based
130: * generalization
131: *
132: * @param distance the maximum distance for point skipping
133: */
134: public void setMaxDistance(double distance) {
135: maxDistance = distance;
136: }
137:
138: /**
139: * Returns the distance limit for point skipping during distance based
140: * generalization
141: *
142: * @return the maximum distance for distance based generalization
143: */
144: public double getMaxDistance() {
145: return maxDistance;
146: }
147:
148: /**
149: * Returns the coordinates and type of the current path segment in the
150: * iteration. The return value is the path-segment type: SEG_MOVETO,
151: * SEG_LINETO, SEG_QUADTO, SEG_CUBICTO, or SEG_CLOSE. A double array of
152: * length 6 must be passed in and can be used to store the coordinates of
153: * the point(s). Each point is stored as a pair of double x,y coordinates.
154: * SEG_MOVETO and SEG_LINETO types returns one point, SEG_QUADTO returns
155: * two points, SEG_CUBICTO returns 3 points and SEG_CLOSE does not return
156: * any points.
157: *
158: * @param coords an array that holds the data returned from this method
159: *
160: * @return the path-segment type of the current path segment.
161: *
162: * @see #SEG_MOVETO
163: * @see #SEG_LINETO
164: * @see #SEG_QUADTO
165: * @see #SEG_CUBICTO
166: * @see #SEG_CLOSE
167: */
168: public int currentSegment(double[] coords) {
169: // first make sure we're not at the last element, this prevents us from exceptions
170: // in the case where coords.size() == 0
171: if (currentCoord == this .coords.size()) {
172: return SEG_CLOSE;
173: } else if (currentCoord == 0) {
174: coords[0] = this .coords.getX(0);
175: coords[1] = this .coords.getY(0);
176: transform(coords, 0, coords, 0, 1);
177:
178: return SEG_MOVETO;
179: } else {
180: coords[0] = this .coords.getX(currentCoord);
181: coords[1] = this .coords.getY(currentCoord);
182: transform(coords, 0, coords, 0, 1);
183:
184: return SEG_LINETO;
185: }
186: }
187:
188: protected void transform(double[] src, int index, double[] dest,
189: int destIndex, int numPoints) {
190: at.transform(src, index, dest, destIndex, numPoints);
191: }
192:
193: /**
194: * Return the winding rule for determining the interior of the path.
195: *
196: * @return <code>WIND_EVEN_ODD</code> by default.
197: */
198: public int getWindingRule() {
199: return WIND_EVEN_ODD;
200: }
201:
202: /**
203: * Tests if the iteration is complete.
204: *
205: * @return <code>true</code> if all the segments have been read;
206: * <code>false</code> otherwise.
207: */
208: public boolean isDone() {
209: return done;
210: }
211:
212: /**
213: * Moves the iterator to the next segment of the path forwards along the
214: * primary direction of traversal as long as there are more points in that
215: * direction.
216: */
217: public void next() {
218: if (currentCoord == coords.size()) {
219: if (currentRing < (rings.length - 1)) {
220: currentCoord = 0;
221: currentRing++;
222: coords = rings[currentRing].getCoordinateSequence();
223: } else {
224: done = true;
225: }
226: } else {
227: if (generalize) {
228: if (oldCoord == null) {
229: currentCoord++;
230: oldCoord = coords.getCoordinate(currentCoord);
231: } else {
232: double distx = 0;
233: double disty = 0;
234:
235: do {
236: currentCoord++;
237:
238: if (currentCoord < coords.size()) {
239: distx = Math.abs(coords.getX(currentCoord)
240: - oldCoord.x);
241: disty = Math.abs(coords.getY(currentCoord)
242: - oldCoord.y);
243: }
244: } while (((distx * xScale) < maxDistance)
245: && ((disty * yScale) < maxDistance)
246: && (currentCoord < coords.size()));
247:
248: if (currentCoord < coords.size()) {
249: oldCoord = coords.getCoordinate(currentCoord);
250: } else {
251: oldCoord = null;
252: }
253: }
254: } else {
255: currentCoord++;
256: }
257: }
258: }
259:
260: }
|