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

com.vividsolutions.jts.geom.Geometry Maven / Gradle / Ivy

The newest version!
/*
 * The JTS Topology Suite is a collection of Java classes that
 * implement the fundamental operations required to validate a given
 * geo-spatial data set to a known topological specification.
 *
 * Copyright (C) 2001 Vivid Solutions
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * For more information, contact:
 *
 *     Vivid Solutions
 *     Suite #1A
 *     2328 Government Street
 *     Victoria BC  V8T 5G5
 *     Canada
 *
 *     (250)385-6040
 *     www.vividsolutions.com
 */
package com.vividsolutions.jts.geom;

import java.io.Serializable;
import java.util.*;

import com.vividsolutions.jts.algorithm.*;
import com.vividsolutions.jts.geom.util.*;
import com.vividsolutions.jts.io.WKTWriter;
import com.vividsolutions.jts.operation.*;
import com.vividsolutions.jts.operation.buffer.BufferOp;
import com.vividsolutions.jts.operation.distance.DistanceOp;
import com.vividsolutions.jts.operation.linemerge.LineMerger;
import com.vividsolutions.jts.operation.overlay.OverlayOp;
import com.vividsolutions.jts.operation.union.UnaryUnionOp;
import com.vividsolutions.jts.operation.overlay.snap.SnapIfNeededOverlayOp;
import com.vividsolutions.jts.operation.predicate.RectangleIntersects;
import com.vividsolutions.jts.operation.predicate.RectangleContains;
import com.vividsolutions.jts.operation.relate.RelateOp;
import com.vividsolutions.jts.operation.valid.IsValidOp;
import com.vividsolutions.jts.util.Assert;

/**
 * A representation of a planar, linear vector geometry.
 * 

* *

Binary Predicates

* Because it is not clear at this time * what semantics for spatial * analysis methods involving GeometryCollections would be useful, * GeometryCollections are not supported as arguments to binary * predicates or the relate * method. * *

Overlay Methods

* * The overlay methods * return the most specific class possible to represent the result. If the * result is homogeneous, a Point, LineString, or * Polygon will be returned if the result contains a single * element; otherwise, a MultiPoint, MultiLineString, * or MultiPolygon will be returned. If the result is * heterogeneous a GeometryCollection will be returned.

* * Because it is not clear at this time what semantics for set-theoretic * methods involving GeometryCollections would be useful, * GeometryCollections * are not supported as arguments to the set-theoretic methods. * *

Representation of Computed Geometries

* * The SFS states that the result * of a set-theoretic method is the "point-set" result of the usual * set-theoretic definition of the operation (SFS 3.2.21.1). However, there are * sometimes many ways of representing a point set as a Geometry. *

* * The SFS does not specify an unambiguous representation of a given point set * returned from a spatial analysis method. One goal of JTS is to make this * specification precise and unambiguous. JTS uses a canonical form for * Geometrys returned from overlay methods. The canonical * form is a Geometry which is simple and noded: *

    *
  • Simple means that the Geometry returned will be simple according to * the JTS definition of isSimple. *
  • Noded applies only to overlays involving LineStrings. It * means that all intersection points on LineStrings will be * present as endpoints of LineStrings in the result. *
* This definition implies that non-simple geometries which are arguments to * spatial analysis methods must be subjected to a line-dissolve process to * ensure that the results are simple. * *

Constructed Points And The Precision Model

* * The results computed by the set-theoretic methods may * contain constructed points which are not present in the input Geometry * s. These new points arise from intersections between line segments in the * edges of the input Geometrys. In the general case it is not * possible to represent constructed points exactly. This is due to the fact * that the coordinates of an intersection point may contain twice as many bits * of precision as the coordinates of the input line segments. In order to * represent these constructed points explicitly, JTS must truncate them to fit * the PrecisionModel.

* * Unfortunately, truncating coordinates moves them slightly. Line segments * which would not be coincident in the exact result may become coincident in * the truncated representation. This in turn leads to "topology collapses" -- * situations where a computed element has a lower dimension than it would in * the exact result.

* * When JTS detects topology collapses during the computation of spatial * analysis methods, it will throw an exception. If possible the exception will * report the location of the collapse.

* *

Geometry Equality

* * There are two ways of comparing geometries for equality: * structural equality and topological equality. * *

Structural Equality

* * Structural Equality is provided by the * {@link #equalsExact(Geometry)} method. * This implements a comparison based on exact, structural pointwise * equality. * The {@link #equals(Object)} is a synonym for this method, * to provide structural equality semantics for * use in Java collections. * It is important to note that structural pointwise equality * is easily affected by things like * ring order and component order. In many situations * it will be desirable to normalize geometries before * comparing them (using the {@link #norm()} * or {@link #normalize()} methods). * {@link #equalsNorm(Geometry)} is provided * as a convenience method to compute equality over * normalized geometries, but it is expensive to use. * Finally, {@link #equalsExact(Geometry, double)} * allows using a tolerance value for point comparison. * * *

Topological Equality

* * Topological Equality is provided by the * {@link #equalsTopo(Geometry)} method. * It implements the SFS definition of point-set equality * defined in terms of the DE-9IM matrix. * To support the SFS naming convention, the method * {@link #equals(Geometry)} is also provided as a synonym. * However, due to the potential for confusion with {@link #equals(Object)} * its use is discouraged. *

* Since {@link #equals(Object)} and {@link #hashCode()} are overridden, * Geometries can be used effectively in Java collections. * *@version 1.7 */ public abstract class Geometry implements Cloneable, Comparable, Serializable { private static final long serialVersionUID = 8763622679187376702L; private static final Class[] sortedClasses = new Class[] { Point.class, MultiPoint.class, LineString.class, LinearRing.class, MultiLineString.class, Polygon.class, MultiPolygon.class, GeometryCollection.class }; private final static GeometryComponentFilter geometryChangedFilter = new GeometryComponentFilter() { public void filter(Geometry geom) { geom.geometryChangedAction(); } }; /** * The bounding box of this Geometry. */ protected Envelope envelope; /** * The {@link GeometryFactory} used to create this Geometry */ protected final GeometryFactory factory; /** * The ID of the Spatial Reference System used by this Geometry */ protected int SRID; /** * An object reference which can be used to carry ancillary data defined * by the client. */ private Object userData = null; /** * Creates a new Geometry via the specified GeometryFactory. * * @param factory */ public Geometry(GeometryFactory factory) { this.factory = factory; this.SRID = factory.getSRID(); } /** * Returns the name of this Geometry's actual class. * *@return the name of this Geometrys actual class */ public abstract String getGeometryType(); /** * Returns true if the array contains any non-empty Geometrys. * *@param geometries an array of Geometrys; no elements may be * null *@return true if any of the Geometrys * isEmpty methods return false */ protected static boolean hasNonEmptyElements(Geometry[] geometries) { for (int i = 0; i < geometries.length; i++) { if (!geometries[i].isEmpty()) { return true; } } return false; } /** * Returns true if the array contains any null elements. * *@param array an array to validate *@return true if any of arrays elements are * null */ protected static boolean hasNullElements(Object[] array) { for (int i = 0; i < array.length; i++) { if (array[i] == null) { return true; } } return false; } /** * Returns the ID of the Spatial Reference System used by the Geometry. *

* * JTS supports Spatial Reference System information in the simple way * defined in the SFS. A Spatial Reference System ID (SRID) is present in * each Geometry object. Geometry provides basic * accessor operations for this field, but no others. The SRID is represented * as an integer. * *@return the ID of the coordinate space in which the Geometry * is defined. * */ public int getSRID() { return SRID; } /** * Sets the ID of the Spatial Reference System used by the Geometry. *

* NOTE: This method should only be used for exceptional circumstances or * for backwards compatibility. Normally the SRID should be set on the * {@link GeometryFactory} used to create the geometry. * SRIDs set using this method will not be propagated to * geometries returned by constructive methods. * * @see GeometryFactory */ public void setSRID(int SRID) { this.SRID = SRID; } /** * Gets the factory which contains the context in which this geometry was created. * * @return the factory for this geometry */ public GeometryFactory getFactory() { return factory; } /** * Gets the user data object for this geometry, if any. * * @return the user data object, or null if none set */ public Object getUserData() { return userData; } /** * Returns the number of {@link Geometry}s in a {@link GeometryCollection} * (or 1, if the geometry is not a collection). * * @return the number of geometries contained in this geometry */ public int getNumGeometries() { return 1; } /** * Returns an element {@link Geometry} from a {@link GeometryCollection} * (or this, if the geometry is not a collection). * * @param n the index of the geometry element * @return the n'th geometry contained in this geometry */ public Geometry getGeometryN(int n) { return this; } /** * A simple scheme for applications to add their own custom data to a Geometry. * An example use might be to add an object representing a Coordinate Reference System. *

* Note that user data objects are not present in geometries created by * construction methods. * * @param userData an object, the semantics for which are defined by the * application using this Geometry */ public void setUserData(Object userData) { this.userData = userData; } /** * Returns the PrecisionModel used by the Geometry. * *@return the specification of the grid of allowable points, for this * Geometry and all other Geometrys */ public PrecisionModel getPrecisionModel() { return factory.getPrecisionModel(); } /** * Returns a vertex of this Geometry * (usually, but not necessarily, the first one). * The returned coordinate should not be assumed * to be an actual Coordinate object used in * the internal representation. * *@return a {@link Coordinate} which is a vertex of this Geometry. *@return null if this Geometry is empty */ public abstract Coordinate getCoordinate(); /** * Returns an array containing the values of all the vertices for * this geometry. * If the geometry is a composite, the array will contain all the vertices * for the components, in the order in which the components occur in the geometry. *

* In general, the array cannot be assumed to be the actual internal * storage for the vertices. Thus modifying the array * may not modify the geometry itself. * Use the {@link CoordinateSequence#setOrdinate} method * (possibly on the components) to modify the underlying data. * If the coordinates are modified, * {@link #geometryChanged} must be called afterwards. * *@return the vertices of this Geometry *@see #geometryChanged *@see CoordinateSequence#setOrdinate */ public abstract Coordinate[] getCoordinates(); /** * Returns the count of this Geometrys vertices. The Geometry * s contained by composite Geometrys must be * Geometry's; that is, they must implement getNumPoints * *@return the number of vertices in this Geometry */ public abstract int getNumPoints(); /** * Tests whether this {@link Geometry} is simple. * The SFS definition of simplicity * follows the general rule that a Geometry is simple if it has no points of * self-tangency, self-intersection or other anomalous points. *

* Simplicity is defined for each {@link Geometry} subclass as follows: *

    *
  • Valid polygonal geometries are simple, since their rings * must not self-intersect. isSimple * tests for this condition and reports false if it is not met. * (This is a looser test than checking for validity). *
  • Linear rings have the same semantics. *
  • Linear geometries are simple iff they do not self-intersect at points * other than boundary points. *
  • Zero-dimensional geometries (points) are simple iff they have no * repeated points. *
  • Empty Geometrys are always simple. *
      * * @return true if this Geometry is simple * @see #isValid */ public boolean isSimple() { IsSimpleOp op = new IsSimpleOp(this); return op.isSimple(); } /** * Tests whether this Geometry * is topologically valid, according to the OGC SFS specification. *

      * For validity rules see the Javadoc for the specific Geometry subclass. * *@return true if this Geometry is valid * * @see IsValidOp */ public boolean isValid() { return IsValidOp.isValid(this); } /** * Tests whether the set of points covered by this Geometry is * empty. * *@return true if this Geometry does not cover any points */ public abstract boolean isEmpty(); /** * Returns the minimum distance between this Geometry * and another Geometry. * * @param g the Geometry from which to compute the distance * @return the distance between the geometries * @return 0 if either input geometry is empty * @throws IllegalArgumentException if g is null */ public double distance(Geometry g) { return DistanceOp.distance(this, g); } /** * Tests whether the distance from this Geometry * to another is less than or equal to a specified value. * * @param geom the Geometry to check the distance to * @param distance the distance value to compare * @return true if the geometries are less than distance apart. */ public boolean isWithinDistance(Geometry geom, double distance) { double envDist = getEnvelopeInternal().distance(geom.getEnvelopeInternal()); if (envDist > distance) return false; return DistanceOp.isWithinDistance(this, geom, distance); /* double geomDist = this.distance(geom); if (geomDist > distance) return false; return true; */ } public boolean isRectangle() { // Polygon overrides to check for actual rectangle return false; } /** * Returns the area of this Geometry. * Areal Geometries have a non-zero area. * They override this function to compute the area. * Others return 0.0 * *@return the area of the Geometry */ public double getArea() { return 0.0; } /** * Returns the length of this Geometry. * Linear geometries return their length. * Areal geometries return their perimeter. * They override this function to compute the area. * Others return 0.0 * *@return the length of the Geometry */ public double getLength() { return 0.0; } /** * Computes the centroid of this Geometry. * The centroid * is equal to the centroid of the set of component Geometries of highest * dimension (since the lower-dimension geometries contribute zero * "weight" to the centroid). *

      * The centroid of an empty geometry is POINT EMPTY. * * @return a {@link Point} which is the centroid of this Geometry */ public Point getCentroid() { if (isEmpty()) return factory.createPoint((Coordinate) null); Coordinate centPt = Centroid.getCentroid(this); return createPointFromInternalCoord(centPt, this); } /** * Computes an interior point of this Geometry. * An interior point is guaranteed to lie in the interior of the Geometry, * if it possible to calculate such a point exactly. Otherwise, * the point may lie on the boundary of the geometry. *

      * The interior point of an empty geometry is POINT EMPTY. * * @return a {@link Point} which is in the interior of this Geometry */ public Point getInteriorPoint() { if (isEmpty()) return factory.createPoint((Coordinate) null); Coordinate interiorPt = null; int dim = getDimension(); if (dim == 0) { InteriorPointPoint intPt = new InteriorPointPoint(this); interiorPt = intPt.getInteriorPoint(); } else if (dim == 1) { InteriorPointLine intPt = new InteriorPointLine(this); interiorPt = intPt.getInteriorPoint(); } else { InteriorPointArea intPt = new InteriorPointArea(this); interiorPt = intPt.getInteriorPoint(); } return createPointFromInternalCoord(interiorPt, this); } /** * Returns the dimension of this geometry. * The dimension of a geometry is is the topological * dimension of its embedding in the 2-D Euclidean plane. * In the JTS spatial model, dimension values are in the set {0,1,2}. *

      * Note that this is a different concept to the dimension of * the vertex {@link Coordinate}s. * The geometry dimension can never be greater than the coordinate dimension. * For example, a 0-dimensional geometry (e.g. a Point) * may have a coordinate dimension of 3 (X,Y,Z). * *@return the topological dimension of this geometry. */ public abstract int getDimension(); /** * Returns the boundary, or an empty geometry of appropriate dimension * if this Geometry is empty. * (In the case of zero-dimensional geometries, ' * an empty GeometryCollection is returned.) * For a discussion of this function, see the OpenGIS Simple * Features Specification. As stated in SFS Section 2.1.13.1, "the boundary * of a Geometry is a set of Geometries of the next lower dimension." * *@return the closure of the combinatorial boundary of this Geometry */ public abstract Geometry getBoundary(); /** * Returns the dimension of this Geometrys inherent boundary. * *@return the dimension of the boundary of the class implementing this * interface, whether or not this object is the empty geometry. Returns * Dimension.FALSE if the boundary is the empty geometry. */ public abstract int getBoundaryDimension(); /** * Gets a Geometry representing the envelope (bounding box) of * this Geometry. *

      * If this Geometry is: *

        *
      • empty, returns an empty Point. *
      • a point, returns a Point. *
      • a line parallel to an axis, a two-vertex LineString *
      • otherwise, returns a * Polygon whose vertices are (minx miny, maxx miny, * maxx maxy, minx maxy, minx miny). *
      * *@return a Geometry representing the envelope of this Geometry * * @see GeometryFactory#toGeometry(Envelope) */ public Geometry getEnvelope() { return getFactory().toGeometry(getEnvelopeInternal()); } /** * Gets an {@link Envelope} containing * the minimum and maximum x and y values in this Geometry. * If the geometry is empty, an empty Envelope * is returned. *

      * The returned object is a copy of the one maintained internally, * to avoid aliasing issues. * For best performance, clients which access this * envelope frequently should cache the return value. * *@return the envelope of this Geometry. *@return an empty Envelope if this Geometry is empty */ public Envelope getEnvelopeInternal() { if (envelope == null) { envelope = computeEnvelopeInternal(); } return new Envelope(envelope); } /** * Notifies this geometry that its coordinates have been changed by an external * party (for example, via a {@link CoordinateFilter}). * When this method is called the geometry will flush * and/or update any derived information it has cached (such as its {@link Envelope} ). * The operation is applied to all component Geometries. */ public void geometryChanged() { apply(geometryChangedFilter); } /** * Notifies this Geometry that its Coordinates have been changed by an external * party. When #geometryChanged is called, this method will be called for * this Geometry and its component Geometries. * * @see #apply(GeometryComponentFilter) */ protected void geometryChangedAction() { envelope = null; } /** * Tests whether this geometry is disjoint from the argument geometry. *

      * The disjoint predicate has the following equivalent definitions: *

        *
      • The two geometries have no point in common *
      • The DE-9IM Intersection Matrix for the two geometries matches * [FF*FF****] *
      • ! g.intersects(this) = true *
        (disjoint is the inverse of intersects) *
      * *@param g the Geometry with which to compare this Geometry *@return true if the two Geometrys are * disjoint * * @see Geometry#intersects */ public boolean disjoint(Geometry g) { return ! intersects(g); } /** * Tests whether this geometry touches the * argument geometry. *

      * The touches predicate has the following equivalent definitions: *

        *
      • The geometries have at least one point in common, * but their interiors do not intersect. *
      • The DE-9IM Intersection Matrix for the two geometries matches * at least one of the following patterns *
          *
        • [FT*******] *
        • [F**T*****] *
        • [F***T****] *
        *
      * If both geometries have dimension 0, the predicate returns false, * since points have only interiors. * This predicate is symmetric. * * *@param g the Geometry with which to compare this Geometry *@return true if the two Geometrys touch; * Returns false if both Geometrys are points */ public boolean touches(Geometry g) { // short-circuit test if (! getEnvelopeInternal().intersects(g.getEnvelopeInternal())) return false; return relate(g).isTouches(getDimension(), g.getDimension()); } /** * Tests whether this geometry intersects the argument geometry. *

      * The intersects predicate has the following equivalent definitions: *

        *
      • The two geometries have at least one point in common *
      • The DE-9IM Intersection Matrix for the two geometries matches * at least one of the patterns *
          *
        • [T********] *
        • [*T*******] *
        • [***T*****] *
        • [****T****] *
        *
      • ! g.disjoint(this) = true *
        (intersects is the inverse of disjoint) *
      * *@param g the Geometry with which to compare this Geometry *@return true if the two Geometrys intersect * * @see Geometry#disjoint */ public boolean intersects(Geometry g) { // short-circuit envelope test if (! getEnvelopeInternal().intersects(g.getEnvelopeInternal())) return false; /** * TODO: (MD) Add optimizations: * * - for P-A case: * If P is in env(A), test for point-in-poly * * - for A-A case: * If env(A1).overlaps(env(A2)) * test for overlaps via point-in-poly first (both ways) * Possibly optimize selection of point to test by finding point of A1 * closest to centre of env(A2). * (Is there a test where we shouldn't bother - e.g. if env A * is much smaller than env B, maybe there's no point in testing * pt(B) in env(A)? */ // optimization for rectangle arguments if (isRectangle()) { return RectangleIntersects.intersects((Polygon) this, g); } if (g.isRectangle()) { return RectangleIntersects.intersects((Polygon) g, this); } // general case return relate(g).isIntersects(); } /** * Tests whether this geometry crosses the * argument geometry. *

      * The crosses predicate has the following equivalent definitions: *

        *
      • The geometries have some but not all interior points in common. *
      • The DE-9IM Intersection Matrix for the two geometries matches * one of the following patterns: *
          *
        • [T*T******] (for P/L, P/A, and L/A situations) *
        • [T*****T**] (for L/P, A/P, and A/L situations) *
        • [0********] (for L/L situations) *
        *
      * For any other combination of dimensions this predicate returns false. *

      * The SFS defined this predicate only for P/L, P/A, L/L, and L/A situations. * In order to make the relation symmetric, * JTS extends the definition to apply to L/P, A/P and A/L situations as well. * *@param g the Geometry with which to compare this Geometry *@return true if the two Geometrys cross. */ public boolean crosses(Geometry g) { // short-circuit test if (! getEnvelopeInternal().intersects(g.getEnvelopeInternal())) return false; return relate(g).isCrosses(getDimension(), g.getDimension()); } /** * Tests whether this geometry is within the * specified geometry. *

      * The within predicate has the following equivalent definitions: *

        *
      • Every point of this geometry is a point of the other geometry, * and the interiors of the two geometries have at least one point in common. *
      • The DE-9IM Intersection Matrix for the two geometries matches * [T*F**F***] *
      • g.contains(this) = true *
        (within is the converse of {@link #contains}) *
      * An implication of the definition is that * "The boundary of a Geometry is not within the Geometry". * In other words, if a geometry A is a subset of * the points in the boundary of a geomtry B, A.within(B) = false * (As a concrete example, take A to be a LineString which lies in the boundary of a Polygon B.) * For a predicate with similar behaviour but avoiding * this subtle limitation, see {@link #coveredBy}. * *@param g the Geometry with which to compare this Geometry *@return true if this Geometry is within * g * * @see Geometry#contains * @see Geometry#coveredBy */ public boolean within(Geometry g) { return g.contains(this); } /** * Tests whether this geometry contains the * argument geometry. *

      * The contains predicate has the following equivalent definitions: *

        *
      • Every point of the other geometry is a point of this geometry, * and the interiors of the two geometries have at least one point in common. *
      • The DE-9IM Intersection Matrix for the two geometries matches * the pattern * [T*****FF*] *
      • g.within(this) = true *
        (contains is the converse of {@link #within} ) *
      * An implication of the definition is that "Geometries do not * contain their boundary". In other words, if a geometry A is a subset of * the points in the boundary of a geometry B, B.contains(A) = false. * (As a concrete example, take A to be a LineString which lies in the boundary of a Polygon B.) * For a predicate with similar behaviour but avoiding * this subtle limitation, see {@link #covers}. * *@param g the Geometry with which to compare this Geometry *@return true if this Geometry contains g * * @see Geometry#within * @see Geometry#covers */ public boolean contains(Geometry g) { // short-circuit test if (! getEnvelopeInternal().contains(g.getEnvelopeInternal())) return false; // optimization for rectangle arguments if (isRectangle()) { return RectangleContains.contains((Polygon) this, g); } // general case return relate(g).isContains(); } /** * Tests whether this geometry overlaps the * specified geometry. *

      * The overlaps predicate has the following equivalent definitions: *

        *
      • The geometries have at least one point each not shared by the other * (or equivalently neither covers the other), * they have the same dimension, * and the intersection of the interiors of the two geometries has * the same dimension as the geometries themselves. *
      • The DE-9IM Intersection Matrix for the two geometries matches * [T*T***T**] (for two points or two surfaces) * or [1*T***T**] (for two curves) *
      * If the geometries are of different dimension this predicate returns false. * This predicate is symmetric. * *@param g the Geometry with which to compare this Geometry *@return true if the two Geometrys overlap. */ public boolean overlaps(Geometry g) { // short-circuit test if (! getEnvelopeInternal().intersects(g.getEnvelopeInternal())) return false; return relate(g).isOverlaps(getDimension(), g.getDimension()); } /** * Tests whether this geometry covers the * argument geometry. *

      * The covers predicate has the following equivalent definitions: *

        *
      • Every point of the other geometry is a point of this geometry. *
      • The DE-9IM Intersection Matrix for the two geometries matches * at least one of the following patterns: *
          *
        • [T*****FF*] *
        • [*T****FF*] *
        • [***T**FF*] *
        • [****T*FF*] *
        *
      • g.coveredBy(this) = true *
        (covers is the converse of {@link #coveredBy}) *
      * If either geometry is empty, the value of this predicate is false. *

      * This predicate is similar to {@link #contains}, * but is more inclusive (i.e. returns true for more cases). * In particular, unlike contains it does not distinguish between * points in the boundary and in the interior of geometries. * For most situations, covers should be used in preference to contains. * As an added benefit, covers is more amenable to optimization, * and hence should be more performant. * *@param g the Geometry with which to compare this Geometry *@return true if this Geometry covers g * * @see Geometry#contains * @see Geometry#coveredBy */ public boolean covers(Geometry g) { // short-circuit test if (! getEnvelopeInternal().covers(g.getEnvelopeInternal())) return false; // optimization for rectangle arguments if (isRectangle()) { // since we have already tested that the test envelope is covered return true; } return relate(g).isCovers(); } /** * Tests whether this geometry is covered by the * argument geometry. *

      * The coveredBy predicate has the following equivalent definitions: *

        *
      • Every point of this geometry is a point of the other geometry. *
      • The DE-9IM Intersection Matrix for the two geometries matches * at least one of the following patterns: *
          *
        • [T*F**F***] *
        • [*TF**F***] *
        • [**FT*F***] *
        • [**F*TF***] *
        *
      • g.covers(this) = true *
        (coveredBy is the converse of {@link #covers}) *
      * If either geometry is empty, the value of this predicate is false. *

      * This predicate is similar to {@link #within}, * but is more inclusive (i.e. returns true for more cases). * *@param g the Geometry with which to compare this Geometry *@return true if this Geometry is covered by g * * @see Geometry#within * @see Geometry#covers */ public boolean coveredBy(Geometry g) { return g.covers(this); } /** * Tests whether the elements in the DE-9IM * {@link IntersectionMatrix} for the two Geometrys match the elements in intersectionPattern. * The pattern is a 9-character string, with symbols drawn from the following set: *

        *
      • 0 (dimension 0) *
      • 1 (dimension 1) *
      • 2 (dimension 2) *
      • T ( matches 0, 1 or 2) *
      • F ( matches FALSE) *
      • * ( matches any value) *
      * For more information on the DE-9IM, see the OpenGIS Simple Features * Specification. * *@param g the Geometry with which to compare * this Geometry *@param intersectionPattern the pattern against which to check the * intersection matrix for the two Geometrys *@return true if the DE-9IM intersection * matrix for the two Geometrys match intersectionPattern * @see IntersectionMatrix */ public boolean relate(Geometry g, String intersectionPattern) { return relate(g).matches(intersectionPattern); } /** * Returns the DE-9IM {@link IntersectionMatrix} for the two Geometrys. * *@param g the Geometry with which to compare this Geometry *@return an {@link IntersectionMatrix} describing the intersections of the interiors, * boundaries and exteriors of the two Geometrys */ public IntersectionMatrix relate(Geometry g) { checkNotGeometryCollection(this); checkNotGeometryCollection(g); return RelateOp.relate(this, g); } /** * Tests whether this geometry is * topologically equal to the argument geometry. *

      * This method is included for backward compatibility reasons. * It has been superseded by the {@link #equalsTopo(Geometry)} method, * which has been named to clearly denote its functionality. *

      * This method should NOT be confused with the method * {@link #equals(Object)}, which implements * an exact equality comparison. * *@param g the Geometry with which to compare this Geometry *@return true if the two Geometrys are topologically equal * *@see #equalsTopo(Geometry) */ public boolean equals(Geometry g) { if (g == null) return false; return equalsTopo(g); } /** * Tests whether this geometry is topologically equal to the argument geometry * as defined by the SFS equals predicate. *

      * The SFS equals predicate has the following equivalent definitions: *

        *
      • The two geometries have at least one point in common, * and no point of either geometry lies in the exterior of the other geometry. *
      • The DE-9IM Intersection Matrix for the two geometries matches * the pattern T*F**FFF* *
           * T*F
           * **F
           * FF*
           * 
        *
      * Note that this method computes topologically equality. * For structural equality, see {@link #equalsExact(Geometry)}. * *@param g the Geometry with which to compare this Geometry *@return true if the two Geometrys are topologically equal * *@see #equalsExact(Geometry) */ public boolean equalsTopo(Geometry g) { // short-circuit test if (! getEnvelopeInternal().equals(g.getEnvelopeInternal())) return false; return relate(g).isEquals(getDimension(), g.getDimension()); } /** * Tests whether this geometry is structurally and numerically equal * to a given Object. * If the argument Object is not a Geometry, * the result is false. * Otherwise, the result is computed using * {@link #equalsExact(Geometry)}. *

      * This method is provided to fulfill the Java contract * for value-based object equality. * In conjunction with {@link #hashCode()} * it provides semantics which are most useful * for using * Geometrys as keys and values in Java collections. *

      * Note that to produce the expected result the input geometries * should be in normal form. It is the caller's * responsibility to perform this where required * (using {@link Geometry#norm() * or {@link #normalize()} as appropriate). * * @param o the Object to compare * @return true if this geometry is exactly equal to the argument * * @see #equalsExact(Geometry) * @see #hashCode() * @see #norm() * @see #normalize() */ public boolean equals(Object o) { if (! (o instanceof Geometry)) return false; Geometry g = (Geometry) o; return equalsExact(g); } /** * Gets a hash code for the Geometry. * * @return an integer value suitable for use as a hashcode */ public int hashCode() { return getEnvelopeInternal().hashCode(); } public String toString() { return toText(); } /** * Returns the Well-known Text representation of this Geometry. * For a definition of the Well-known Text format, see the OpenGIS Simple * Features Specification. * *@return the Well-known Text representation of this Geometry */ public String toText() { WKTWriter writer = new WKTWriter(); return writer.write(this); } /** * Computes a buffer area around this geometry having the given width. The * buffer of a Geometry is the Minkowski sum or difference of the geometry * with a disc of radius abs(distance). *

      * Mathematically-exact buffer area boundaries can contain circular arcs. * To represent these arcs using linear geometry they must be approximated with line segments. * The buffer geometry is constructed using 8 segments per quadrant to approximate * the circular arcs. * The end cap style is CAP_ROUND. *

      * The buffer operation always returns a polygonal result. The negative or * zero-distance buffer of lines and points is always an empty {@link Polygon}. * This is also the result for the buffers of degenerate (zero-area) polygons. * * @param distance * the width of the buffer (may be positive, negative or 0) * @return a polygonal geometry representing the buffer region (which may be * empty) * * @throws TopologyException * if a robustness error occurs * * @see #buffer(double, int) * @see #buffer(double, int, int) */ public Geometry buffer(double distance) { return BufferOp.bufferOp(this, distance); } /** * Computes a buffer area around this geometry having the given width and with * a specified accuracy of approximation for circular arcs. *

      * Mathematically-exact buffer area boundaries can contain circular arcs. * To represent these arcs * using linear geometry they must be approximated with line segments. The * quadrantSegments argument allows controlling the accuracy of * the approximation by specifying the number of line segments used to * represent a quadrant of a circle *

      * The buffer operation always returns a polygonal result. The negative or * zero-distance buffer of lines and points is always an empty {@link Polygon}. * This is also the result for the buffers of degenerate (zero-area) polygons. * * @param distance * the width of the buffer (may be positive, negative or 0) * @param quadrantSegments * the number of line segments used to represent a quadrant of a * circle * @return a polygonal geometry representing the buffer region (which may be * empty) * * @throws TopologyException * if a robustness error occurs * * @see #buffer(double) * @see #buffer(double, int, int) */ public Geometry buffer(double distance, int quadrantSegments) { return BufferOp.bufferOp(this, distance, quadrantSegments); } /** * Computes a buffer area around this geometry having the given * width and with a specified accuracy of approximation for circular arcs, * and using a specified end cap style. *

      * Mathematically-exact buffer area boundaries can contain circular arcs. * To represent these arcs using linear geometry they must be approximated with line segments. * The quadrantSegments argument allows controlling the * accuracy of the approximation * by specifying the number of line segments used to represent a quadrant of a circle *

      * The end cap style specifies the buffer geometry that will be * created at the ends of linestrings. The styles provided are: *

        *
      • BufferOp.CAP_ROUND - (default) a semi-circle *
      • BufferOp.CAP_BUTT - a straight line perpendicular to the end segment *
      • BufferOp.CAP_SQUARE - a half-square *
      *

      * The buffer operation always returns a polygonal result. The negative or * zero-distance buffer of lines and points is always an empty {@link Polygon}. * This is also the result for the buffers of degenerate (zero-area) polygons. * *@param distance the width of the buffer (may be positive, negative or 0) *@param quadrantSegments the number of line segments used to represent a quadrant of a circle *@param endCapStyle the end cap style to use *@return a polygonal geometry representing the buffer region (which may be empty) * * @throws TopologyException if a robustness error occurs * * @see #buffer(double) * @see #buffer(double, int) * @see BufferOp */ public Geometry buffer(double distance, int quadrantSegments, int endCapStyle) { return BufferOp.bufferOp(this, distance, quadrantSegments, endCapStyle); } /** * Computes the smallest convex Polygon that contains all the * points in the Geometry. This obviously applies only to Geometry * s which contain 3 or more points; the results for degenerate cases are * specified as follows: *

      * * * * * * * * * * * * * * * * * * * *
      Number of Points in argument Geometry Geometry class of result
      0 empty GeometryCollection
      1 Point
      2 LineString
      3 or more Polygon
      * *@return the minimum-area convex polygon containing this Geometry' * s points */ public Geometry convexHull() { return (new ConvexHull(this)).getConvexHull(); } /** * Computes a new geometry which has all component coordinate sequences * in reverse order (opposite orientation) to this one. * * @return a reversed geometry */ public abstract Geometry reverse(); /** * Computes a Geometry representing the point-set which is * common to both this Geometry and the other Geometry. *

      * The intersection of two geometries of different dimension produces a result * geometry of dimension less than or equal to the minimum dimension of the input * geometries. * The result geometry may be a heterogenous {@link GeometryCollection}. * If the result is empty, it is an atomic geometry * with the dimension of the lowest input dimension. *

      * Intersection of {@link GeometryCollection}s is supported * only for homogeneous collection types. *

      * Non-empty heterogeneous {@link GeometryCollection} arguments are not supported. * * @param other the Geometry with which to compute the intersection * @return a Geometry representing the point-set common to the two Geometrys * @throws TopologyException if a robustness error occurs * @throws IllegalArgumentException if the argument is a non-empty heterogeneous GeometryCollection */ public Geometry intersection(Geometry other) { /** * TODO: MD - add optimization for P-A case using Point-In-Polygon */ // special case: if one input is empty ==> empty if (this.isEmpty() || other.isEmpty()) return OverlayOp.createEmptyResult(OverlayOp.INTERSECTION, this, other, factory); // compute for GCs if (this.isGeometryCollection()) { final Geometry g2 = other; return GeometryCollectionMapper.map( (GeometryCollection) this, new GeometryMapper.MapOp() { public Geometry map(Geometry g) { return g.intersection(g2); } }); } // if (isGeometryCollection(other)) // return other.intersection(this); checkNotGeometryCollection(this); checkNotGeometryCollection(other); return SnapIfNeededOverlayOp.overlayOp(this, other, OverlayOp.INTERSECTION); } /** * Computes a Geometry representing the point-set * which is contained in both this * Geometry and the other Geometry. *

      * The union of two geometries of different dimension produces a result * geometry of dimension equal to the maximum dimension of the input * geometries. * The result geometry may be a heterogenous * {@link GeometryCollection}. * If the result is empty, it is an atomic geometry * with the dimension of the highest input dimension. *

      * Unioning {@link LineString}s has the effect of * noding and dissolving the input linework. In this context * "noding" means that there will be a node or endpoint in the result for * every endpoint or line segment crossing in the input. "Dissolving" means * that any duplicate (i.e. coincident) line segments or portions of line * segments will be reduced to a single line segment in the result. * If merged linework is required, the {@link LineMerger} * class can be used. *

      * Non-empty {@link GeometryCollection} arguments are not supported. * * @param other * the Geometry with which to compute the union * @return a point-set combining the points of this Geometry and the * points of other * @throws TopologyException * if a robustness error occurs * @throws IllegalArgumentException * if either input is a non-empty GeometryCollection * @see LineMerger */ public Geometry union(Geometry other) { // handle empty geometry cases if (this.isEmpty() || other.isEmpty()) { if (this.isEmpty() && other.isEmpty()) return OverlayOp.createEmptyResult(OverlayOp.UNION, this, other, factory); // special case: if either input is empty ==> other input if (this.isEmpty()) return (Geometry) other.clone(); if (other.isEmpty()) return (Geometry) clone(); } // TODO: optimize if envelopes of geometries do not intersect checkNotGeometryCollection(this); checkNotGeometryCollection(other); return SnapIfNeededOverlayOp.overlayOp(this, other, OverlayOp.UNION); } /** * Computes a Geometry representing the closure of the point-set * of the points contained in this Geometry that are not contained in * the other Geometry. *

      * If the result is empty, it is an atomic geometry * with the dimension of the left-hand input. *

      * Non-empty {@link GeometryCollection} arguments are not supported. * *@param other the Geometry with which to compute the * difference *@return a Geometry representing the point-set difference of this Geometry with * other * @throws TopologyException if a robustness error occurs * @throws IllegalArgumentException if either input is a non-empty GeometryCollection */ public Geometry difference(Geometry other) { // special case: if A.isEmpty ==> empty; if B.isEmpty ==> A if (this.isEmpty()) return OverlayOp.createEmptyResult(OverlayOp.DIFFERENCE, this, other, factory); if (other.isEmpty()) return (Geometry) clone(); checkNotGeometryCollection(this); checkNotGeometryCollection(other); return SnapIfNeededOverlayOp.overlayOp(this, other, OverlayOp.DIFFERENCE); } /** * Computes a Geometry representing the closure of the point-set * which is the union of the points in this Geometry which are not * contained in the other Geometry, * with the points in the other Geometry not contained in this * Geometry. * If the result is empty, it is an atomic geometry * with the dimension of the highest input dimension. *

      * Non-empty {@link GeometryCollection} arguments are not supported. * *@param other the Geometry with which to compute the symmetric * difference *@return a Geometry representing the point-set symmetric difference of this Geometry * with other * @throws TopologyException if a robustness error occurs * @throws IllegalArgumentException if either input is a non-empty GeometryCollection */ public Geometry symDifference(Geometry other) { // handle empty geometry cases if (this.isEmpty() || other.isEmpty()) { // both empty - check dimensions if (this.isEmpty() && other.isEmpty()) return OverlayOp.createEmptyResult(OverlayOp.SYMDIFFERENCE, this, other, factory); // special case: if either input is empty ==> result = other arg if (this.isEmpty()) return (Geometry) other.clone(); if (other.isEmpty()) return (Geometry) clone(); } checkNotGeometryCollection(this); checkNotGeometryCollection(other); return SnapIfNeededOverlayOp.overlayOp(this, other, OverlayOp.SYMDIFFERENCE); } /** * Computes the union of all the elements of this geometry. *

      * This method supports * {@link GeometryCollection}s * (which the other overlay operations currently do not). *

      * The result obeys the following contract: *

        *
      • Unioning a set of {@link LineString}s has the effect of fully noding * and dissolving the linework. *
      • Unioning a set of {@link Polygon}s always * returns a {@link Polygonal} geometry (unlike {@link #union(Geometry)}, * which may return geometries of lower dimension if a topology collapse occurred). *
      * * @return the union geometry * @throws TopologyException if a robustness error occurs * * @see UnaryUnionOp */ public Geometry union() { return UnaryUnionOp.union(this); } /** * Returns true if the two Geometrys are exactly equal, * up to a specified distance tolerance. * Two Geometries are exactly equal within a distance tolerance * if and only if: *
        *
      • they have the same structure *
      • they have the same values for their vertices, * within the given tolerance distance, in exactly the same order. *
      * This method does not * test the values of the GeometryFactory, the SRID, * or the userData fields. *

      * To properly test equality between different geometries, * it is usually necessary to {@link #normalize()} them first. * * @param other the Geometry with which to compare this Geometry * @param tolerance distance at or below which two Coordinates * are considered equal * @return true if this and the other Geometry * have identical structure and point values, up to the distance tolerance. * * @see #equalsExact(Geometry) * @see #normalize() * @see #norm() */ public abstract boolean equalsExact(Geometry other, double tolerance); /** * Returns true if the two Geometrys are exactly equal. * Two Geometries are exactly equal iff: *

        *
      • they have the same structure *
      • they have the same values for their vertices, * in exactly the same order. *
      * This provides a stricter test of equality than * {@link #equalsTopo(Geometry)}, which is more useful * in certain situations * (such as using geometries as keys in collections). *

      * This method does not * test the values of the GeometryFactory, the SRID, * or the userData fields. *

      * To properly test equality between different geometries, * it is usually necessary to {@link #normalize()} them first. * *@param other the Geometry with which to compare this Geometry *@return true if this and the other Geometry * have identical structure and point values. * * @see #equalsExact(Geometry, double) * @see #normalize() * @see #norm() */ public boolean equalsExact(Geometry other) { return this == other || equalsExact(other, 0); } /** * Tests whether two geometries are exactly equal * in their normalized forms. * This is a convenience method which creates normalized * versions of both geometries before computing * {@link #equalsExact(Geometry)}. *

      * This method is relatively expensive to compute. * For maximum performance, the client * should instead perform normalization on the individual geometries * at an appropriate point during processing. * * @param g a Geometry * @return true if the input geometries are exactly equal in their normalized form */ public boolean equalsNorm(Geometry g) { if (g == null) return false; return norm().equalsExact(g.norm()); } /** * Performs an operation with or on this Geometry's * coordinates. * If this method modifies any coordinate values, * {@link #geometryChanged} must be called to update the geometry state. * Note that you cannot use this method to * modify this Geometry if its underlying CoordinateSequence's #get method * returns a copy of the Coordinate, rather than the actual Coordinate stored * (if it even stores Coordinate objects at all). * *@param filter the filter to apply to this Geometry's * coordinates */ public abstract void apply(CoordinateFilter filter); /** * Performs an operation on the coordinates in this Geometry's * {@link CoordinateSequence}s. * If the filter reports that a coordinate value has been changed, * {@link #geometryChanged} will be called automatically. * *@param filter the filter to apply */ public abstract void apply(CoordinateSequenceFilter filter); /** * Performs an operation with or on this Geometry and its * subelement Geometrys (if any). * Only GeometryCollections and subclasses * have subelement Geometry's. * *@param filter the filter to apply to this Geometry (and * its children, if it is a GeometryCollection). */ public abstract void apply(GeometryFilter filter); /** * Performs an operation with or on this Geometry and its * component Geometry's. Only GeometryCollections and * Polygons have component Geometry's; for Polygons they are the LinearRings * of the shell and holes. * *@param filter the filter to apply to this Geometry. */ public abstract void apply(GeometryComponentFilter filter); /** * Creates and returns a full copy of this {@link Geometry} object * (including all coordinates contained by it). * Subclasses are responsible for overriding this method and copying * their internal data. Overrides should call this method first. * * @return a clone of this instance */ public Object clone() { try { Geometry clone = (Geometry) super.clone(); if (clone.envelope != null) { clone.envelope = new Envelope(clone.envelope); } return clone; } catch (CloneNotSupportedException e) { Assert.shouldNeverReachHere(); return null; } } /** * Converts this Geometry to normal form (or * canonical form ). Normal form is a unique representation for Geometry * s. It can be used to test whether two Geometrys are equal * in a way that is independent of the ordering of the coordinates within * them. Normal form equality is a stronger condition than topological * equality, but weaker than pointwise equality. The definitions for normal * form use the standard lexicographical ordering for coordinates. "Sorted in * order of coordinates" means the obvious extension of this ordering to * sequences of coordinates. *

      * NOTE that this method mutates the value of this geometry in-place. * If this is not safe and/or wanted, the geometry should be * cloned prior to normalization. */ public abstract void normalize(); /** * Creates a new Geometry which is a normalized * copy of this Geometry. * * @return a normalized copy of this geometry. * @see #normalize() */ public Geometry norm() { Geometry copy = (Geometry) clone(); copy.normalize(); return copy; } /** * Returns whether this Geometry is greater than, equal to, * or less than another Geometry.

      * * If their classes are different, they are compared using the following * ordering: *

        *
      • Point (lowest) *
      • MultiPoint *
      • LineString *
      • LinearRing *
      • MultiLineString *
      • Polygon *
      • MultiPolygon *
      • GeometryCollection (highest) *
      * If the two Geometrys have the same class, their first * elements are compared. If those are the same, the second elements are * compared, etc. * *@param o a Geometry with which to compare this Geometry *@return a positive number, 0, or a negative number, depending on whether * this object is greater than, equal to, or less than o, as * defined in "Normal Form For Geometry" in the JTS Technical * Specifications */ public int compareTo(Object o) { Geometry other = (Geometry) o; if (getClassSortIndex() != other.getClassSortIndex()) { return getClassSortIndex() - other.getClassSortIndex(); } if (isEmpty() && other.isEmpty()) { return 0; } if (isEmpty()) { return -1; } if (other.isEmpty()) { return 1; } return compareToSameClass(o); } /** * Returns whether this Geometry is greater than, equal to, * or less than another Geometry, * using the given {@link CoordinateSequenceComparator}. *

      * * If their classes are different, they are compared using the following * ordering: *

        *
      • Point (lowest) *
      • MultiPoint *
      • LineString *
      • LinearRing *
      • MultiLineString *
      • Polygon *
      • MultiPolygon *
      • GeometryCollection (highest) *
      * If the two Geometrys have the same class, their first * elements are compared. If those are the same, the second elements are * compared, etc. * *@param o a Geometry with which to compare this Geometry *@param comp a CoordinateSequenceComparator * *@return a positive number, 0, or a negative number, depending on whether * this object is greater than, equal to, or less than o, as * defined in "Normal Form For Geometry" in the JTS Technical * Specifications */ public int compareTo(Object o, CoordinateSequenceComparator comp) { Geometry other = (Geometry) o; if (getClassSortIndex() != other.getClassSortIndex()) { return getClassSortIndex() - other.getClassSortIndex(); } if (isEmpty() && other.isEmpty()) { return 0; } if (isEmpty()) { return -1; } if (other.isEmpty()) { return 1; } return compareToSameClass(o, comp); } /** * Returns whether the two Geometrys are equal, from the point * of view of the equalsExact method. Called by equalsExact * . In general, two Geometry classes are considered to be * "equivalent" only if they are the same class. An exception is LineString * , which is considered to be equivalent to its subclasses. * *@param other the Geometry with which to compare this Geometry * for equality *@return true if the classes of the two Geometry * s are considered to be equal by the equalsExact method. */ protected boolean isEquivalentClass(Geometry other) { return this.getClass().getName().equals(other.getClass().getName()); } /** * Throws an exception if g's class is GeometryCollection * . (Its subclasses do not trigger an exception). * *@param g the Geometry to check *@throws IllegalArgumentException if g is a GeometryCollection * but not one of its subclasses */ protected void checkNotGeometryCollection(Geometry g) { //Don't use instanceof because we want to allow subclasses if (g.getClass().getName().equals("com.vividsolutions.jts.geom.GeometryCollection")) { throw new IllegalArgumentException("This method does not support GeometryCollection arguments"); } } /** * Tests whether this is an instance of a general {@link GeometryCollection}, * rather than a homogeneous subclass. * * @return true if this is a hetereogeneous GeometryCollection */ protected boolean isGeometryCollection() { return getClass().equals(com.vividsolutions.jts.geom.GeometryCollection.class); } /** * Returns the minimum and maximum x and y values in this Geometry * , or a null Envelope if this Geometry is empty. * Unlike getEnvelopeInternal, this method calculates the Envelope * each time it is called; getEnvelopeInternal caches the result * of this method. * *@return this Geometrys bounding box; if the Geometry * is empty, Envelope#isNull will return true */ protected abstract Envelope computeEnvelopeInternal(); /** * Returns whether this Geometry is greater than, equal to, * or less than another Geometry having the same class. * *@param o a Geometry having the same class as this Geometry *@return a positive number, 0, or a negative number, depending on whether * this object is greater than, equal to, or less than o, as * defined in "Normal Form For Geometry" in the JTS Technical * Specifications */ protected abstract int compareToSameClass(Object o); /** * Returns whether this Geometry is greater than, equal to, * or less than another Geometry of the same class. * using the given {@link CoordinateSequenceComparator}. * *@param o a Geometry having the same class as this Geometry *@param comp a CoordinateSequenceComparator *@return a positive number, 0, or a negative number, depending on whether * this object is greater than, equal to, or less than o, as * defined in "Normal Form For Geometry" in the JTS Technical * Specifications */ protected abstract int compareToSameClass(Object o, CoordinateSequenceComparator comp); /** * Returns the first non-zero result of compareTo encountered as * the two Collections are iterated over. If, by the time one of * the iterations is complete, no non-zero result has been encountered, * returns 0 if the other iteration is also complete. If b * completes before a, a positive number is returned; if a * before b, a negative number. * *@param a a Collection of Comparables *@param b a Collection of Comparables *@return the first non-zero compareTo result, if any; * otherwise, zero */ protected int compare(Collection a, Collection b) { Iterator i = a.iterator(); Iterator j = b.iterator(); while (i.hasNext() && j.hasNext()) { Comparable aElement = (Comparable) i.next(); Comparable bElement = (Comparable) j.next(); int comparison = aElement.compareTo(bElement); if (comparison != 0) { return comparison; } } if (i.hasNext()) { return 1; } if (j.hasNext()) { return -1; } return 0; } protected boolean equal(Coordinate a, Coordinate b, double tolerance) { if (tolerance == 0) { return a.equals(b); } return a.distance(b) <= tolerance; } private int getClassSortIndex() { for (int i = 0; i < sortedClasses.length; i++) { if (sortedClasses[i].isInstance(this)) return i; } Assert.shouldNeverReachHere("Class not supported: " + this.getClass()); return -1; } private Point createPointFromInternalCoord(Coordinate coord, Geometry exemplar) { exemplar.getPrecisionModel().makePrecise(coord); return exemplar.getFactory().createPoint(coord); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy