/*
* 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.Collection;
import java.util.Iterator;
//import com.vividsolutions.jts.algorithm.*;
//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.overlay.OverlayOp;
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;
/**
* The base class for all geometric objects.
* <P>
*
* <H3>Binary Predicates</H3>
* Because it is not clear at this time what semantics for spatial analysis
* methods involving <code>GeometryCollection</code>s would be useful,
* <code>GeometryCollection</code>s are not supported as arguments to binary
* predicates (other than <code>convexHull</code>) or the <code>relate</code>
* method.
*
* <H3>Set-Theoretic Methods</H3>
*
* The spatial analysis methods will return the most specific class possible to
* represent the result. If the result is homogeneous, a <code>Point</code>,
* <code>LineString</code>, or <code>Polygon</code> will be returned if the
* result contains a single element; otherwise, a <code>MultiPoint</code>,
* <code>MultiLineString</code>, or <code>MultiPolygon</code> will be returned.
* If the result is heterogeneous a <code>GeometryCollection</code> will be
* returned.
* <P>
*
* Because it is not clear at this time what semantics for set-theoretic methods
* involving <code>GeometryCollection</code>s would be useful,
* <code>GeometryCollections</code> are not supported as arguments to the
* set-theoretic methods.
*
* <H4>Representation of Computed Geometries</H4>
*
* 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
* <code>Geometry</code>.
* <P>
*
* 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 will use a canonical form for
* <code>Geometry</code>s returned from spatial analysis methods. The canonical
* form is a <code>Geometry</code> which is simple and noded:
* <UL>
* <LI>Simple means that the Geometry returned will be simple according to the
* JTS definition of <code>isSimple</code>.
* <LI>Noded applies only to overlays involving <code>LineString</code>s. It
* means that all intersection points on <code>LineString</code>s will be
* present as endpoints of <code>LineString</code>s in the result.
* </UL>
* 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.
*
* <H4>Constructed Points And The Precision Model</H4>
*
* The results computed by the set-theoretic methods may contain constructed
* points which are not present in the input <code>Geometry</code> s. These new
* points arise from intersections between line segments in the edges of the
* input <code>Geometry</code>s. 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 <code>PrecisionModel</code>.
* <P>
*
* 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.
* <P>
*
* 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.
* <P>
*
* #equals(Object) and #hashCode are not overridden, so that when two
* topologically equal Geometries are added to HashMaps and HashSets, they
* remain distinct. This behaviour is desired in many cases.
*
*@version 1.7
*/
public abstract class Geometry implements Cloneable, Comparable, Serializable {
private static final long serialVersionUID = 8763622679187376702L;
private final static 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 <code>Geometry</code>.
*/
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 <code>Geometry</code>
*/
protected int SRID;
/**
* Creates a new <tt>Geometry</tt> via the specified GeometryFactory.
*
* @param factory
*/
public Geometry(GeometryFactory factory) {
this.factory = factory;
this.SRID = factory.getSRID();
}
/**
* Returns the name of this object's <code>com.vivid.jts.geom</code>
* interface.
*
*@return the name of this <code>Geometry</code>s most specific
* <code>com.vividsolutions.jts.geom</code> interface
*/
public abstract String getGeometryType();
/**
* Returns true if the array contains any non-empty <code>Geometry</code>s.
*
*@param geometries
* an array of <code>Geometry</code>s; no elements may be
* <code>null</code>
*@return <code>true</code> if any of the <code>Geometry</code>s
* <code>isEmpty</code> methods return <code>false</code>
*/
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 <code>null</code> elements.
*
*@param array
* an array to validate
*@return <code>true</code> if any of <code>array</code>s elements are
* <code>null</code>
*/
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
* <code>Geometry</code>.
* <P>
*
* JTS supports Spatial Reference System information in the simple way
* defined in the SFS. A Spatial Reference System ID (SRID) is present in
* each <code>Geometry</code> object. <code>Geometry</code> 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 <code>Geometry</code>
* is defined.
*
*/
public int getSRID() {
return SRID;
}
/**
* Sets the ID of the Spatial Reference System used by the
* <code>Geometry</code>.
*/
public void setSRID(int SRID) {
this.SRID = SRID;
}
private Object userData = null;
/**
* 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 <code>null</code> 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
* <code>this</code>, 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.
* <p>
* 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 <code>PrecisionModel</code> used by the <code>Geometry</code>
* .
*
*@return the specification of the grid of allowable points, for this
* <code>Geometry</code> and all other <code>Geometry</code>s
*/
public PrecisionModel getPrecisionModel() {
return factory.getPrecisionModel();
}
/**
* Returns a vertex of this <code>Geometry</code>.
*
*@return a {@link Coordinate} which is a vertex of this
* <code>Geometry</code>. Returns <code>null</code> if this Geometry
* is empty
*/
public abstract Coordinate getCoordinate();
/**
* Returns this <code>Geometry</code> s vertices. If you modify the
* coordinates in this array, be sure to call #geometryChanged afterwards.
* The <code>Geometry</code>s contained by composite <code>Geometry</code>s
* must be Geometry's; that is, they must implement
* <code>getCoordinates</code>.
*
*@return the vertices of this <code>Geometry</code>
*/
public abstract Coordinate[] getCoordinates();
/**
* Returns the count of this <code>Geometry</code>s vertices. The
* <code>Geometry</code> s contained by composite <code>Geometry</code>s
* must be Geometry's; that is, they must implement
* <code>getNumPoints</code>
*
*@return the number of vertices in this <code>Geometry</code>
*/
public abstract int getNumPoints();
/**
* Tests whether this {@link Geometry} is simple. In general, the SFS
* specification of simplicity follows the rule:
* <UL>
* <LI>A Geometry is simple iff the only self-intersections are at boundary
* points.
* </UL>
* Simplicity is defined for each {@link Geometry} subclass as follows:
* <ul>
* <li>Valid polygonal geometries are simple by definition, so
* <code>isSimple</code> trivially returns true.
* <li>Linear geometries are simple iff they do not self-intersect at points
* other than boundary points.
* <li>Zero-dimensional geometries (points) are simple iff they have no
* repeated points.
* <li>Empty <code>Geometry</code>s are always simple
* <ul>
*
* @return <code>true</code> if this <code>Geometry</code> has any points of
* self-tangency, self-intersection or other anomalous points
* @see #isValid
*/
/*
* public boolean isSimple() { checkNotGeometryCollection(this); IsSimpleOp
* op = new IsSimpleOp(this); return op.isSimple(); }
*/
/**
* Tests the validity of this <code>Geometry</code>. Subclasses provide
* their own definition of "valid".
*
*@return <code>true</code> if this <code>Geometry</code> is valid
*
* @see IsValidOp
*/
/*
* public boolean isValid() { IsValidOp isValidOp = new IsValidOp(this);
* return isValidOp.isValid(); }
*/
/**
* Returns whether or not the set of points in this <code>Geometry</code> is
* empty.
*
*@return <code>true</code> if this <code>Geometry</code> equals the empty
* geometry
*/
public abstract boolean isEmpty();
/**
* Returns the minimum distance between this <code>Geometry</code> and the
* <code>Geometry</code> g
*
*@param g
* the <code>Geometry</code> from which to compute the distance
*/
/*
* public double distance(Geometry g) { return DistanceOp.distance(this, g);
* }
*/
/**
* Tests whether the distance from this <code>Geometry</code> 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 <code>true</code> if the geometries are less than
* <code>distance</code> 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 <code>Geometry</code>. 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 <code>Geometry</code>. 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 <code>Geometry</code>. 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)
*
* @return a {@link Point} which is the centroid of this Geometry
*/
/*
* public Point getCentroid() { if (isEmpty()) { return null; } Coordinate
* centPt = null; int dim = getDimension(); if (dim == 0) { CentroidPoint
* cent = new CentroidPoint(); cent.add(this); centPt = cent.getCentroid();
* } else if (dim == 1) { CentroidLine cent = new CentroidLine();
* cent.add(this); centPt = cent.getCentroid(); } else { CentroidArea cent =
* new CentroidArea(); cent.add(this); centPt = cent.getCentroid(); } return
* createPointFromInternalCoord(centPt, this);
*
* }
*/
/**
* Computes an interior point of this <code>Geometry</code>. 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.
*
* @return a {@link Point} which is in the interior of this Geometry
*/
/*
* public Point getInteriorPoint() { 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 <code>Geometry</code>.
*
*@return the dimension of the class implementing this interface, whether
* or not this object is the empty geometry
*/
public abstract int getDimension();
/**
* Returns the boundary, or an empty geometry of appropriate dimension if
* this <code>Geometry</code> 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
* <code>Geometry</code>
*/
public abstract Geometry getBoundary();
/**
* Returns the dimension of this <code>Geometry</code>s inherent boundary.
*
*@return the dimension of the boundary of the class implementing this
* interface, whether or not this object is the empty geometry.
* Returns <code>Dimension.FALSE</code> if the boundary is the empty
* geometry.
*/
public abstract int getBoundaryDimension();
/**
* Returns this <code>Geometry</code>s bounding box. If this
* <code>Geometry</code> is the empty geometry, returns an empty
* <code>Point</code>. If the <code>Geometry</code> is a point, returns a
* non-empty <code>Point</code>. Otherwise, returns a <code>Polygon</code>
* whose points are (minx, miny), (maxx, miny), (maxx, maxy), (minx, maxy),
* (minx, miny).
*
*@return an empty <code>Point</code> (for empty <code>Geometry</code>s), a
* <code>Point</code> (for <code>Point</code>s) or a
* <code>Polygon</code> (in all other cases)
*/
public Geometry getEnvelope() {
return getFactory().toGeometry(getEnvelopeInternal());
}
/**
* Returns the minimum and maximum x and y values in this
* <code>Geometry</code> , or a null <code>Envelope</code> if this
* <code>Geometry</code> is empty.
*
*@return this <code>Geometry</code>s bounding box; if the
* <code>Geometry</code> is empty, <code>Envelope#isNull</code> will
* return <code>true</code>
*/
public Envelope getEnvelopeInternal() {
if (envelope == null) {
envelope = computeEnvelopeInternal();
}
return envelope;
}
/**
* Notifies this Geometry that its Coordinates have been changed by an
* external party (using a CoordinateFilter, for example). The Geometry will
* flush and/or update any information it has cached (such as its
* {@link Envelope} ).
*/
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;
}
/**
* Returns <code>true</code> if this geometry is disjoint to the specified
* geometry.
* <p>
* The <code>disjoint</code> predicate has the following equivalent
* definitions:
* <ul>
* <li>The two geometries have no point in common
* <li>The DE-9IM Intersection Matrix for the two geometries is FF*FF****
* <li>! <code>g.intersects(this)</code> (<code>disjoint</code> is the
* inverse of <code>intersects</code>)
* </ul>
*
*@param g
* the <code>Geometry</code> with which to compare this
* <code>Geometry</code>
*@return <code>true</code> if the two <code>Geometry</code>s are disjoint
*
* @see Geometry#intersects
*/
/*
* public boolean disjoint(Geometry g) { return ! intersects(g); }
*/
/**
* Returns <code>true</code> if this geometry touches the specified
* geometry.
* <p>
* The <code>touches</code> predicate has the following equivalent
* definitions:
* <ul>
* <li>The geometries have at least one point in common, but their interiors
* do not intersect.
* <li>The DE-9IM Intersection Matrix for the two geometries is FT*******,
* F**T***** or F***T****
* </ul>
* If both geometries have dimension 0, this predicate returns
* <code>false</code>
*
*@param g
* the <code>Geometry</code> with which to compare this
* <code>Geometry</code>
*@return <code>true</code> if the two <code>Geometry</code>s touch;
* Returns <code>false</code> if both <code>Geometry</code>s are
* points
*/
/*
* public boolean touches(Geometry g) { // short-circuit test if (!
* getEnvelopeInternal().intersects(g.getEnvelopeInternal())) return false;
* return relate(g).isTouches(getDimension(), g.getDimension()); }
*/
/**
* Returns <code>true</code> if this geometry intersects the specified
* geometry.
* <p>
* The <code>intersects</code> predicate has the following equivalent
* definitions:
* <ul>
* <li>The two geometries have at least one point in common
* <li>! <code>g.disjoint(this)</code> (<code>intersects</code> is the
* inverse of <code>disjoint</code>)
* </ul>
*
*@param g
* the <code>Geometry</code> with which to compare this
* <code>Geometry</code>
*@return <code>true</code> if the two <code>Geometry</code>s 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(); }
*/
/**
* Returns <code>true</code> if this geometry crosses the specified
* geometry.
* <p>
* The <code>crosses</code> predicate has the following equivalent
* definitions:
* <ul>
* <li>The geometries have some but not all interior points in common.
* <li>The DE-9IM Intersection Matrix for the two geometries is
* <ul>
* <li>T*T****** (for P/L, P/A, and L/A situations)
* <li>T*****T** (for L/P, L/A, and A/L situations)
* <li>0******** (for L/L situations)
* </ul>
* </ul>
* For any other combination of dimensions this predicate returns
* <code>false</code>.
* <p>
* The SFS defined this predicate only for P/L, P/A, L/L, and L/A
* situations. JTS extends the definition to apply to L/P, A/P and A/L
* situations as well. This makes the relation symmetric.
*
*@param g
* the <code>Geometry</code> with which to compare this
* <code>Geometry</code>
*@return <code>true</code> if the two <code>Geometry</code>s cross.
*/
/*
* public boolean crosses(Geometry g) { // short-circuit test if (!
* getEnvelopeInternal().intersects(g.getEnvelopeInternal())) return false;
* return relate(g).isCrosses(getDimension(), g.getDimension()); }
*/
/**
* Returns <code>true</code> if this geometry is within the specified
* geometry.
* <p>
* The <code>within</code> predicate has the following equivalent
* definitions:
* <ul>
* <li>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.
* <li>The DE-9IM Intersection Matrix for the two geometries is T*F**F***
* <li><code>g.contains(this)</code> (<code>within</code> is the inverse of
* <code>contains</code>)
* </ul>
* An implication of the definition is that
* "The boundary of a Polygon is not within the Polygon". In other words, if
* a geometry G is a subset of the points in the boundary of a polygon P,
* <code>G.within(P) = false</code>
*
*@param g
* the <code>Geometry</code> with which to compare this
* <code>Geometry</code>
*@return <code>true</code> if this <code>Geometry</code> is within
* <code>other</code>
*
* @see Geometry#contains
*/
/*
* public boolean within(Geometry g) { return g.contains(this); }
*/
/**
* Returns <code>true</code> if this geometry contains the specified
* geometry.
* <p>
* The <code>contains</code> predicate has the following equivalent
* definitions:
* <ul>
* <li>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.
* <li>The DE-9IM Intersection Matrix for the two geometries is
* <code>T*****FF*</code>
* <li><code>g.within(this)</code> (<code>contains</code> is the inverse of
* <code>within</code>)
* </ul>
* An implication of the definition is that "Polygons do not contain their
* boundary". In other words, if a geometry G is a subset of the points in
* the boundary of a polygon P, <code>P.contains(G) = false</code>
*
*@param g
* the <code>Geometry</code> with which to compare this
* <code>Geometry</code>
*@return <code>true</code> if this <code>Geometry</code> contains
* <code>g</code>
*
* @see Geometry#within
*/
/*
* 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(); }
*/
/**
* Returns <code>true</code> if this geometry overlaps the specified
* geometry.
* <p>
* The <code>overlaps</code> predicate has the following equivalent
* definitions:
* <ul>
* <li>The geometries have some but not all points in common, they have the
* same dimension, and the intersection of the interiors of the two
* geometries has the same dimension as the geometries themselves.
* <li>The DE-9IM Intersection Matrix for the two geometries is
* <code>T*T***T**</code> (for two points or two surfaces) or
* <code>1*T***T**</code> (for two curves)
* </ul>
* If the geometries are of different dimension this predicate returns
* <code>false</code>.
*
*@param g
* the <code>Geometry</code> with which to compare this
* <code>Geometry</code>
*@return <code>true</code> if the two <code>Geometry</code>s overlap.
*/
/*
* public boolean overlaps(Geometry g) { // short-circuit test if (!
* getEnvelopeInternal().intersects(g.getEnvelopeInternal())) return false;
* return relate(g).isOverlaps(getDimension(), g.getDimension()); }
*/
/**
* Returns <code>true</code> if this geometry covers the specified geometry.
* <p>
* The <code>covers</code> predicate has the following equivalent
* definitions:
* <ul>
* <li>Every point of the other geometry is a point of this geometry.
* <li>The DE-9IM Intersection Matrix for the two geometries is
* <code>T*****FF*</code> or <code>*T****FF*</code> or
* <code>***T**FF*</code> or <code>****T*FF*</code>
* <li><code>g.coveredBy(this)</code> (<code>covers</code> is the inverse of
* <code>coverdBy</code>)
* </ul>
* Note the difference between <code>covers</code> and <code>contains</code>
* - <code>covers</code> is a more inclusive relation. In particular, unlike
* <code>contains</code> it does not distinguish between points in the
* boundary and in the interior of geometries. For most situations,
* <code>covers</code> should be used in preference to <code>contains</code>
* . As an added benefit, <code>covers</code> is more amenable to
* optimization, and hence should be more performant.
*
*@param g
* the <code>Geometry</code> with which to compare this
* <code>Geometry</code>
*@return <code>true</code> if this <code>Geometry</code> covers
* <code>g</code>
*
* @see Geometry#contains
* @see Geometry#coveredBy
*/
/*
* public boolean covers(Geometry g) { // short-circuit test if (!
* getEnvelopeInternal().contains(g.getEnvelopeInternal())) return false; //
* optimization for rectangle arguments if (isRectangle()) { return
* getEnvelopeInternal().contains(g.getEnvelopeInternal()); } return
* relate(g).isCovers(); }
*/
/**
* Returns <code>true</code> if this geometry is covered by the specified
* geometry.
* <p>
* The <code>coveredBy</code> predicate has the following equivalent
* definitions:
* <ul>
* <li>Every point of this geometry is a point of the other geometry.
* <li>The DE-9IM Intersection Matrix for the two geometries is
* <code>T*F**F***</code> or <code>*TF**F***</code> or
* <code>**FT*F***</code> or <code>**F*TF***</code>
* <li><code>g.covers(this)</code> (<code>coveredBy</code> is the inverse of
* <code>covers</code>)
* </ul>
* Note the difference between <code>coveredBy</code> and
* <code>within</code> - <code>coveredBy</code> is a more inclusive
* relation.
*
*@param g
* the <code>Geometry</code> with which to compare this
* <code>Geometry</code>
*@return <code>true</code> if this <code>Geometry</code> is covered by
* <code>g</code>
*
* @see Geometry#within
* @see Geometry#covers
*/
/*
* public boolean coveredBy(Geometry g) { return g.covers(this); }
*/
/**
* Returns <code>true</code> if the elements in the DE-9IM
* {@link IntersectionMatrix} for the two <code>Geometry</code>s match the
* elements in <code>intersectionPattern</code>. The pattern is a
* 9-character string, with symbols drawn from the following set:
* <UL>
* <LI>0 (dimension 0)
* <LI>1 (dimension 1)
* <LI>2 (dimension 2)
* <LI>T ( matches 0, 1 or 2)
* <LI>F ( matches FALSE)
* <LI>* ( matches any value)
* </UL>
* For more information on the DE-9IM, see the <i>OpenGIS Simple Features
* Specification</i>.
*
*@param other
* the <code>Geometry</code> with which to compare this
* <code>Geometry</code>
*@param intersectionPattern
* the pattern against which to check the intersection matrix for
* the two <code>Geometry</code>s
*@return <code>true</code> if the DE-9IM intersection matrix for the two
* <code>Geometry</code>s match <code>intersectionPattern</code>
* @see IntersectionMatrix
*/
/*
* public boolean relate(Geometry g, String intersectionPattern) { return
* relate(g).matches(intersectionPattern); }
*/
/**
* Returns the DE-9IM {@link IntersectionMatrix} for the two
* <code>Geometry</code>s.
*
*@param other
* the <code>Geometry</code> with which to compare this
* <code>Geometry</code>
*@return an {@link IntersectionMatrix} describing the intersections of the
* interiors, boundaries and exteriors of the two
* <code>Geometry</code>s
*/
/*
* public IntersectionMatrix relate(Geometry g) {
* checkNotGeometryCollection(this); checkNotGeometryCollection(g); return
* RelateOp.relate(this, g); }
*/
/**
* Returns <code>true</code> if this geometry is equal to the specified
* geometry.
* <p>
* The <code>equals</code> predicate has the following equivalent
* definitions:
* <ul>
* <li>The two geometries have at least one point in common, and no point of
* either geometry lies in the exterior of the other geometry.
* <li>The DE-9IM Intersection Matrix for the two geometries is T*F**FFF*
* </ul>
*
*@param other
* the <code>Geometry</code> with which to compare this
* <code>Geometry</code>
*@return <code>true</code> if the two <code>Geometry</code>s are equal
*/
/*
* public boolean equals(Geometry g) { // short-circuit test if (!
* getEnvelopeInternal().equals(g.getEnvelopeInternal())) return false;
* return relate(g).isEquals(getDimension(), g.getDimension()); }
*/
// <<PERHAPS:DESIGN>> Override Object#equals [Jon Aquino]
/*
* public String toString() { return toText(); }
*/
/**
* Returns the Well-known Text representation of this <code>Geometry</code>.
* For a definition of the Well-known Text format, see the OpenGIS Simple
* Features Specification.
*
*@return the Well-known Text representation of this <code>Geometry</code>
*/
/*
* 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 <code>abs(distance)</code>. The buffer is
* constructed using 8 segments per quadrant to represent curves. The end
* cap style is <tt>CAP_ROUND</tt>.
*
*@param distance
* the width of the buffer (may be positive, negative or 0)
*@return an area geometry representing the buffer region
*
* @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.
* <p>
* Buffer area boundaries can contain circular arcs. To represent these arcs
* using linear geometry they must be approximated with line segments. The
* <code>quadrantSegments</code> argument allows controlling the accuracy of
* the approximation by specifying the number of line segments used to
* represent a quadrant of a circle
*
*@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 an area geometry representing the buffer region
*
* @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.
* <p>
* Buffer area boundaries can contain circular arcs. To represent these arcs
* using linear geometry they must be approximated with line segments. The
* <code>quadrantSegments</code> argument allows controlling the accuracy of
* the approximation by specifying the number of line segments used to
* represent a quadrant of a circle
* <p>
* The end cap style specifies the buffer geometry that will be created at
* the ends of linestrings. The styles provided are:
* <ul>
* <li><tt>BufferOp.CAP_ROUND</tt> - (default) a semi-circle
* <li><tt>BufferOp.CAP_BUTT</tt> - a straight line perpendicular to the end
* segment
* <li><tt>BufferOp.CAP_SQUARE</tt> - a half-square
* </ul>
*
*@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 an area geometry representing the buffer region
*
* @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 <code>Polygon</code> that contains all the
* points in the <code>Geometry</code>. This obviously applies only to
* <code>Geometry</code> s which contain 3 or more points; the results for
* degenerate cases are specified as follows:
* <TABLE>
* <TR>
* <TH>Number of <code>Point</code>s in argument <code>Geometry</code></TH>
* <TH> <code>Geometry</code> class of result</TH>
* </TR>
* <TR>
* <TD>0</TD>
* <TD>empty <code>GeometryCollection</code></TD>
* </TR>
* <TR>
* <TD>1</TD>
* <TD> <code>Point</code></TD>
* </TR>
* <TR>
* <TD>2</TD>
* <TD> <code>LineString</code></TD>
* </TR>
* <TR>
* <TD>3 or more</TD>
* <TD> <code>Polygon</code></TD>
* </TR>
* </TABLE>
*
*@return the minimum-area convex polygon containing this
* <code>Geometry</code>' s points
*/
/*
* public Geometry convexHull() { return (new
* ConvexHull(this)).getConvexHull(); }
*/
/**
* Computes a <code>Geometry</code> representing the points shared by this
* <code>Geometry</code> and <code>other</code>.
*
* @param other
* the <code>Geometry</code> with which to compute the
* intersection
* @return the points common to the two <code>Geometry</code>s
* @throws TopologyException
* if a robustness error occurs
* @throws IllegalArgumentException
* if either input is a non-empty 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())
* return this.getFactory().createGeometryCollection(null); if
* (other.isEmpty()) return
* this.getFactory().createGeometryCollection(null);
*
* checkNotGeometryCollection(this); checkNotGeometryCollection(other);
* return SnapIfNeededOverlayOp.overlayOp(this, other,
* OverlayOp.INTERSECTION); }
*/
/**
* Computes a <code>Geometry</code> representing all the points in this
* <code>Geometry</code> and <code>other</code>.
*
*@param other
* the <code>Geometry</code> with which to compute the union
*@return a set combining the points of this <code>Geometry</code> and the
* points of <code>other</code>
* @throws TopologyException
* if a robustness error occurs
* @throws IllegalArgumentException
* if either input is a non-empty GeometryCollection
*/
/*
* public Geometry union(Geometry other) { // special case: if either input
* is empty ==> other input if (this.isEmpty()) return (Geometry)
* other.clone(); if (other.isEmpty()) return (Geometry) clone();
*
* checkNotGeometryCollection(this); checkNotGeometryCollection(other);
* return SnapIfNeededOverlayOp.overlayOp(this, other, OverlayOp.UNION); }
*/
/**
* Computes a <code>Geometry</code> representing the points making up this
* <code>Geometry</code> that do not make up <code>other</code>. This method
* returns the closure of the resultant <code>Geometry</code>.
*
*@param other
* the <code>Geometry</code> with which to compute the difference
*@return the point set difference of this <code>Geometry</code> with
* <code>other</code>
* @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
* this.getFactory().createGeometryCollection(null); if (other.isEmpty())
* return (Geometry) clone();
*
* checkNotGeometryCollection(this); checkNotGeometryCollection(other);
* return SnapIfNeededOverlayOp.overlayOp(this, other,
* OverlayOp.DIFFERENCE); }
*/
/**
* Returns a set combining the points in this <code>Geometry</code> not in
* <code>other</code>, and the points in <code>other</code> not in this
* <code>Geometry</code>. This method returns the closure of the resultant
* <code>Geometry</code>.
*
*@param other
* the <code>Geometry</code> with which to compute the symmetric
* difference
*@return the point set symmetric difference of this <code>Geometry</code>
* with <code>other</code>
* @throws TopologyException
* if a robustness error occurs
* @throws IllegalArgumentException
* if either input is a non-empty GeometryCollection
*/
/*
* public Geometry symDifference(Geometry other) { // special case: if
* either input is empty ==> other input if (this.isEmpty()) return
* (Geometry) other.clone(); if (other.isEmpty()) return (Geometry) clone();
*
* checkNotGeometryCollection(this); checkNotGeometryCollection(other);
* return SnapIfNeededOverlayOp.overlayOp(this, other,
* OverlayOp.SYMDIFFERENCE); }
*/
/**
* Returns true if the two <code>Geometry</code>s are exactly equal, up to a
* specified distance tolerance. Two Geometries are exactly equal within a
* distance tolerance if and only if:
* <ul>
* <li>they have the same class
* <li>they have the same values for their vertices, within the given
* tolerance distance, in exactly the same order.
* </ul>
* If this and the other <code>Geometry</code>s are composites and any
* children are not <code>Geometry</code>s, returns <code>false</code>.
*
* @param other
* the <code>Geometry</code> with which to compare this
* <code>Geometry</code>
* @parm tolerance distance at or below which two <code>Coordinate</code>s
* are considered equal
* @return <code>true</code> if this and the other <code>Geometry</code> are
* of the same class and have equal internal data.
*/
public abstract boolean equalsExact(Geometry other, double tolerance);
/**
* Returns true if the two <code>Geometry</code>s are exactly equal. Two
* Geometries are exactly equal iff:
* <ul>
* <li>they have the same class
* <li>they have the same values of Coordinates in their internal Coordinate
* lists, in exactly the same order.
* </ul>
* If this and the other <code>Geometry</code>s are composites and any
* children are not <code>Geometry</code>s, returns false.
* <p>
* This provides a stricter test of equality than <code>equals</code>.
*
*@param other
* the <code>Geometry</code> with which to compare this
* <code>Geometry</code>
*@return <code>true</code> if this and the other <code>Geometry</code> are
* of the same class and have equal internal data.
*/
public boolean equalsExact(Geometry other) {
return equalsExact(other, 0);
}
/**
* Performs an operation with or on this <code>Geometry</code>'s
* coordinates. If this method modifies any coordinate values,
* #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 Coordinates at all).
*
*@param filter
* the filter to apply to this <code>Geometry</code>'s
* coordinates
*/
public abstract void apply(CoordinateFilter filter);
/**
* Performs an operation on the coordinates in this <code>Geometry</code>'s
* {@link CoordinateSequence}s. If this method modifies any coordinate
* values, #geometryChanged() must be called to update the geometry state.
*
*@param filter
* the filter to apply
*/
public abstract void apply(CoordinateSequenceFilter filter);
/**
* Performs an operation with or on this <code>Geometry</code> and its
* subelement <code>Geometry</code>s (if any). Only GeometryCollections and
* subclasses have subelement Geometry's.
*
*@param filter
* the filter to apply to this <code>Geometry</code> (and its
* children, if it is a <code>GeometryCollection</code>).
*/
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 <code>Geometry</code>.
*/
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 <code>Geometry</code> to <b>normal form</b> (or <b>
* canonical form</b> ). Normal form is a unique representation for
* <code>Geometry</code> s. It can be used to test whether two
* <code>Geometry</code>s 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.
*/
public abstract void normalize();
/**
* Returns whether this <code>Geometry</code> is greater than, equal to, or
* less than another <code>Geometry</code>.
* <P>
*
* If their classes are different, they are compared using the following
* ordering:
* <UL>
* <LI>Point (lowest)
* <LI>MultiPoint
* <LI>LineString
* <LI>LinearRing
* <LI>MultiLineString
* <LI>Polygon
* <LI>MultiPolygon
* <LI>GeometryCollection (highest)
* </UL>
* If the two <code>Geometry</code>s have the same class, their first
* elements are compared. If those are the same, the second elements are
* compared, etc.
*
*@param o
* a <code>Geometry</code> with which to compare this
* <code>Geometry</code>
*@return a positive number, 0, or a negative number, depending on whether
* this object is greater than, equal to, or less than
* <code>o</code>, 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 <code>Geometry</code> is greater than, equal to, or
* less than another <code>Geometry</code>, using the given
* {@link CoordinateSequenceComparator}.
* <P>
*
* If their classes are different, they are compared using the following
* ordering:
* <UL>
* <LI>Point (lowest)
* <LI>MultiPoint
* <LI>LineString
* <LI>LinearRing
* <LI>MultiLineString
* <LI>Polygon
* <LI>MultiPolygon
* <LI>GeometryCollection (highest)
* </UL>
* If the two <code>Geometry</code>s have the same class, their first
* elements are compared. If those are the same, the second elements are
* compared, etc.
*
*@param o
* a <code>Geometry</code> with which to compare this
* <code>Geometry</code>
*@param comp
* a <code>CoordinateSequenceComparator</code>
*
*@return a positive number, 0, or a negative number, depending on whether
* this object is greater than, equal to, or less than
* <code>o</code>, 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 <code>Geometry</code>s are equal, from the point
* of view of the <code>equalsExact</code> method. Called by
* <code>equalsExact</code> . In general, two <code>Geometry</code> classes
* are considered to be "equivalent" only if they are the same class. An
* exception is <code>LineString</code> , which is considered to be
* equivalent to its subclasses.
*
*@param other
* the <code>Geometry</code> with which to compare this
* <code>Geometry</code> for equality
*@return <code>true</code> if the classes of the two <code>Geometry</code>
* s are considered to be equal by the <code>equalsExact</code>
* method.
*/
protected boolean isEquivalentClass(Geometry other) {
return this.getClass().getName().equals(other.getClass().getName());
}
/**
* Throws an exception if <code>g</code>'s class is
* <code>GeometryCollection</code> . (Its subclasses do not trigger an
* exception).
*
*@param g
* the <code>Geometry</code> to check
*@throws IllegalArgumentException
* if <code>g</code> is a <code>GeometryCollection</code> 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");
}
}
/**
* Returns the minimum and maximum x and y values in this
* <code>Geometry</code> , or a null <code>Envelope</code> if this
* <code>Geometry</code> is empty. Unlike <code>getEnvelopeInternal</code>,
* this method calculates the <code>Envelope</code> each time it is called;
* <code>getEnvelopeInternal</code> caches the result of this method.
*
*@return this <code>Geometry</code>s bounding box; if the
* <code>Geometry</code> is empty, <code>Envelope#isNull</code> will
* return <code>true</code>
*/
protected abstract Envelope computeEnvelopeInternal();
/**
* Returns whether this <code>Geometry</code> is greater than, equal to, or
* less than another <code>Geometry</code> having the same class.
*
*@param o
* a <code>Geometry</code> having the same class as this
* <code>Geometry</code>
*@return a positive number, 0, or a negative number, depending on whether
* this object is greater than, equal to, or less than
* <code>o</code>, as defined in "Normal Form For Geometry" in the
* JTS Technical Specifications
*/
protected abstract int compareToSameClass(Object o);
/**
* Returns whether this <code>Geometry</code> is greater than, equal to, or
* less than another <code>Geometry</code> of the same class. using the
* given {@link CoordinateSequenceComparator}.
*
*@param o
* a <code>Geometry</code> having the same class as this
* <code>Geometry</code>
*@param comp
* a <code>CoordinateSequenceComparator</code>
*@return a positive number, 0, or a negative number, depending on whether
* this object is greater than, equal to, or less than
* <code>o</code>, 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 <code>compareTo</code> encountered
* as the two <code>Collection</code>s 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
* <code>b</code> completes before <code>a</code>, a positive number is
* returned; if a before b, a negative number.
*
*@param a
* a <code>Collection</code> of <code>Comparable</code>s
*@param b
* a <code>Collection</code> of <code>Comparable</code>s
*@return the first non-zero <code>compareTo</code> 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);
}
}