All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jaitools.imageutils.shape.LiteShape Maven / Gradle / Ivy

Go to download

Support and utility classes used by other JAITools components and available for general use.

There is a newer version: 1.6.0
Show newest version
/* 
 *  Copyright (c) 2011, Andrea Aime. All rights reserved. 
 *   
 *  Redistribution and use in source and binary forms, with or without modification, 
 *  are permitted provided that the following conditions are met: 
 *   
 *  - Redistributions of source code must retain the above copyright notice, this  
 *    list of conditions and the following disclaimer. 
 *   
 *  - Redistributions in binary form must reproduce the above copyright notice, this 
 *    list of conditions and the following disclaimer in the documentation and/or 
 *    other materials provided with the distribution.   
 *   
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
 *  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
 *  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 
 *  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
 *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 
 *  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 */   

package org.jaitools.imageutils.shape;


import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;


import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryCollection;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.LinearRing;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;


/**
 * A thin wrapper that adapts a JTS geometry to the Shape interface so that the geometry 
 * can be used by java2d without coordinate cloning.
 * 

* This class was ported back and simplified from GeoTools, with permission from the author(s). * * @author Andrea Aime * @since 1.2 * @version $Id: $ */ public class LiteShape implements Shape, Cloneable { /** The wrapped JTS geometry */ private Geometry geometry; /** * Creates a new LiteShape object. * * @param geom - the wrapped geometry * */ public LiteShape(Geometry geom) { if (geom != null){ this.geometry = (Geometry) geom.clone(); } } /** * Sets the geometry contained in this lite shape. Convenient to reuse this * object instead of creating it again and again during rendering * * @param g */ public void setGeometry(Geometry g) { this.geometry = (Geometry) g.clone(); } /** * Tests if the interior of the Shape entirely contains the * specified Rectangle2D. This method might conservatively * return false when: * *

    *
  • * the intersect method returns true and *
  • *
  • * the calculations to determine whether or not the Shape * entirely contains the Rectangle2D are prohibitively * expensive. *
  • *
* * This means that this method might return false even though * the Shape contains the Rectangle2D. The * Area class can be used to perform more accurate * computations of geometric intersection for any Shape * object if a more precise answer is required. * * @param r The specified Rectangle2D * * @return true if the interior of the Shape * entirely contains the Rectangle2D; * false otherwise or, if the Shape * contains the Rectangle2D and the * intersects method returns true and * the containment calculations would be too expensive to perform. * * @see #contains(double, double, double, double) */ public boolean contains(Rectangle2D r) { Geometry rect = rectangleToGeometry(r); return geometry.contains(rect); } /** * Tests if a specified {@link Point2D} is inside the boundary of the * Shape. * * @param p a specified Point2D * * @return true if the specified Point2D is * inside the boundary of the Shape; * false otherwise. */ public boolean contains(Point2D p) { Coordinate coord = new Coordinate(p.getX(), p.getY()); Geometry point = geometry.getFactory().createPoint(coord); return geometry.contains(point); } /** * Tests if the specified coordinates are inside the boundary of the * Shape. * * @param x the specified coordinates, x value * @param y the specified coordinates, y value * * @return true if the specified coordinates are inside the * Shape boundary; false otherwise. */ public boolean contains(double x, double y) { Coordinate coord = new Coordinate(x, y); Geometry point = geometry.getFactory().createPoint(coord); return geometry.contains(point); } /** * Tests if the interior of the Shape entirely contains the * specified rectangular area. All coordinates that lie inside the * rectangular area must lie within the Shape for the entire * rectanglar area to be considered contained within the * Shape. * *

* This method might conservatively return false when: * *

    *
  • * the intersect method returns true and *
  • *
  • * the calculations to determine whether or not the Shape * entirely contains the rectangular area are prohibitively expensive. *
  • *
* * This means that this method might return false even though * the Shape contains the rectangular area. The * Area class can be used to perform more accurate * computations of geometric intersection for any Shape * object if a more precise answer is required. *

* * @param x the coordinates of the specified rectangular area, x value * @param y the coordinates of the specified rectangular area, y value * @param w the width of the specified rectangular area * @param h the height of the specified rectangular area * * @return true if the interior of the Shape * entirely contains the specified rectangular area; * false otherwise or, if the Shape * contains the rectangular area and the intersects * method returns true and the containment * calculations would be too expensive to perform. * * @see java.awt.geom.Area * @see #intersects */ public boolean contains(double x, double y, double w, double h) { Geometry rect = createRectangle(x, y, w, h); return geometry.contains(rect); } /** * Returns an integer {@link Rectangle} that completely encloses the * Shape. Note that there is no guarantee that the returned * Rectangle is the smallest bounding box that encloses the * Shape, only that the Shape lies entirely * within the indicated Rectangle. The returned * Rectangle might also fail to completely enclose the * Shape if the Shape overflows the limited * range of the integer data type. The getBounds2D method * generally returns a tighter bounding box due to its greater flexibility * in representation. * * @return an integer Rectangle that completely encloses the * Shape. * * @see #getBounds2D */ public Rectangle getBounds() { /** * Compute the integer bounds that will fully contain the shape */ Envelope env = geometry.getEnvelopeInternal(); int x = (int) Math.floor(env.getMinX()); int y = (int) Math.floor(env.getMinY()); int w = (int) Math.ceil(env.getMaxX()) - x; int h = (int) Math.ceil(env.getMaxY()) - y; return new Rectangle(x, y, w, h); } /** * Returns a high precision and more accurate bounding box of the * Shape than the getBounds method. Note that * there is no guarantee that the returned {@link Rectangle2D} is the * smallest bounding box that encloses the Shape, only that * the Shape lies entirely within the indicated * Rectangle2D. The bounding box returned by this method is * usually tighter than that returned by the getBounds method * and never fails due to overflow problems since the return value can be * an instance of the Rectangle2D that uses double precision * values to store the dimensions. * * @return an instance of Rectangle2D that is a high-precision * bounding box of the Shape. * * @see #getBounds */ public Rectangle2D getBounds2D() { Envelope env = geometry.getEnvelopeInternal(); return new Rectangle2D.Double(env.getMinX(), env.getMinY(), env.getWidth(), env.getHeight()); } /** * Returns an iterator object that iterates along the Shape * boundary and provides access to the geometry of the Shape * outline. If an optional {@link AffineTransform} is specified, the * coordinates returned in the iteration are transformed accordingly. * *

* Each call to this method returns a fresh PathIterator * object that traverses the geometry of the Shape object * independently from any other PathIterator objects in use * at the same time. *

* *

* It is recommended, but not guaranteed, that objects implementing the * Shape interface isolate iterations that are in process * from any changes that might occur to the original object's geometry * during such iterations. *

* *

* Before using a particular implementation of the Shape * interface in more than one thread simultaneously, refer to its * documentation to verify that it guarantees that iterations are isolated * from modifications. *

* * @param at an optional AffineTransform to be applied to the * coordinates as they are returned in the iteration, or * null if untransformed coordinates are desired * * @return a new PathIterator object, which independently * traverses the geometry of the Shape. */ public PathIterator getPathIterator(AffineTransform at) { AbstractLiteIterator pi = null; AffineTransform combined = at; // return iterator according to the kind of geometry we include if (this.geometry instanceof Point) { pi = new PointIterator((Point) geometry, combined); } if (this.geometry instanceof Polygon) { pi = new PolygonIterator((Polygon) geometry, combined); } else if (this.geometry instanceof LinearRing) { pi = new LineIterator((LinearRing) geometry, combined); } else if (this.geometry instanceof LineString) { pi = new LineIterator((LineString) geometry, combined); } else if (this.geometry instanceof GeometryCollection) { pi = new GeomCollectionIterator((GeometryCollection) geometry, combined); } return pi; } /** * Returns an iterator object that iterates along the Shape * boundary and provides access to a flattened view of the * Shape outline geometry. * *

* Only SEG_MOVETO, SEG_LINETO, and SEG_CLOSE point types are returned by * the iterator. *

* *

* If an optional AffineTransform is specified, the * coordinates returned in the iteration are transformed accordingly. *

* *

* The amount of subdivision of the curved segments is controlled by the * flatness parameter, which specifies the maximum distance * that any point on the unflattened transformed curve can deviate from * the returned flattened path segments. Note that a limit on the accuracy * of the flattened path might be silently imposed, causing very small * flattening parameters to be treated as larger values. This limit, if * there is one, is defined by the particular implementation that is used. *

* *

* Each call to this method returns a fresh PathIterator * object that traverses the Shape object geometry * independently from any other PathIterator objects in use * at the same time. *

* *

* It is recommended, but not guaranteed, that objects implementing the * Shape interface isolate iterations that are in process * from any changes that might occur to the original object's geometry * during such iterations. *

* *

* Before using a particular implementation of this interface in more than * one thread simultaneously, refer to its documentation to verify that it * guarantees that iterations are isolated from modifications. *

* * @param at an optional AffineTransform to be applied to the * coordinates as they are returned in the iteration, or * null if untransformed coordinates are desired * @param flatness the maximum distance that the line segments used to * approximate the curved segments are allowed to deviate from any * point on the original curve * * @return a new PathIterator that independently traverses the * Shape geometry. */ public PathIterator getPathIterator(AffineTransform at, double flatness) { return getPathIterator(at); } /** * Tests if the interior of the Shape intersects the interior * of a specified Rectangle2D. This method might * conservatively return true when: * *
    *
  • * there is a high probability that the Rectangle2D and the * Shape intersect, but *
  • *
  • * the calculations to accurately determine this intersection are * prohibitively expensive. *
  • *
* * This means that this method might return true even though * the Rectangle2D does not intersect the Shape. * * @param r the specified Rectangle2D * * @return true if the interior of the Shape and * the interior of the specified Rectangle2D * intersect, or are both highly likely to intersect and * intersection calculations would be too expensive to * perform; false otherwise. * * @see #intersects(double, double, double, double) */ public boolean intersects(Rectangle2D r) { Geometry rect = rectangleToGeometry(r); return geometry.intersects(rect); } /** * Tests if the interior of the Shape intersects the interior * of a specified rectangular area. The rectangular area is considered to * intersect the Shape if any point is contained in both the * interior of the Shape and the specified rectangular area. * *

* This method might conservatively return true when: * *

    *
  • * there is a high probability that the rectangular area and the * Shape intersect, but *
  • *
  • * the calculations to accurately determine this intersection are * prohibitively expensive. *
  • *
* * This means that this method might return true even though * the rectangular area does not intersect the Shape. The * {@link java.awt.geom.Area Area} class can be used to perform more * accurate computations of geometric intersection for any * Shape object if a more precise answer is required. *

* * @param x the coordinates of the specified rectangular area, x value * @param y the coordinates of the specified rectangular area, y value * @param w the width of the specified rectangular area * @param h the height of the specified rectangular area * * @return true if the interior of the Shape and * the interior of the rectangular area intersect, or are both * highly likely to intersect and intersection calculations would * be too expensive to perform; false otherwise. * * @see java.awt.geom.Area */ public boolean intersects(double x, double y, double w, double h) { Geometry rect = createRectangle(x, y, w, h); return geometry.intersects(rect); } /** * Converts the Rectangle2D passed as parameter in a jts Geometry object * * @param r the rectangle to be converted * * @return a geometry with the same vertices as the rectangle */ private Geometry rectangleToGeometry(Rectangle2D r) { return createRectangle(r.getMinX(), r.getMinY(), r.getWidth(), r.getHeight()); } /** * Creates a jts Geometry object representing a rectangle with the given * parameters * * @param x left coordinate * @param y bottom coordinate * @param w width * @param h height * * @return a rectangle with the specified position and size */ private Geometry createRectangle(double x, double y, double w, double h) { Coordinate[] coords = { new Coordinate(x, y), new Coordinate(x, y + h), new Coordinate(x + w, y + h), new Coordinate(x + w, y), new Coordinate(x, y) }; LinearRing lr = geometry.getFactory().createLinearRing(coords); return geometry.getFactory().createPolygon(lr, null); } public Geometry getGeometry() { return geometry; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy