/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2014 - 2015, Open Source Geospatial Foundation (OSGeo)
*
* 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;
* version 2.1 of the License.
*
* 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.
*/
package org.geotools.geometry.jts;
import java.util.Arrays;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.CoordinateFilter;
import com.vividsolutions.jts.geom.CoordinateSequence;
import com.vividsolutions.jts.geom.CoordinateSequenceComparator;
import com.vividsolutions.jts.geom.CoordinateSequenceFilter;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryComponentFilter;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.GeometryFilter;
import com.vividsolutions.jts.geom.IntersectionMatrix;
import com.vividsolutions.jts.geom.LinearRing;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.PrecisionModel;
import com.vividsolutions.jts.geom.impl.CoordinateArraySequence;
/**
* A CircularRing is a CircularString whose start and end point coincide. The ring needs to be
* formed of at least two arc circles, in order to be able to determine its orientation.
*
* @author Andrea Aime - GeoSolutions
*/
public class CircularRing extends LinearRing implements SingleCurvedGeometry<LinearRing>,
CurvedRing {
private static final long serialVersionUID = -5796254063449438787L;
/**
* This sequence is used as a fake to trick the constructor
*/
static final CoordinateSequence FAKE_RING_2D = new CoordinateArraySequence(
new Coordinate[] { new Coordinate(0, 0), new Coordinate(0, 1), new Coordinate(1, 1),
new Coordinate(0, 0) });
CircularString delegate;
public CircularRing(CoordinateSequence points, GeometryFactory factory, double tolerance) {
super(FAKE_RING_2D, factory);
delegate = new CircularString(points, factory, tolerance);
if (!delegate.isClosed()) {
throw new IllegalArgumentException(
"Start and end point are not matching, this is not a ring");
}
}
public CircularRing(double[] controlPoints, GeometryFactory factory, double tolerance) {
super(FAKE_RING_2D, factory);
delegate = new CircularString(controlPoints, factory, tolerance);
if (!delegate.isClosed()) {
throw new IllegalArgumentException(
"Start and end point are not matching, this is not a ring");
}
}
@Override
public int getNumArcs() {
return delegate.getNumArcs();
}
@Override
public CircularArc getArcN(int arcIndex) {
return delegate.getArcN(arcIndex);
}
@Override
public LinearRing linearize() {
CoordinateSequence cs = delegate.getLinearizedCoordinateSequence(delegate.tolerance);
return getFactory().createLinearRing(cs);
}
public LinearRing linearize(double tolerance) {
CoordinateSequence cs = delegate.getLinearizedCoordinateSequence(delegate.tolerance);
return getFactory().createLinearRing(cs);
}
@Override
public double getTolerance() {
return delegate.getTolerance();
}
@Override
public CoordinateSequence getLinearizedCoordinateSequence(double tolerance) {
return delegate.getLinearizedCoordinateSequence(tolerance);
}
@Override
public double[] getControlPoints() {
return delegate.controlPoints;
}
/* Optimized overridden methods */
public boolean isClosed() {
return true;
}
public int getDimension() {
return super.getDimension();
}
public int getBoundaryDimension() {
return super.getDimension();
}
public boolean isEmpty() {
return false;
}
public String getGeometryType() {
return "CircularRing";
}
@Override
public int getCoordinatesDimension() {
return delegate.getDimension();
}
/**
* Returns a normalized ring (one that does not have a single arc closing on itself)
*
* @return
*/
public CircularRing normalizeRing() {
if (!isClosed() || getNumArcs() > 1) {
return this;
}
// Single arc with only two points case, we need to create two arcs, trying to
// preserve the original points
CircularArc arc = getArcN(0);
Coordinate center = arc.getCenter();
double radius = arc.getRadius();
double[] cp = arc.getControlPoints();
double angle1 = Math.atan2(cp[1] - center.y, cp[0] - center.x);
double angle2 = Math.atan2(cp[3] - center.y, cp[2] - center.x);
// compute the two mid points
double am1 = (angle1 + angle2) / 2;
double am2 = am1 + Math.PI;
// generate the new control points sequence
double[] ncp = new double[10];
ncp[0] = cp[0];
ncp[1] = cp[1];
ncp[2] = center.x + radius * Math.cos(am1);
ncp[3] = center.y + radius * Math.sin(am1);
ncp[4] = cp[2];
ncp[5] = cp[3];
ncp[6] = center.x + radius * Math.cos(am2);
ncp[7] = center.y + radius * Math.sin(am2);
ncp[8] = cp[0];
ncp[9] = cp[1];
return new CircularRing(ncp, factory, delegate.getTolerance());
}
public Geometry reverse() {
double[] controlPoints = delegate.controlPoints;
GrowableOrdinateArray array = new GrowableOrdinateArray();
array.addAll(controlPoints);
array.reverseOrdinates(0, array.size() - 1);
return new CircularRing(array.getData(), getFactory(), delegate.tolerance);
}
public int getNumGeometries() {
return 1;
}
public Geometry getGeometryN(int n) {
return this;
}
public void setUserData(Object userData) {
super.setUserData(userData);
}
public int getSRID() {
return super.getSRID();
}
public void setSRID(int SRID) {
super.setSRID(SRID);
}
public GeometryFactory getFactory() {
return super.getFactory();
}
public Object getUserData() {
return super.getUserData();
}
public PrecisionModel getPrecisionModel() {
return super.getPrecisionModel();
}
public boolean isRectangle() {
return false;
}
public Point getInteriorPoint() {
return delegate.getInteriorPoint();
}
public Geometry getEnvelope() {
return delegate.getEnvelope();
}
public Envelope getEnvelopeInternal() {
return delegate.getEnvelopeInternal();
}
@Override
protected Envelope computeEnvelopeInternal() {
return delegate.getEnvelopeInternal();
}
public boolean equalsExact(Geometry other) {
return equalsExact(other, 0);
}
public boolean equalsExact(Geometry other, double tolerance) {
if (other instanceof CircularRing) {
CircularRing csOther = (CircularRing) other;
if (Arrays.equals(delegate.controlPoints, csOther.delegate.controlPoints)) {
return true;
}
}
return linearize(tolerance).equalsExact(other, tolerance);
}
public boolean equals(Geometry other) {
if (other instanceof CircularRing) {
CircularRing csOther = (CircularRing) other;
if (Arrays.equals(delegate.controlPoints, csOther.delegate.controlPoints)) {
return true;
}
}
return linearize().equals(other);
}
public boolean equalsTopo(Geometry other) {
if (other instanceof CircularRing) {
CircularRing csOther = (CircularRing) other;
if (Arrays.equals(delegate.controlPoints, csOther.delegate.controlPoints)) {
return true;
}
}
return linearize().equalsTopo(other);
}
public boolean equals(Object o) {
if (o instanceof Geometry) {
return equals((Geometry) o);
} else {
return false;
}
}
public int hashCode() {
return super.hashCode();
}
public String toString() {
return toCurvedText();
}
public String toCurvedText() {
return delegate.toCurvedText();
}
public boolean equalsNorm(Geometry g) {
return super.equalsNorm(g);
}
/*
* Simple linearized delegate methods
*/
public Coordinate[] getCoordinates() {
return linearize().getCoordinates();
}
public CoordinateSequence getCoordinateSequence() {
// trick to avoid issues while JTS validates the ring is closed,
// it's calling super.isClosed() breaking the local override
if (delegate != null) {
return linearize().getCoordinateSequence();
} else {
return super.getCoordinateSequence();
}
}
public Coordinate getCoordinateN(int n) {
// trick to avoid issues while JTS validates the ring is closed,
// it's calling super.isClosed() breaking the local override
if (delegate != null) {
return linearize().getCoordinateN(n);
} else {
return super.getCoordinateN(n);
}
}
public Coordinate getCoordinate() {
return linearize().getCoordinate();
}
public int getNumPoints() {
// trick to avoid issues while JTS validates the ring is closed,
// it's calling super.isClosed() breaking the local override
if (delegate != null) {
return linearize().getNumPoints();
} else {
return super.getNumPoints();
}
}
public Point getPointN(int n) {
return linearize().getPointN(n);
}
public Point getStartPoint() {
return linearize().getStartPoint();
}
public Point getEndPoint() {
return linearize().getEndPoint();
}
public boolean isRing() {
return linearize().isRing();
}
public double getLength() {
// todo: maybe compute the actual circular length?
return linearize().getLength();
}
public Geometry getBoundary() {
return linearize().getBoundary();
}
public boolean isCoordinate(Coordinate pt) {
return linearize().isCoordinate(pt);
}
public void apply(CoordinateFilter filter) {
linearize().apply(filter);
}
public void apply(CoordinateSequenceFilter filter) {
linearize().apply(filter);
}
public void apply(GeometryFilter filter) {
linearize().apply(filter);
}
public void apply(GeometryComponentFilter filter) {
linearize().apply(filter);
}
public void normalize() {
linearize().normalize();
}
public boolean isSimple() {
return linearize().isSimple();
}
public boolean isValid() {
return linearize().isValid();
}
public double distance(Geometry g) {
return linearize().distance(g);
}
public boolean isWithinDistance(Geometry geom, double distance) {
return linearize().isWithinDistance(geom, distance);
}
public double getArea() {
return linearize().getArea();
}
public Point getCentroid() {
return linearize().getCentroid();
}
public void geometryChanged() {
linearize().geometryChanged();
}
public boolean disjoint(Geometry g) {
return linearize().disjoint(g);
}
public boolean touches(Geometry g) {
return linearize().touches(g);
}
public boolean intersects(Geometry g) {
return linearize().intersects(g);
}
public boolean crosses(Geometry g) {
return linearize().crosses(g);
}
public boolean within(Geometry g) {
return linearize().within(g);
}
public boolean contains(Geometry g) {
return linearize().contains(g);
}
public boolean overlaps(Geometry g) {
return linearize().overlaps(g);
}
public boolean covers(Geometry g) {
return linearize().covers(g);
}
public boolean coveredBy(Geometry g) {
return linearize().coveredBy(g);
}
public boolean relate(Geometry g, String intersectionPattern) {
return linearize().relate(g, intersectionPattern);
}
public IntersectionMatrix relate(Geometry g) {
return linearize().relate(g);
}
public Geometry buffer(double distance) {
return linearize().buffer(distance);
}
public Geometry buffer(double distance, int quadrantSegments) {
return linearize().buffer(distance, quadrantSegments);
}
public Geometry buffer(double distance, int quadrantSegments, int endCapStyle) {
return linearize().buffer(distance, quadrantSegments, endCapStyle);
}
public Geometry convexHull() {
return linearize().convexHull();
}
public Geometry intersection(Geometry other) {
return linearize().intersection(other);
}
public Geometry union(Geometry other) {
return linearize().union(other);
}
public Geometry difference(Geometry other) {
return linearize().difference(other);
}
public Geometry symDifference(Geometry other) {
return linearize().symDifference(other);
}
public Geometry union() {
return linearize().union();
}
public Geometry norm() {
return linearize().norm();
}
public int compareTo(Object o) {
return linearize().compareTo(o);
}
public int compareTo(Object o, CoordinateSequenceComparator comp) {
return linearize().compareTo(o, comp);
}
@Override
public String toText() {
return linearize().toText();
}
}