/*
* This is part of Geomajas, a GIS framework, http://www.geomajas.org/.
*
* Copyright 2008-2015 Geosparc nv, http://www.geosparc.com/, Belgium.
*
* The program is available in open source according to the GNU Affero
* General Public License. All contributions in this program are covered
* by the Geomajas Contributors License Agreement. For full licensing
* details, see LICENSE.txt in the project root.
*/
package org.geomajas.gwt.client.spatial.geometry;
import org.geomajas.geometry.Coordinate;
import org.geomajas.gwt.client.spatial.Bbox;
import org.geomajas.layer.LayerType;
/**
* Ploygon client-side GWT object.
*
* @author Pieter De Graef
*/
public class Polygon extends AbstractGeometry {
private static final long serialVersionUID = -5666523373215557580L;
private LinearRing exteriorRing;
private LinearRing[] interiorRings;
// -------------------------------------------------------------------------
// Constructor:
// -------------------------------------------------------------------------
Polygon(int srid, int precision) {
super(srid, precision);
}
Polygon(int srid, int precision, LinearRing exteriorRing, LinearRing[] interiorRings) {
super(srid, precision);
this.exteriorRing = exteriorRing;
this.interiorRings = interiorRings;
}
// -------------------------------------------------------------------------
// Class specific functions:
// -------------------------------------------------------------------------
LinearRing[] getInteriorRings() {
return interiorRings;
}
void setInteriorRings(LinearRing[] interiorRings) {
this.interiorRings = interiorRings;
}
void setExteriorRing(LinearRing exteriorRing) {
this.exteriorRing = exteriorRing;
}
/**
* Get the outermost LinearRing of the polygon.
*/
public LinearRing getExteriorRing() {
return exteriorRing;
}
/**
* Get one of the interior LinearRing geometries. i.e. one of the holes.
*
* @param n
* Index in the geometry's interior rings.
* @return Returns the hole if it exists, null otherwise.
*/
public LinearRing getInteriorRingN(int n) {
if (!isEmpty()) {
if (interiorRings != null && interiorRings.length > n) {
return interiorRings[n];
}
}
return null;
}
/**
* Get the total number of interior rings (holes) in the polygon.
*
* @return
*/
public int getNumInteriorRing() {
if (interiorRings != null) {
return interiorRings.length;
}
return 0;
}
// -------------------------------------------------------------------------
// Geometry implementation:
// -------------------------------------------------------------------------
@Override
public Object clone() { // NOSONAR super.clone() not supported by GWT
return getGeometryFactory().createPolygon(exteriorRing, interiorRings);
}
/**
* Return the minimal distance between this coordinate and any vertex of the geometry.
*
* @return Return the minimal distance
*/
public double getDistance(Coordinate coordinate) {
double distance = Double.MAX_VALUE;
if (!isEmpty()) {
for (LinearRing interiorRing : interiorRings) {
double d = interiorRing.getDistance(coordinate);
if (d < distance) {
distance = d;
}
}
double d = exteriorRing.getDistance(coordinate);
if (d < distance) {
distance = d;
}
}
return distance;
}
/**
* Return the sum of all points in the exterior ring + the total number of points in all the interior rings.
*/
public int getNumPoints() {
if (isEmpty()) {
return 0;
}
int total = exteriorRing.getNumPoints();
if (interiorRings != null) {
for (LinearRing interiorRing : interiorRings) {
total += interiorRing.getNumPoints();
}
}
return total;
}
/**
* Return the shell area minus holes areas.
*/
public double getArea() {
double area = 0;
if (!isEmpty()) {
area = exteriorRing.getArea();
if (interiorRings != null) {
for (LinearRing interiorRing : interiorRings) {
area -= interiorRing.getArea();
}
}
}
return area;
}
/**
* Return the total length of all rings.
*/
public double getLength() {
double length = 0;
if (!isEmpty()) {
length = exteriorRing.getLength();
if (interiorRings != null) {
for (LinearRing interiorRing : interiorRings) {
length += interiorRing.getLength();
}
}
}
return length;
}
/**
* The centroid is also known as the "center of gravity" or the "center of mass".
*
* @return Return the center point.
*/
public Coordinate getCentroid() {
if (isEmpty()) {
return null;
}
return exteriorRing.getCentroid();
}
/**
* Is the shell empty or not?
*/
public boolean isEmpty() {
return exteriorRing == null;
}
/**
* Check the exterior ring and all interior rings for validity. Also checks to see if there are no intersections
* between any of the rings (both exterior and interior).
*/
public boolean isValid() {
if (isEmpty()) {
return true;
}
if (!exteriorRing.isValid()) {
return false;
}
if (interiorRings != null) {
LinearRing ring1;
LinearRing ring2;
for (int i = 0; i <= interiorRings.length; i++) {
if (i == interiorRings.length) {
ring1 = exteriorRing;
} else {
ring1 = interiorRings[i];
if (!ring1.isValid()) {
return false;
}
}
for (int j = 0; j <= interiorRings.length; j++) {
if (i == j) {
continue;
}
if (j == interiorRings.length) {
ring2 = exteriorRing;
} else {
ring2 = interiorRings[j];
}
if (ring1.intersects(ring2)) {
return false;
}
}
}
}
return true;
}
/**
* Return the bounds of the exterior ring, or null if the polygon is empty.
*/
public Bbox getBounds() {
if (isEmpty()) {
return null;
}
return exteriorRing.getBounds();
}
/**
* Return the coordinate of the exterior ring, or null if the polygon is empty.
*/
public Coordinate getCoordinate() {
if (isEmpty()) {
return null;
}
return exteriorRing.getCoordinate();
}
/**
* Return the concatenated coordinates of both the exterior ring and all the interior rings. If the polygon is
* empty, null will be returned.
*/
public Coordinate[] getCoordinates() {
if (isEmpty()) {
return null;
}
int len = getNumPoints();
Coordinate[] coordinates = new Coordinate[len];
int count;
for (count = 0; count < exteriorRing.getNumPoints(); count++) {
coordinates[count] = exteriorRing.getCoordinateN(count);
}
if (interiorRings != null) {
for (LinearRing interiorRing : interiorRings) {
for (int n = 0; n < interiorRing.getNumPoints(); n++) {
coordinates[count++] = interiorRing.getCoordinateN(n);
}
}
}
return coordinates;
}
public LayerType getLayerType() {
return LayerType.POLYGON;
}
/**
* Does this polygon intersects with the given geometry?
*
* @param geometry
* The other geometry.
* @return Returns true of the polygon intersects the other geometry.
*/
public boolean intersects(Geometry geometry) {
if (isEmpty()) {
return false;
}
if (exteriorRing.intersects(geometry)) {
return true;
}
if (interiorRings != null) {
for (LinearRing interiorRing : interiorRings) {
if (interiorRing.intersects(geometry)) {
return true;
}
}
}
return false;
}
public String toWkt() {
if (isEmpty()) {
return "POLYGON EMPTY";
}
String wkt = "POLYGON (";
String ringWkt = exteriorRing.toWkt();
wkt += ringWkt.substring(ringWkt.indexOf("("));
if (interiorRings != null) {
for (LinearRing interiorRing : interiorRings) {
ringWkt = interiorRing.toWkt();
wkt += "," + ringWkt.substring(ringWkt.indexOf("("));
}
}
return wkt + ")";
}
}