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

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

There is a newer version: 0.1.4
Show 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.util.Arrays;

import com.vividsolutions.jts.algorithm.*;

/**
 * Represents a polygon with linear edges, which may include holes.
 * The outer boundary (shell) 
 * and inner boundaries (holes) of the polygon are represented by {@link LinearRing}s.
 * The boundary rings of the polygon may have any orientation.
 * Polygons are closed, simple geometries by definition.
 * 

* The polygon model conforms to the assertions specified in the * OpenGIS Simple Features * Specification for SQL. *

* A Polygon is topologically valid if and only if: *

    *
  • the coordinates which define it are valid coordinates *
  • the linear rings for the shell and holes are valid * (i.e. are closed and do not self-intersect) *
  • holes touch the shell or another hole at at most one point * (which implies that the rings of the shell and holes must not cross) *
  • the interior of the polygon is connected, * or equivalently no sequence of touching holes * makes the interior of the polygon disconnected * (i.e. effectively split the polygon into two pieces). *
* *@version 1.7 */ public class Polygon extends Geometry implements Polygonal { private static final long serialVersionUID = -3494792200821764533L; /** * The exterior boundary, * or null if this Polygon * is empty. */ protected LinearRing shell = null; /** * The interior boundaries, if any. * This instance var is never null. * If there are no holes, the array is of zero length. */ protected LinearRing[] holes; /** * Constructs a Polygon with the given exterior boundary. * *@param shell the outer boundary of the new Polygon, * or null or an empty LinearRing if the empty * geometry is to be created. *@param precisionModel the specification of the grid of allowable points * for this Polygon *@param SRID the ID of the Spatial Reference System used by this * Polygon * @deprecated Use GeometryFactory instead */ public Polygon(LinearRing shell, PrecisionModel precisionModel, int SRID) { this(shell, new LinearRing[]{}, new GeometryFactory(precisionModel, SRID)); } /** * Constructs a Polygon with the given exterior boundary and * interior boundaries. * *@param shell the outer boundary of the new Polygon, * or null or an empty LinearRing if the empty * geometry is to be created. *@param holes the inner boundaries of the new Polygon * , or null or empty LinearRings if the empty * geometry is to be created. *@param precisionModel the specification of the grid of allowable points * for this Polygon *@param SRID the ID of the Spatial Reference System used by this * Polygon * @deprecated Use GeometryFactory instead */ public Polygon(LinearRing shell, LinearRing[] holes, PrecisionModel precisionModel, int SRID) { this(shell, holes, new GeometryFactory(precisionModel, SRID)); } /** * Constructs a Polygon with the given exterior boundary and * interior boundaries. * *@param shell the outer boundary of the new Polygon, * or null or an empty LinearRing if the empty * geometry is to be created. *@param holes the inner boundaries of the new Polygon * , or null or empty LinearRings if the empty * geometry is to be created. */ public Polygon(LinearRing shell, LinearRing[] holes, GeometryFactory factory) { super(factory); if (shell == null) { shell = getFactory().createLinearRing((CoordinateSequence)null); } if (holes == null) { holes = new LinearRing[]{}; } if (hasNullElements(holes)) { throw new IllegalArgumentException("holes must not contain null elements"); } if (shell.isEmpty() && hasNonEmptyElements(holes)) { throw new IllegalArgumentException("shell is empty but holes are not"); } this.shell = shell; this.holes = holes; } public Coordinate getCoordinate() { return shell.getCoordinate(); } public Coordinate[] getCoordinates() { if (isEmpty()) { return new Coordinate[]{}; } Coordinate[] coordinates = new Coordinate[getNumPoints()]; int k = -1; Coordinate[] shellCoordinates = shell.getCoordinates(); for (int x = 0; x < shellCoordinates.length; x++) { k++; coordinates[k] = shellCoordinates[x]; } for (int i = 0; i < holes.length; i++) { Coordinate[] childCoordinates = holes[i].getCoordinates(); for (int j = 0; j < childCoordinates.length; j++) { k++; coordinates[k] = childCoordinates[j]; } } return coordinates; } public int getNumPoints() { int numPoints = shell.getNumPoints(); for (int i = 0; i < holes.length; i++) { numPoints += holes[i].getNumPoints(); } return numPoints; } public int getDimension() { return 2; } public int getBoundaryDimension() { return 1; } public boolean isEmpty() { return shell.isEmpty(); } /** * Tests if a valid polygon is simple. * This method always returns true, since a valid polygon is always simple * * @return true */ /* public boolean isSimple() { return true; } */ public boolean isRectangle() { if (getNumInteriorRing() != 0) return false; if (shell == null) return false; if (shell.getNumPoints() != 5) return false; CoordinateSequence seq = shell.getCoordinateSequence(); // check vertices have correct values Envelope env = getEnvelopeInternal(); for (int i = 0; i < 5; i++) { double x = seq.getX(i); if (! (x == env.getMinX() || x == env.getMaxX())) return false; double y = seq.getY(i); if (! (y == env.getMinY() || y == env.getMaxY())) return false; } // check vertices are in right order double prevX = seq.getX(0); double prevY = seq.getY(0); for (int i = 1; i <= 4; i++) { double x = seq.getX(i); double y = seq.getY(i); boolean xChanged = x != prevX; boolean yChanged = y != prevY; if (xChanged == yChanged) return false; prevX = x; prevY = y; } return true; } public LineString getExteriorRing() { return shell; } public int getNumInteriorRing() { return holes.length; } public LineString getInteriorRingN(int n) { return holes[n]; } public String getGeometryType() { return "Polygon"; } /** * Returns the area of this Polygon * *@return the area of the polygon */ public double getArea() { double area = 0.0; area += Math.abs(CGAlgorithms.signedArea(shell.getCoordinateSequence())); for (int i = 0; i < holes.length; i++) { area -= Math.abs(CGAlgorithms.signedArea(holes[i].getCoordinateSequence())); } return area; } /** * Returns the perimeter of this Polygon * *@return the perimeter of the polygon */ public double getLength() { double len = 0.0; len += shell.getLength(); for (int i = 0; i < holes.length; i++) { len += holes[i].getLength(); } return len; } /** * Computes the boundary of this geometry * * @return a lineal geometry (which may be empty) * @see Geometry#getBoundary */ public Geometry getBoundary() { if (isEmpty()) { return getFactory().createMultiLineString(null); } LinearRing[] rings = new LinearRing[holes.length + 1]; rings[0] = shell; for (int i = 0; i < holes.length; i++) { rings[i + 1] = holes[i]; } // create LineString or MultiLineString as appropriate if (rings.length <= 1) return getFactory().createLinearRing(rings[0].getCoordinateSequence()); return getFactory().createMultiLineString(rings); } protected Envelope computeEnvelopeInternal() { return shell.getEnvelopeInternal(); } public boolean equalsExact(Geometry other, double tolerance) { if (!isEquivalentClass(other)) { return false; } Polygon otherPolygon = (Polygon) other; Geometry thisShell = shell; Geometry otherPolygonShell = otherPolygon.shell; if (!thisShell.equalsExact(otherPolygonShell, tolerance)) { return false; } if (holes.length != otherPolygon.holes.length) { return false; } for (int i = 0; i < holes.length; i++) { if (!((Geometry) holes[i]).equalsExact(otherPolygon.holes[i], tolerance)) { return false; } } return true; } public void apply(CoordinateFilter filter) { shell.apply(filter); for (int i = 0; i < holes.length; i++) { holes[i].apply(filter); } } public void apply(CoordinateSequenceFilter filter) { shell.apply(filter); if (! filter.isDone()) { for (int i = 0; i < holes.length; i++) { holes[i].apply(filter); if (filter.isDone()) break; } } if (filter.isGeometryChanged()) geometryChanged(); } public void apply(GeometryFilter filter) { filter.filter(this); } public void apply(GeometryComponentFilter filter) { filter.filter(this); shell.apply(filter); for (int i = 0; i < holes.length; i++) { holes[i].apply(filter); } } /** * Creates and returns a full copy of this {@link Polygon} object. * (including all coordinates contained by it). * * @return a clone of this instance */ public Object clone() { Polygon poly = (Polygon) super.clone(); poly.shell = (LinearRing) shell.clone(); poly.holes = new LinearRing[holes.length]; for (int i = 0; i < holes.length; i++) { poly.holes[i] = (LinearRing) holes[i].clone(); } return poly;// return the clone } public Geometry convexHull() { return getExteriorRing().convexHull(); } public void normalize() { normalize(shell, true); for (int i = 0; i < holes.length; i++) { normalize(holes[i], false); } Arrays.sort(holes); } protected int compareToSameClass(Object o) { LinearRing thisShell = shell; LinearRing otherShell = ((Polygon) o).shell; return thisShell.compareToSameClass(otherShell); } protected int compareToSameClass(Object o, CoordinateSequenceComparator comp) { Polygon poly = (Polygon) o; LinearRing thisShell = shell; LinearRing otherShell = poly.shell; int shellComp = thisShell.compareToSameClass(otherShell, comp); if (shellComp != 0) return shellComp; int nHole1 = getNumInteriorRing(); int nHole2 = poly.getNumInteriorRing(); int i = 0; while (i < nHole1 && i < nHole2) { LinearRing thisHole = (LinearRing) getInteriorRingN(i); LinearRing otherHole = (LinearRing) poly.getInteriorRingN(i); int holeComp = thisHole.compareToSameClass(otherHole, comp); if (holeComp != 0) return holeComp; i++; } if (i < nHole1) return 1; if (i < nHole2) return -1; return 0; } private void normalize(LinearRing ring, boolean clockwise) { if (ring.isEmpty()) { return; } Coordinate[] uniqueCoordinates = new Coordinate[ring.getCoordinates().length - 1]; System.arraycopy(ring.getCoordinates(), 0, uniqueCoordinates, 0, uniqueCoordinates.length); Coordinate minCoordinate = CoordinateArrays.minCoordinate(ring.getCoordinates()); CoordinateArrays.scroll(uniqueCoordinates, minCoordinate); System.arraycopy(uniqueCoordinates, 0, ring.getCoordinates(), 0, uniqueCoordinates.length); ring.getCoordinates()[uniqueCoordinates.length] = uniqueCoordinates[0]; if (CGAlgorithms.isCCW(ring.getCoordinates()) == clockwise) { CoordinateArrays.reverse(ring.getCoordinates()); } } public Geometry reverse() { Polygon poly = (Polygon) super.clone(); poly.shell = (LinearRing) ((LinearRing) shell.clone()).reverse(); poly.holes = new LinearRing[holes.length]; for (int i = 0; i < holes.length; i++) { poly.holes[i] = (LinearRing) ((LinearRing) holes[i].clone()).reverse(); } return poly;// return the clone } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy