/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2014, 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.ArrayList;
import java.util.List;
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.LineString;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.PrecisionModel;
/**
* A CompoundCurve is a connected sequence of circular arcs and linear segments.
*
* @author Andrea Aime - GeoSolutions
*/
public class CompoundCurve extends LineString implements CompoundCurvedGeometry<LineString> {
private static final long serialVersionUID = -5796254063449438787L;
List<LineString> components;
LineString linearized;
double tolerance;
public CompoundCurve(List<LineString> components, GeometryFactory factory, double tolerance) {
super(CircularString.FAKE_STRING_2D, factory);
this.tolerance = tolerance;
// sanity check, we don't want compound curves containing other compound curves
this.components = new ArrayList<>();
for (LineString ls : components) {
if (ls instanceof CompoundCurve) {
CompoundCurve cc = (CompoundCurve) ls;
this.components.addAll(cc.components);
} else {
this.components.add(ls);
}
}
// check connectedness
if (components.size() > 1) {
LineString prev = components.get(0);
for (int i = 1; i < components.size(); i++) {
LineString curr = components.get(i);
Point endPoint = prev.getEndPoint();
Point startPoint = curr.getStartPoint();
if (!endPoint.equals(startPoint)) {
throw new IllegalArgumentException(
"Found two elements that are not connected, " + prev + " and " + curr);
}
prev = curr;
}
}
}
@Override
public int getCoordinatesDimension() {
if (components.size() == 0) {
return 2;
}
int dimension = Integer.MAX_VALUE;
for (LineString component : components) {
int curr;
if (component instanceof CurvedGeometry<?>) {
curr = ((CurvedGeometry<?>) component).getCoordinatesDimension();
} else {
curr = component.getCoordinateSequence().getDimension();
}
dimension = Math.min(curr, dimension);
}
return dimension;
}
@Override
public LineString linearize() {
return linearize(this.tolerance);
}
public LineString linearize(double tolerance) {
// use the cached one if we are asked for the default geometry tolerance
boolean isDefaultTolerance = CircularArc.equals(tolerance, this.tolerance);
if (linearized != null && isDefaultTolerance) {
return linearized;
}
CoordinateSequence cs = getLinearizedCoordinateSequence(tolerance);
LineString result = new LineString(cs, factory);
if (isDefaultTolerance) {
linearized = result;
}
return result;
}
protected CoordinateSequence getLinearizedCoordinateSequence(final double tolerance) {
// collect all the points of all components
final GrowableOrdinateArray gar = new GrowableOrdinateArray();
for (LineString component : components) {
// the last point of the previous element is the first point of the next one,
// remove the duplication
if (gar.size() > 0) {
gar.setSize(gar.size() - 2);
}
// linearize with tolerance the circular strings, take the linear ones as is
if (component instanceof SingleCurvedGeometry<?>) {
SingleCurvedGeometry<?> curved = (SingleCurvedGeometry<?>) component;
CoordinateSequence cs = curved
.getLinearizedCoordinateSequence(tolerance);
gar.addAll(cs);
} else {
CoordinateSequence cs = component.getCoordinateSequence();
for (int i = 0; i < cs.size(); i++) {
gar.add(cs.getX(i), cs.getY(i));
}
}
}
CoordinateSequence cs = gar.toCoordinateSequence(getFactory());
return cs;
}
@Override
public double getTolerance() {
return tolerance;
}
/**
* Returns the components of this compound curve, which will be a list of straight LineString
* objects and CircularString/CircularRing
*
* @return
*/
public List<LineString> getComponents() {
return components;
}
/* Optimized overridden methods */
public boolean isClosed() {
LineString firstComponent = components.get(0);
LineString lastComponent = components.get(components.size() - 1);
return firstComponent.getStartPoint().equals(lastComponent.getEndPoint());
}
public int getDimension() {
return super.getDimension();
}
public int getBoundaryDimension() {
return super.getDimension();
}
public boolean isEmpty() {
for (LineString ls : components) {
if (!ls.isEmpty()) {
return false;
}
}
;
return true;
}
public String getGeometryType() {
return "CompoundCurve";
}
public Geometry reverse() {
// reverse the component, and reverse each component internal elements
List<LineString> reversedComponents = new ArrayList<>(components.size());
for (LineString ls : components) {
LineString reversed = (LineString) ls.reverse();
reversedComponents.add(0, reversed);
}
return new CompoundCurve(reversedComponents, getFactory(), tolerance);
}
public Point getInteriorPoint() {
return components.get(components.size() / 2).getInteriorPoint();
}
public Geometry getEnvelope() {
return super.getEnvelope();
}
public Envelope getEnvelopeInternal() {
return super.getEnvelopeInternal();
}
@Override
protected Envelope computeEnvelopeInternal() {
final Envelope result = new Envelope();
for (LineString ls : components) {
result.expandToInclude(ls.getEnvelopeInternal());
}
return result;
}
public int getNumGeometries() {
return components.size();
}
public Geometry getGeometryN(int n) {
return components.get(n);
}
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 equalsExact(Geometry other) {
return equalsExact(other, 0);
}
public boolean equalsExact(Geometry other, double tolerance) {
if (other instanceof CompoundCurve) {
CompoundCurve ccOther = (CompoundCurve) other;
if (ccOther.components.size() != components.size()) {
return false;
}
for (int i = 0; i < components.size(); i++) {
LineString ls1 = components.get(i);
LineString ls2 = ccOther.components.get(i);
if (!ls1.equalsExact(ls2, tolerance)) {
return false;
}
}
return true;
}
return linearize(tolerance).equalsExact(other, tolerance);
}
public boolean equals(Geometry other) {
if (other instanceof CompoundCurve) {
CompoundCurve ccOther = (CompoundCurve) other;
if (ccOther.components.size() != components.size()) {
return false;
}
for (int i = 0; i < components.size(); i++) {
LineString ls1 = components.get(i);
LineString ls2 = ccOther.components.get(i);
if (!ls1.equals(ls2)) {
return false;
}
}
return true;
}
return linearize().equals(other);
}
public boolean equalsTopo(Geometry other) {
if (other instanceof CompoundCurve) {
CompoundCurve ccOther = (CompoundCurve) other;
if (ccOther.components.size() != components.size()) {
return false;
}
for (int i = 0; i < components.size(); i++) {
LineString ls1 = components.get(i);
LineString ls2 = ccOther.components.get(i);
if (!ls1.equalsTopo(ls2)) {
return false;
}
}
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() {
StringBuilder sb = new StringBuilder("COMPOUNDCURVE");
if (components.size() == 0) {
sb.append(" EMPTY");
} else {
sb.append("(");
for (int k = 0; k < components.size(); k++) {
LineString component = components.get(k);
if (component instanceof SingleCurvedGeometry<?>) {
SingleCurvedGeometry<?> curved = (SingleCurvedGeometry<?>) component;
sb.append(curved.toCurvedText());
} else {
sb.append("(");
CoordinateSequence cs = component.getCoordinateSequence();
for (int i = 0; i < cs.size(); i++) {
sb.append(cs.getX(i) + " " + cs.getY(i));
if (i < cs.size() - 1) {
sb.append(", ");
}
}
sb.append(")");
}
if (k < components.size() - 1) {
sb.append(", ");
}
}
sb.append(")");
}
return sb.toString();
}
public boolean equalsNorm(Geometry g) {
return super.equalsNorm(g);
}
/*
* Simple linearized delegate methods
*/
public boolean isRectangle() {
return linearized.isRectangle();
}
public Coordinate[] getCoordinates() {
return linearize().getCoordinates();
}
public CoordinateSequence getCoordinateSequence() {
return linearize().getCoordinateSequence();
}
public Coordinate getCoordinateN(int n) {
return linearize().getCoordinateN(n);
}
public Coordinate getCoordinate() {
return linearize().getCoordinate();
}
public int getNumPoints() {
return linearize().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();
}
}