001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2003, Geotools Project Managment Committee (PMC)
005: * (C) 2003, Institut de Recherche pour le D?veloppement
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: */
017: package org.geotools.geometry.jts;
018:
019: // J2SE dependencies
020: import java.awt.Rectangle;
021: import java.awt.Shape;
022: import java.awt.geom.AffineTransform;
023: import java.awt.geom.NoninvertibleTransformException;
024: import java.awt.geom.PathIterator;
025: import java.awt.geom.Point2D;
026: import java.awt.geom.Rectangle2D;
027: import java.util.logging.Level;
028: import java.util.logging.LogRecord;
029: import java.util.logging.Logger;
030:
031: import org.geotools.referencing.operation.matrix.XAffineTransform;
032:
033: /**
034: * Apply an arbitrary {@link AffineTransform} on a {@link Shape}. This class is
035: * used internally by {@link RenderedMarks}. It is designed for reuse with many
036: * different affine transforms and shapes. This class is <strong>not</strong>
037: * thread-safe.
038: *
039: * @source $URL:
040: * http://svn.geotools.org/geotools/trunk/gt/module/render/src/org/geotools/renderer/lite/TransformedShape.java $
041: * @version $Id: TransformedShape.java 27862 2007-11-12 19:51:19Z desruisseaux $
042: * @author Martin Desruisseaux
043: */
044: public final class TransformedShape extends AffineTransform implements
045: Shape {
046: /**
047: * The wrapped shape.
048: */
049: public Shape shape;
050:
051: /**
052: * A temporary point.
053: */
054: private final Point2D.Double point = new Point2D.Double();
055:
056: /**
057: * A temporary rectangle.
058: */
059: private final Rectangle2D.Double rectangle = new Rectangle2D.Double();
060:
061: /**
062: * Construct a transformed shape initialized to the identity transform.
063: */
064: public TransformedShape() {
065: }
066:
067: /**
068: * Returns the 6 coefficients values.
069: */
070: public void getMatrix(final float[] matrix, int offset) {
071: matrix[offset] = (float) getScaleX(); // m00
072: matrix[++offset] = (float) getShearY(); // m10
073: matrix[++offset] = (float) getShearX(); // m01
074: matrix[++offset] = (float) getScaleY(); // m11
075: matrix[++offset] = (float) getTranslateX(); // m02
076: matrix[++offset] = (float) getTranslateY(); // m12
077: }
078:
079: /**
080: * Set the transform from a flat matrix.
081: *
082: * @param matrix
083: * The flat matrix.
084: * @param offset
085: * The index of the first element to use in <code>matrix</code>.
086: */
087: public void setTransform(final float[] matrix, int offset) {
088: setTransform(matrix[offset], matrix[++offset],
089: matrix[++offset], matrix[++offset], matrix[++offset],
090: matrix[++offset]);
091: }
092:
093: /**
094: * Set the transform from a flat matrix.
095: *
096: * @param matrix
097: * The flat matrix.
098: *
099: */
100: public void setTransform(final double[] matrix) {
101: setTransform(matrix[0], matrix[1], matrix[2], matrix[3],
102: matrix[4], matrix[5]);
103: }
104:
105: /**
106: * Apply a uniform scale.
107: */
108: public void scale(final double s) {
109: scale(s, s);
110: }
111:
112: /**
113: * Tests if the specified coordinates are inside the boundary of the
114: * <code>Shape</code>.
115: */
116: public boolean contains(double x, double y) {
117: point.x = x;
118: point.y = y;
119: return contains(point);
120: }
121:
122: /**
123: * Tests if a specified {@link Point2D} is inside the boundary of the
124: * <code>Shape</code>.
125: */
126: public boolean contains(final Point2D p) {
127: try {
128: return shape.contains(inverseTransform(p, point));
129: } catch (NoninvertibleTransformException exception) {
130: exceptionOccured(exception, "contains");
131: return false;
132: }
133: }
134:
135: /**
136: * Tests if the interior of the <code>Shape</code> entirely contains the
137: * specified rectangular area.
138: */
139: public boolean contains(double x, double y, double width,
140: double height) {
141: rectangle.x = x;
142: rectangle.y = y;
143: rectangle.width = width;
144: rectangle.height = height;
145: return contains(rectangle);
146: }
147:
148: /**
149: * Tests if the interior of the <code>Shape</code> entirely contains the
150: * specified <code>Rectangle2D</code>. This method might conservatively
151: * return <code>false</code>.
152: */
153: public boolean contains(final Rectangle2D r) {
154: try {
155: return shape.contains(XAffineTransform.inverseTransform(
156: this , r, rectangle));
157: } catch (NoninvertibleTransformException exception) {
158: exceptionOccured(exception, "contains");
159: return false;
160: }
161: }
162:
163: /**
164: * Tests if the interior of the <code>Shape</code> intersects the interior
165: * of a specified rectangular area.
166: */
167: public boolean intersects(double x, double y, double width,
168: double height) {
169: rectangle.x = x;
170: rectangle.y = y;
171: rectangle.width = width;
172: rectangle.height = height;
173: return intersects(rectangle);
174: }
175:
176: /**
177: * Tests if the interior of the <code>Shape</code> intersects the interior
178: * of a specified <code>Rectangle2D</code>. This method might
179: * conservatively return <code>true</code>.
180: */
181: public boolean intersects(final Rectangle2D r) {
182: try {
183: return shape.intersects(XAffineTransform.inverseTransform(
184: this , r, rectangle));
185: } catch (NoninvertibleTransformException exception) {
186: exceptionOccured(exception, "intersects");
187: return false;
188: }
189: }
190:
191: /**
192: * Returns an integer {@link Rectangle} that completely encloses the
193: * <code>Shape</code>.
194: */
195: public Rectangle getBounds() {
196: final Rectangle rect = shape.getBounds();
197: return (Rectangle) XAffineTransform.transform(this , rect, rect);
198: }
199:
200: /**
201: * Returns a high precision and more accurate bounding box of the
202: * <code>Shape</code> than the <code>getBounds</code> method.
203: *
204: * @todo REVISIT: tranform currently results in a new rectangle being
205: * created, is this a memory overhead?
206: */
207: public Rectangle2D getBounds2D() {
208: final Rectangle2D rect = shape.getBounds2D();
209: return XAffineTransform.transform(this , rect, null);
210: // REVISIT: used to read (this,rect,rect) - this can result in an
211: // unmidifiable geometry exception
212: }
213:
214: /**
215: * Returns an iterator object that iterates along the <code>Shape</code>
216: * boundary and provides access to the geometry of the <code>Shape</code>
217: * outline.
218: */
219: public PathIterator getPathIterator(AffineTransform at) {
220: if (!isIdentity()) {
221: if (at == null || at.isIdentity()) {
222: return shape.getPathIterator(this );
223: }
224: at = new AffineTransform(at);
225: at.concatenate(this );
226: }
227: return shape.getPathIterator(at);
228: }
229:
230: /**
231: * Returns an iterator object that iterates along the <code>Shape</code>
232: * boundary and provides access to a flattened view of the
233: * <code>Shape</code> outline geometry.
234: */
235: public PathIterator getPathIterator(AffineTransform at,
236: final double flatness) {
237: if (!isIdentity()) {
238: if (at == null || at.isIdentity()) {
239: return shape.getPathIterator(this , flatness);
240: }
241: at = new AffineTransform(at);
242: at.concatenate(this );
243: }
244: return shape.getPathIterator(at, flatness);
245: }
246:
247: /**
248: * Invoked when an inverse transform was required but the transform is not
249: * invertible. This error should not happen. However, even if it happen, it
250: * will not prevent the application to work since <code>contains(...)</code>
251: * method may conservatively return <code>false</code>. We will just log
252: * a warning message and continue.
253: */
254: private static void exceptionOccured(
255: final NoninvertibleTransformException exception,
256: final String method) {
257: final LogRecord record = new LogRecord(Level.WARNING, exception
258: .getLocalizedMessage());
259: record.setSourceClassName(TransformedShape.class.getName());
260: record.setSourceMethodName(method);
261: record.setThrown(exception);
262: org.geotools.util.logging.Logging.getLogger(
263: "org.geotools.renderer.lite").log(record);
264: }
265: }
|