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.Rectangle;
019: import java.awt.Shape;
020: import java.awt.geom.AffineTransform;
021: import java.awt.geom.PathIterator;
022: import java.awt.geom.Point2D;
023: import java.awt.geom.Rectangle2D;
024: import java.util.ArrayList;
025:
026: import org.opengis.referencing.FactoryException;
027: import org.opengis.referencing.operation.MathTransform;
028: import org.opengis.referencing.operation.TransformException;
029:
030: import com.vividsolutions.jts.geom.Coordinate;
031: import com.vividsolutions.jts.geom.Envelope;
032: import com.vividsolutions.jts.geom.Geometry;
033: import com.vividsolutions.jts.geom.GeometryCollection;
034: import com.vividsolutions.jts.geom.GeometryFactory;
035: import com.vividsolutions.jts.geom.LineString;
036: import com.vividsolutions.jts.geom.LinearRing;
037: import com.vividsolutions.jts.geom.Point;
038: import com.vividsolutions.jts.geom.Polygon;
039:
040: /**
041: * A thin wrapper that adapts a JTS geometry to the Shape interface so that the
042: * geometry can be used by java2d without coordinate cloning
043: *
044: * @author Andrea Aime
045: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/main/src/main/java/org/geotools/geometry/jts/LiteShape2.java $
046: * @version $Id: LiteShape2.java 26769 2007-08-30 12:49:25Z aaime $
047: */
048: public final class LiteShape2 implements Shape, Cloneable {
049:
050: /** The wrapped JTS geometry */
051: private Geometry geometry;
052:
053: private boolean generalize = false;
054:
055: private double maxDistance = 1;
056:
057: // cached iterators
058: private LineIterator2 lineIterator = new LineIterator2();
059:
060: private GeomCollectionIterator collIterator = new GeomCollectionIterator();
061:
062: private EmptyIterator emptyiterator = new EmptyIterator();
063:
064: private static GeometryFactory geomFac;
065:
066: /** transform from dataspace to screenspace */
067: private MathTransform mathTransform;
068:
069: /**
070: * Creates a new LiteShape object.
071: *
072: * @param geom -
073: * the wrapped geometry
074: * @param mathTransform -
075: * the transformation applied to the geometry in order to get to
076: * the shape points
077: * @param decimator -
078: *
079: * @param generalize -
080: * set to true if the geometry need to be generalized during
081: * rendering
082: * @param maxDistance -
083: * distance used in the generalization process
084: * @throws TransformException
085: * @throws FactoryException
086: */
087: public LiteShape2(Geometry geom, MathTransform mathTransform,
088: Decimator decimator, boolean generalize, double maxDistance)
089: throws TransformException, FactoryException {
090: this (geom, mathTransform, decimator, generalize);
091: this .maxDistance = maxDistance;
092: }
093:
094: /**
095: * Creates a new LiteShape object.
096: *
097: * @param geom -
098: * the wrapped geometry
099: * @param mathTransform -
100: * the transformation applied to the geometry in order to get to
101: * the shape points
102: * @param decimator -
103: *
104: * @param generalize -
105: * set to true if the geometry need to be generalized during
106: * rendering
107: *
108: * @throws TransformException
109: * @throws FactoryException
110: */
111: public LiteShape2(Geometry geom, MathTransform mathTransform,
112: Decimator decimator, boolean generalize)
113: throws TransformException, FactoryException {
114: this (geom, mathTransform, decimator, generalize, true);
115: }
116:
117: /**
118: * Creates a new LiteShape object.
119: *
120: * @param geom -
121: * the wrapped geometry
122: * @param mathTransform -
123: * the transformation applied to the geometry in order to get to
124: * the shape points
125: * @param decimator -
126: *
127: * @param generalize -
128: * set to true if the geometry need to be generalized during
129: * rendering
130: *
131: * @param clone - if clone is false the original geometry may be modified directly, if true it will be
132: * cloned to make sure the original remains untouched
133: *
134: * @throws TransformException
135: * @throws FactoryException
136: */
137: public LiteShape2(Geometry geom, MathTransform mathTransform,
138: Decimator decimator, boolean generalize, boolean clone)
139: throws TransformException, FactoryException {
140: if (geom != null) {
141: if (!clone
142: && geom.getFactory().getCoordinateSequenceFactory() instanceof LiteCoordinateSequenceFactory)
143: this .geometry = geom;
144: else if (geom.getFactory().getCoordinateSequenceFactory() instanceof LiteCoordinateSequenceFactory)
145: this .geometry = cloneGeometryLCS(geom); // optimized version
146: else
147: this .geometry = cloneGeometry(geom);
148: }
149:
150: this .mathTransform = mathTransform;
151: if (decimator != null) {
152: decimator.decimateTransformGeneralize(this .geometry,
153: this .mathTransform);
154: } else {
155: // if we have a transform a decimation span can be detected, so try to decimate anyways
156: if (mathTransform != null && !mathTransform.isIdentity()
157: && generalize)
158: new Decimator(mathTransform.inverse())
159: .decimate(this .geometry);
160: if (geometry != null)
161: transformGeometry(geometry);
162: }
163: this .generalize = false;
164: }
165:
166: /**
167: * changes this to a new CSF -- more efficient than the JTS way
168: * @param geom
169: */
170: private final Geometry cloneGeometryLCS(Polygon geom) {
171: LinearRing lr = (LinearRing) cloneGeometryLCS((LinearRing) geom
172: .getExteriorRing());
173: LinearRing[] rings = new LinearRing[geom.getNumInteriorRing()];
174: for (int t = 0; t < rings.length; t++) {
175: rings[t] = (LinearRing) cloneGeometryLCS((LinearRing) geom
176: .getInteriorRingN(t));
177: }
178: return getGeometryFactory().createPolygon(lr, rings);
179: }
180:
181: private final Geometry cloneGeometryLCS(Point geom) {
182: return getGeometryFactory().createPoint(
183: new LiteCoordinateSequence(
184: (LiteCoordinateSequence) geom
185: .getCoordinateSequence()));
186: }
187:
188: private final Geometry cloneGeometryLCS(LineString geom) {
189: return getGeometryFactory().createLineString(
190: new LiteCoordinateSequence(
191: (LiteCoordinateSequence) geom
192: .getCoordinateSequence()));
193: }
194:
195: private final Geometry cloneGeometryLCS(LinearRing geom) {
196: return getGeometryFactory().createLinearRing(
197: new LiteCoordinateSequence(
198: (LiteCoordinateSequence) geom
199: .getCoordinateSequence()));
200: }
201:
202: private final Geometry cloneGeometryLCS(Geometry geom) {
203: if (geom instanceof LineString)
204: return cloneGeometryLCS((LineString) geom);
205: else if (geom instanceof Polygon)
206: return cloneGeometryLCS((Polygon) geom);
207: else if (geom instanceof Point)
208: return cloneGeometryLCS((Point) geom);
209: else
210: return cloneGeometryLCS((GeometryCollection) geom);
211: }
212:
213: private final Geometry cloneGeometryLCS(GeometryCollection geom) {
214: if (geom.getNumGeometries() == 0) {
215: Geometry[] gs = new Geometry[0];
216: return getGeometryFactory().createGeometryCollection(gs);
217: }
218:
219: ArrayList gs = new ArrayList(geom.getNumGeometries());
220: int n = geom.getNumGeometries();
221: for (int t = 0; t < n; t++) {
222: gs.add(t, cloneGeometryLCS(geom.getGeometryN(t)));
223: }
224: return getGeometryFactory().buildGeometry(gs);
225: }
226:
227: /**
228: * changes this to a new CSF -- more efficient than the JTS way
229: * @param geom
230: */
231: private final Geometry cloneGeometry(Polygon geom) {
232: LinearRing lr = (LinearRing) cloneGeometry((LinearRing) geom
233: .getExteriorRing());
234: LinearRing[] rings = new LinearRing[geom.getNumInteriorRing()];
235: for (int t = 0; t < rings.length; t++) {
236: rings[t] = (LinearRing) cloneGeometry((LinearRing) geom
237: .getInteriorRingN(t));
238: }
239: return getGeometryFactory().createPolygon(lr, rings);
240: }
241:
242: private final Geometry cloneGeometry(Point geom) {
243: return getGeometryFactory().createPoint(
244: new LiteCoordinateSequence((Coordinate[]) geom
245: .getCoordinates()));
246: }
247:
248: private final Geometry cloneGeometry(LineString geom) {
249: return getGeometryFactory().createLineString(
250: new LiteCoordinateSequence((Coordinate[]) geom
251: .getCoordinates()));
252: }
253:
254: private final Geometry cloneGeometry(LinearRing geom) {
255: return getGeometryFactory().createLinearRing(
256: new LiteCoordinateSequence((Coordinate[]) geom
257: .getCoordinates()));
258: }
259:
260: private final Geometry cloneGeometry(Geometry geom) {
261: if (geom instanceof LineString)
262: return cloneGeometry((LineString) geom);
263: else if (geom instanceof Polygon)
264: return cloneGeometry((Polygon) geom);
265: else if (geom instanceof Point)
266: return cloneGeometry((Point) geom);
267: else
268: return cloneGeometry((GeometryCollection) geom);
269: }
270:
271: private final Geometry cloneGeometry(GeometryCollection geom) {
272: if (geom.getNumGeometries() == 0) {
273: Geometry[] gs = new Geometry[0];
274: return getGeometryFactory().createGeometryCollection(gs);
275: }
276:
277: ArrayList gs = new ArrayList(geom.getNumGeometries());
278: int n = geom.getNumGeometries();
279: for (int t = 0; t < n; t++) {
280: gs.add(cloneGeometry(geom.getGeometryN(t)));
281: }
282: return getGeometryFactory().buildGeometry(gs);
283: }
284:
285: private void transformGeometry(Geometry geometry)
286: throws TransformException, FactoryException {
287:
288: if (mathTransform == null || mathTransform.isIdentity())
289: return;
290:
291: if (geometry instanceof GeometryCollection) {
292: GeometryCollection collection = (GeometryCollection) geometry;
293: for (int i = 0; i < collection.getNumGeometries(); i++) {
294: transformGeometry(collection.getGeometryN(i));
295: }
296: } else if (geometry instanceof Point) {
297: LiteCoordinateSequence seq = (LiteCoordinateSequence) ((Point) geometry)
298: .getCoordinateSequence();
299: double[] coords = seq.getArray();
300: double[] newCoords = new double[coords.length];
301: mathTransform
302: .transform(coords, 0, newCoords, 0, seq.size());
303: seq.setArray(newCoords);
304: } else if (geometry instanceof Polygon) {
305: Polygon polygon = (Polygon) geometry;
306: transformGeometry(polygon.getExteriorRing());
307: for (int i = 0; i < polygon.getNumInteriorRing(); i++) {
308: transformGeometry(polygon.getInteriorRingN(i));
309: }
310: } else if (geometry instanceof LineString) {
311: LiteCoordinateSequence seq = (LiteCoordinateSequence) ((LineString) geometry)
312: .getCoordinateSequence();
313: double[] coords = seq.getArray();
314: mathTransform.transform(coords, 0, coords, 0, seq.size());
315: seq.setArray(coords);
316: }
317: }
318:
319: private GeometryFactory getGeometryFactory() {
320: if (geomFac == null) {
321: geomFac = new GeometryFactory(
322: new LiteCoordinateSequenceFactory());
323: }
324:
325: return geomFac;
326: }
327:
328: /**
329: * Sets the geometry contained in this lite shape. Convenient to reuse this
330: * object instead of creating it again and again during rendering
331: *
332: * @param g
333: * @throws TransformException
334: * @throws FactoryException
335: */
336: public void setGeometry(Geometry g) throws TransformException,
337: FactoryException {
338: if (g != null) {
339: this .geometry = getGeometryFactory().createGeometry(g);
340: transformGeometry(geometry);
341: }
342: }
343:
344: /**
345: * Tests if the interior of the <code>Shape</code> entirely contains the
346: * specified <code>Rectangle2D</code>. This method might conservatively
347: * return <code>false</code> when:
348: *
349: * <ul>
350: * <li>the <code>intersect</code> method returns <code>true</code> and
351: * </li>
352: * <li>the calculations to determine whether or not the <code>Shape</code>
353: * entirely contains the <code>Rectangle2D</code> are prohibitively
354: * expensive.</li>
355: * </ul>
356: *
357: * This means that this method might return <code>false</code> even though
358: * the <code>Shape</code> contains the <code>Rectangle2D</code>. The
359: * <code>Area</code> class can be used to perform more accurate
360: * computations of geometric intersection for any <code>Shape</code>
361: * object if a more precise answer is required.
362: *
363: * @param r
364: * The specified <code>Rectangle2D</code>
365: *
366: * @return <code>true</code> if the interior of the <code>Shape</code>
367: * entirely contains the <code>Rectangle2D</code>;
368: * <code>false</code> otherwise or, if the <code>Shape</code>
369: * contains the <code>Rectangle2D</code> and the
370: * <code>intersects</code> method returns <code>true</code> and
371: * the containment calculations would be too expensive to perform.
372: *
373: * @see #contains(double, double, double, double)
374: */
375: public boolean contains(Rectangle2D r) {
376: Geometry rect = rectangleToGeometry(r);
377:
378: return geometry.contains(rect);
379: }
380:
381: /**
382: * Tests if a specified {@link Point2D}is inside the boundary of the
383: * <code>Shape</code>.
384: *
385: * @param p
386: * a specified <code>Point2D</code>
387: *
388: * @return <code>true</code> if the specified <code>Point2D</code> is
389: * inside the boundary of the <code>Shape</code>;
390: * <code>false</code> otherwise.
391: */
392: public boolean contains(Point2D p) {
393: Coordinate coord = new Coordinate(p.getX(), p.getY());
394: Geometry point = geometry.getFactory().createPoint(coord);
395:
396: return geometry.contains(point);
397: }
398:
399: /**
400: * Tests if the specified coordinates are inside the boundary of the
401: * <code>Shape</code>.
402: *
403: * @param x
404: * the specified coordinates, x value
405: * @param y
406: * the specified coordinates, y value
407: *
408: * @return <code>true</code> if the specified coordinates are inside the
409: * <code>Shape</code> boundary; <code>false</code> otherwise.
410: */
411: public boolean contains(double x, double y) {
412: Coordinate coord = new Coordinate(x, y);
413: Geometry point = geometry.getFactory().createPoint(coord);
414:
415: return geometry.contains(point);
416: }
417:
418: /**
419: * Tests if the interior of the <code>Shape</code> entirely contains the
420: * specified rectangular area. All coordinates that lie inside the
421: * rectangular area must lie within the <code>Shape</code> for the entire
422: * rectanglar area to be considered contained within the <code>Shape</code>.
423: *
424: * <p>
425: * This method might conservatively return <code>false</code> when:
426: *
427: * <ul>
428: * <li>the <code>intersect</code> method returns <code>true</code> and
429: * </li>
430: * <li>the calculations to determine whether or not the <code>Shape</code>
431: * entirely contains the rectangular area are prohibitively expensive.</li>
432: * </ul>
433: *
434: * This means that this method might return <code>false</code> even though
435: * the <code>Shape</code> contains the rectangular area. The
436: * <code>Area</code> class can be used to perform more accurate
437: * computations of geometric intersection for any <code>Shape</code>
438: * object if a more precise answer is required.
439: * </p>
440: *
441: * @param x
442: * the coordinates of the specified rectangular area, x value
443: * @param y
444: * the coordinates of the specified rectangular area, y value
445: * @param w
446: * the width of the specified rectangular area
447: * @param h
448: * the height of the specified rectangular area
449: *
450: * @return <code>true</code> if the interior of the <code>Shape</code>
451: * entirely contains the specified rectangular area;
452: * <code>false</code> otherwise or, if the <code>Shape</code>
453: * contains the rectangular area and the <code>intersects</code>
454: * method returns <code>true</code> and the containment
455: * calculations would be too expensive to perform.
456: *
457: * @see java.awt.geom.Area
458: * @see #intersects
459: */
460: public boolean contains(double x, double y, double w, double h) {
461: Geometry rect = createRectangle(x, y, w, h);
462:
463: return geometry.contains(rect);
464: }
465:
466: /**
467: * Returns an integer {@link Rectangle}that completely encloses the
468: * <code>Shape</code>. Note that there is no guarantee that the returned
469: * <code>Rectangle</code> is the smallest bounding box that encloses the
470: * <code>Shape</code>, only that the <code>Shape</code> lies entirely
471: * within the indicated <code>Rectangle</code>. The returned
472: * <code>Rectangle</code> might also fail to completely enclose the
473: * <code>Shape</code> if the <code>Shape</code> overflows the limited
474: * range of the integer data type. The <code>getBounds2D</code> method
475: * generally returns a tighter bounding box due to its greater flexibility
476: * in representation.
477: *
478: * @return an integer <code>Rectangle</code> that completely encloses the
479: * <code>Shape</code>.
480: *
481: * @see #getBounds2D
482: */
483: public Rectangle getBounds() {
484: Coordinate[] coords = geometry.getEnvelope().getCoordinates();
485:
486: // get out corners. the documentation doens't specify in which
487: // order the bounding box coordinates are returned
488: double x1;
489:
490: // get out corners. the documentation doens't specify in which
491: // order the bounding box coordinates are returned
492: double y1;
493:
494: // get out corners. the documentation doens't specify in which
495: // order the bounding box coordinates are returned
496: double x2;
497:
498: // get out corners. the documentation doens't specify in which
499: // order the bounding box coordinates are returned
500: double y2;
501: x1 = x2 = coords[0].x;
502: y1 = y2 = coords[0].y;
503:
504: for (int i = 1; i < 3; i++) {
505: double x = coords[i].x;
506: double y = coords[i].y;
507:
508: if (x < x1) {
509: x1 = x;
510: }
511:
512: if (x > x2) {
513: x2 = x;
514: }
515:
516: if (y < y1) {
517: y1 = y;
518: }
519:
520: if (y > y2) {
521: y2 = y;
522: }
523: }
524:
525: x1 = Math.ceil(x1);
526: x2 = Math.floor(x2);
527: y1 = Math.ceil(y1);
528: y2 = Math.floor(y2);
529:
530: return new Rectangle((int) x1, (int) y1, (int) (x2 - x1),
531: (int) (y2 - y1));
532: }
533:
534: /**
535: * Returns a high precision and more accurate bounding box of the
536: * <code>Shape</code> than the <code>getBounds</code> method. Note that
537: * there is no guarantee that the returned {@link Rectangle2D}is the
538: * smallest bounding box that encloses the <code>Shape</code>, only that
539: * the <code>Shape</code> lies entirely within the indicated
540: * <code>Rectangle2D</code>. The bounding box returned by this method is
541: * usually tighter than that returned by the <code>getBounds</code> method
542: * and never fails due to overflow problems since the return value can be an
543: * instance of the <code>Rectangle2D</code> that uses double precision
544: * values to store the dimensions.
545: *
546: * @return an instance of <code>Rectangle2D</code> that is a
547: * high-precision bounding box of the <code>Shape</code>.
548: *
549: * @see #getBounds
550: */
551: public Rectangle2D getBounds2D() {
552: Envelope env = geometry.getEnvelopeInternal();
553: return new Rectangle2D.Double(env.getMinX(), env.getMinY(), env
554: .getWidth(), env.getHeight());
555: }
556:
557: /**
558: * Returns an iterator object that iterates along the <code>Shape</code>
559: * boundary and provides access to the geometry of the <code>Shape</code>
560: * outline. If an optional {@link AffineTransform}is specified, the
561: * coordinates returned in the iteration are transformed accordingly.
562: *
563: * <p>
564: * Each call to this method returns a fresh <code>PathIterator</code>
565: * object that traverses the geometry of the <code>Shape</code> object
566: * independently from any other <code>PathIterator</code> objects in use
567: * at the same time.
568: * </p>
569: *
570: * <p>
571: * It is recommended, but not guaranteed, that objects implementing the
572: * <code>Shape</code> interface isolate iterations that are in process
573: * from any changes that might occur to the original object's geometry
574: * during such iterations.
575: * </p>
576: *
577: * <p>
578: * Before using a particular implementation of the <code>Shape</code>
579: * interface in more than one thread simultaneously, refer to its
580: * documentation to verify that it guarantees that iterations are isolated
581: * from modifications.
582: * </p>
583: *
584: * @param at
585: * an optional <code>AffineTransform</code> to be applied to
586: * the coordinates as they are returned in the iteration, or
587: * <code>null</code> if untransformed coordinates are desired
588: *
589: * @return a new <code>PathIterator</code> object, which independently
590: * traverses the geometry of the <code>Shape</code>.
591: */
592: public PathIterator getPathIterator(AffineTransform at) {
593: PathIterator pi = null;
594:
595: if (this .geometry.isEmpty())
596: return emptyiterator;
597:
598: // return iterator according to the kind of geometry we include
599: if (this .geometry instanceof Point) {
600: pi = new PointIterator((Point) geometry, at);
601: }
602:
603: if (this .geometry instanceof Polygon) {
604:
605: pi = new PolygonIterator((Polygon) geometry, at,
606: generalize, maxDistance);
607: } else if (this .geometry instanceof LineString) {
608: lineIterator.init((LineString) geometry, at);
609: pi = lineIterator;
610: } else if (this .geometry instanceof GeometryCollection) {
611: collIterator.init((GeometryCollection) geometry, at,
612: generalize, maxDistance);
613: pi = collIterator;
614: }
615: return pi;
616: }
617:
618: /**
619: * Returns an iterator object that iterates along the <code>Shape</code>
620: * boundary and provides access to a flattened view of the
621: * <code>Shape</code> outline geometry.
622: *
623: * <p>
624: * Only SEG_MOVETO, SEG_LINETO, and SEG_CLOSE point types are returned by
625: * the iterator.
626: * </p>
627: *
628: * <p>
629: * If an optional <code>AffineTransform</code> is specified, the
630: * coordinates returned in the iteration are transformed accordingly.
631: * </p>
632: *
633: * <p>
634: * The amount of subdivision of the curved segments is controlled by the
635: * <code>flatness</code> parameter, which specifies the maximum distance
636: * that any point on the unflattened transformed curve can deviate from the
637: * returned flattened path segments. Note that a limit on the accuracy of
638: * the flattened path might be silently imposed, causing very small
639: * flattening parameters to be treated as larger values. This limit, if
640: * there is one, is defined by the particular implementation that is used.
641: * </p>
642: *
643: * <p>
644: * Each call to this method returns a fresh <code>PathIterator</code>
645: * object that traverses the <code>Shape</code> object geometry
646: * independently from any other <code>PathIterator</code> objects in use
647: * at the same time.
648: * </p>
649: *
650: * <p>
651: * It is recommended, but not guaranteed, that objects implementing the
652: * <code>Shape</code> interface isolate iterations that are in process
653: * from any changes that might occur to the original object's geometry
654: * during such iterations.
655: * </p>
656: *
657: * <p>
658: * Before using a particular implementation of this interface in more than
659: * one thread simultaneously, refer to its documentation to verify that it
660: * guarantees that iterations are isolated from modifications.
661: * </p>
662: *
663: * @param at
664: * an optional <code>AffineTransform</code> to be applied to
665: * the coordinates as they are returned in the iteration, or
666: * <code>null</code> if untransformed coordinates are desired
667: * @param flatness
668: * the maximum distance that the line segments used to
669: * approximate the curved segments are allowed to deviate from
670: * any point on the original curve
671: *
672: * @return a new <code>PathIterator</code> that independently traverses
673: * the <code>Shape</code> geometry.
674: */
675: public PathIterator getPathIterator(AffineTransform at,
676: double flatness) {
677: return getPathIterator(at);
678: }
679:
680: /**
681: * Tests if the interior of the <code>Shape</code> intersects the interior
682: * of a specified <code>Rectangle2D</code>. This method might
683: * conservatively return <code>true</code> when:
684: *
685: * <ul>
686: * <li>there is a high probability that the <code>Rectangle2D</code> and
687: * the <code>Shape</code> intersect, but</li>
688: * <li>the calculations to accurately determine this intersection are
689: * prohibitively expensive.</li>
690: * </ul>
691: *
692: * This means that this method might return <code>true</code> even though
693: * the <code>Rectangle2D</code> does not intersect the <code>Shape</code>.
694: *
695: * @param r
696: * the specified <code>Rectangle2D</code>
697: *
698: * @return <code>true</code> if the interior of the <code>Shape</code>
699: * and the interior of the specified <code>Rectangle2D</code>
700: * intersect, or are both highly likely to intersect and
701: * intersection calculations would be too expensive to perform;
702: * <code>false</code> otherwise.
703: *
704: * @see #intersects(double, double, double, double)
705: */
706: public boolean intersects(Rectangle2D r) {
707: Geometry rect = rectangleToGeometry(r);
708:
709: return geometry.intersects(rect);
710: }
711:
712: /**
713: * Tests if the interior of the <code>Shape</code> intersects the interior
714: * of a specified rectangular area. The rectangular area is considered to
715: * intersect the <code>Shape</code> if any point is contained in both the
716: * interior of the <code>Shape</code> and the specified rectangular area.
717: *
718: * <p>
719: * This method might conservatively return <code>true</code> when:
720: *
721: * <ul>
722: * <li>there is a high probability that the rectangular area and the
723: * <code>Shape</code> intersect, but</li>
724: * <li>the calculations to accurately determine this intersection are
725: * prohibitively expensive.</li>
726: * </ul>
727: *
728: * This means that this method might return <code>true</code> even though
729: * the rectangular area does not intersect the <code>Shape</code>. The
730: * {@link java.awt.geom.Area Area}class can be used to perform more
731: * accurate computations of geometric intersection for any
732: * <code>Shape</code> object if a more precise answer is required.
733: * </p>
734: *
735: * @param x
736: * the coordinates of the specified rectangular area, x value
737: * @param y
738: * the coordinates of the specified rectangular area, y value
739: * @param w
740: * the width of the specified rectangular area
741: * @param h
742: * the height of the specified rectangular area
743: *
744: * @return <code>true</code> if the interior of the <code>Shape</code>
745: * and the interior of the rectangular area intersect, or are both
746: * highly likely to intersect and intersection calculations would be
747: * too expensive to perform; <code>false</code> otherwise.
748: *
749: * @see java.awt.geom.Area
750: */
751: public boolean intersects(double x, double y, double w, double h) {
752: Geometry rect = createRectangle(x, y, w, h);
753:
754: return geometry.intersects(rect);
755: }
756:
757: /**
758: * Converts the Rectangle2D passed as parameter in a jts Geometry object
759: *
760: * @param r
761: * the rectangle to be converted
762: *
763: * @return a geometry with the same vertices as the rectangle
764: */
765: private Geometry rectangleToGeometry(Rectangle2D r) {
766: return createRectangle(r.getMinX(), r.getMinY(), r.getWidth(),
767: r.getHeight());
768: }
769:
770: /**
771: * Creates a jts Geometry object representing a rectangle with the given
772: * parameters
773: *
774: * @param x
775: * left coordinate
776: * @param y
777: * bottom coordinate
778: * @param w
779: * width
780: * @param h
781: * height
782: *
783: * @return a rectangle with the specified position and size
784: */
785: private Geometry createRectangle(double x, double y, double w,
786: double h) {
787: Coordinate[] coords = { new Coordinate(x, y),
788: new Coordinate(x, y + h), new Coordinate(x + w, y + h),
789: new Coordinate(x + w, y), new Coordinate(x, y) };
790: LinearRing lr = geometry.getFactory().createLinearRing(coords);
791:
792: return geometry.getFactory().createPolygon(lr, null);
793: }
794:
795: public MathTransform getMathTransform() {
796: return mathTransform;
797: }
798:
799: public Geometry getGeometry() {
800: return geometry;
801: }
802: }
|