/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2006-2008, 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.iso.complex;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import org.geotools.geometry.iso.coordinate.DirectPositionImpl;
import org.geotools.geometry.iso.coordinate.EnvelopeImpl;
import org.geotools.geometry.iso.coordinate.LineStringImpl;
import org.geotools.geometry.iso.io.GeometryToString;
import org.geotools.geometry.iso.primitive.CurveBoundaryImpl;
import org.geotools.geometry.iso.primitive.CurveImpl;
import org.geotools.geometry.iso.primitive.OrientableCurveImpl;
import org.geotools.geometry.iso.primitive.PointImpl;
import org.geotools.geometry.iso.util.DoubleOperation;
import org.opengis.geometry.DirectPosition;
import org.opengis.geometry.Envelope;
import org.opengis.geometry.complex.Complex;
import org.opengis.geometry.complex.CompositeCurve;
import org.opengis.geometry.primitive.Curve;
import org.opengis.geometry.primitive.CurveBoundary;
import org.opengis.geometry.primitive.OrientableCurve;
import org.opengis.geometry.primitive.OrientablePrimitive;
import org.opengis.geometry.primitive.Primitive;
/**
*
* A composite curve, CompositeCurve shall be a Composite with all the geometric
* properties of a curve. These properties are instantiated in the operation
* "curve". Essentially, a composite curve is a list of orientable curves
* (OrientableCurve) agreeing in orientation in a manner such that each curve
* (except the first) begins where the previous one ends.
*
* @author Jackson Roehrig & Sanjay Jena
*
* @source $URL$
*/
public class CompositeCurveImpl extends CompositeImpl<OrientableCurveImpl>
implements CompositeCurve {
private static final long serialVersionUID = -5774107742557426260L;
protected EnvelopeImpl envelope = null;
/**
* The association role Composition::generator associates this
* CompositeCurve to the primitive Curves and OrientableCurves in its
* generating set, the curves that form the core of this complex.
* CompositeCurve::generator : Sequence<OrientableCurve> -- the start point
* of each orientable curve in the generator is the -- end point of the
* previous one CompositeCurve: forAll (1 < j < generator.count - 1)?
* generator[j].endPoint = generator[j+1].startPoint;
*
* NOTE To get a full representation of the elements in the Complex, the
* Points on the boundary of the generator set of Curve would be added to
* the curves in the generator list.
*
* The generator elements will be passed through the super constructor. The
* plausibility of the Curves will be checked within the constructor of the
* CompositeCurve
*
* Constructs a Composite Curve
* @param generator
*/
public CompositeCurveImpl(List<OrientableCurve> generator) {
/* Pass elements to super constructor */
super(generator);
this.checkConsistency();
}
/**
* - Check plausibility, if all curve elements are continuous
* - Build Envelope
*/
private void checkConsistency() {
/* Check plausibility, if all curve elements are continuous */
Iterator<? extends Primitive> ci = this.elements.iterator();
if (!ci.hasNext())
throw new IllegalArgumentException("Curve has no elements."); //$NON-NLS-1$
CurveImpl c0 = (CurveImpl) ci.next();
;
//this.envelope = new EnvelopeImpl(c0.getEnvelope());
this.envelope = new EnvelopeImpl(c0.getEnvelope());
while (ci.hasNext()) {
CurveImpl c1 = (CurveImpl) ci.next();
this.envelope.expand(c1.getEnvelope());
DirectPosition startPoint = c1.getStartPoint();
DirectPosition endPoint = c0.getEndPoint();
if (!endPoint.equals(startPoint)){
throw new IllegalArgumentException(
"Curve elements are not continous. The end point of a curve has to accord to the start point of the following curve."); //$NON-NLS-1$
}
c0 = c1;
}
}
/* (non-Javadoc)
* @see org.geotools.geometry.featgeom.complex.ComplexImpl#getElements()
*/
public List<Primitive> getElements() {
// Override to return a List
return (List<Primitive>) super.getElements();
}
/* (non-Javadoc)
* @see org.geotools.geometry.featgeom.complex.ComplexImpl#createBoundary()
*/
public Set<Complex> createBoundary() {
/*
* used after construction, when the generators were already defined.
* derived classes (e.g. Ring) may overwrite these method
*/
List<Primitive> generator = this.getElements();
if (generator == null)
throw new IllegalArgumentException(
"Could not create the boundary of CompositeCurve."); //$NON-NLS-1$
TreeSet<Complex> result = new TreeSet<Complex>();
result.add( new CurveBoundaryImpl( getCoordinateReferenceSystem(),
new PointImpl( ((CurveImpl) generator
.get(0)).getStartPoint()), new PointImpl( ((CurveImpl) generator
.get(0)).getEndPoint()) ) );
return result;
}
/* (non-Javadoc)
* @see org.geotools.geometry.featgeom.complex.CompositeImpl#getGeneratorClass()
*/
public Class getGeneratorClass() {
return OrientableCurveImpl.class;
}
/**
* Returns all control points of the Ring as one LineString
*
* @return Returns all control points of the Ring as one LineString
*/
// public LineString asLineString() {
// int len = 0;
// /* All Curves of the CompositeCurve */
// OrientableCurve tmpCurves[] = this.getGenerators();
// /* Two dimensional array with all LineSegments for each curve */
// LineSegment tmpLineSegments[][] = new LineSegment[tmpCurves.length][];
// /* Copy LineSegments into array and count number of segments */
// for (int i=0; i<tmpCurves.length; i++) {
// tmpLineSegments[i] = tmpCurves[i].asLineString(0, 0).asLineSegment();
// len += tmpLineSegments[i].length;
// }
// /* Array of Position - one position for each point of the LineSegments */
// Position tmpPositions[] = new Position[len];
// /* First Point */
// tmpPositions[0] = new Position(tmpLineSegments[0][0].startPoint());
// int index = 1;
// /* Copy all following end points into the array of the positions */
// for (int i=0; i<tmpLineSegments.length; i++) {
// for (int j=0; j<tmpLineSegments[i].length; j++) {
// tmpPositions[index] = new Position(tmpLineSegments[i][j].endPoint());
// index++;
// }
// }
// /* Return new LineString, created by the array of Positions of all curves
// */
// return new LineString(tmpPositions);
// }
/***************************************************************************
* ********************************************************* * Method
* implementation of Interface Object **
* *********************************************************
**************************************************************************/
/**
* The method <code>dimension</code> returns the inherent dimension of
* this Object, which is less than or equal to the coordinate dimension. The
* dimension of a collection of geometric objects is the largest dimension
* of any of its pieces. Points are 0-dimensional, curves are 1-dimensional,
* surfaces are 2-dimensional, and solids are 3-dimensional. Locally, the
* dimension of a geometric object at a point is the dimension of a local
* neighborhood of the point - that is the dimension of any coordinate
* neighborhood of the point. Dimension is unambiguously defined only for
* DirectPositions interior to this Object. If the passed DirectPosition2D
* is NULL, then the method returns the largest possible dimension for any
* DirectPosition2D in this Object.
*
* @param point
* a <code>DirectPosition2D</code> value
* @return an <code>int</code> value
*/
public int getDimension(@SuppressWarnings("unused")
final DirectPositionImpl point) {
return 1;
}
/* (non-Javadoc)
* @see org.geotools.geometry.featgeom.root.GeometryImpl#getEnvelope()
*/
public Envelope getEnvelope() {
return this.envelope;
}
/* (non-Javadoc)
* @see org.geotools.geometry.featgeom.root.GeometryImpl#getBoundary()
*/
public CurveBoundary getBoundary() {
// Return start and end point
DirectPosition start = ((Curve)this.getGenerators().get(0)).getStartPoint();
DirectPosition end = ((Curve)this.getGenerators().get(this.elements.size())).getEndPoint();
// Compare start point with end point
if (start.equals(end))
// the boundary is null if the composite curve is closed
return null;
else
return new CurveBoundaryImpl(getCoordinateReferenceSystem(),
new PointImpl(start),
new PointImpl(end));
//return this.getFeatGeometryFactory().getPrimitiveFactory().createCurveBoundary(start, end);
}
/**
* @return LineStringImpl
*/
public LineStringImpl asLineString() {
return this.asLineString(0.0, 0.0);
}
/**
* @param maxSpacing
* @param maxOffset
* @return LineStringImpl
*/
public LineStringImpl asLineString(double maxSpacing, double maxOffset) {
// The function "asLineString" constructs a line string (sequence of
// line segments) where the control points (ends of
// the segments) lie on this curve. If "maxSpacing" is given (not zero),
// then the distance between control points along
// the generated curve shall be not more than "maxSpacing". If
// "maxOffset" is given (not zero), the distance between
// generated curve at any point and the original curve shall not be more
// than the "maxOffset". If both parameters are
// set, then both criteria shall be met. If the original control points
// of the curve lie on the curve, then they shall be
// included in the returned LineString's controlPoints. If both
// parameters are set to zero, then the line string
// returned shall be constructed from the control points of the original
// curve.
// GenericCurve::asLineString(spacing : Distance = 0, offset : Distance
// = 0)
// : LineString
// NOTE This function is useful in creating linear approximations of the
// curve for simple actions such as display. It is often
// referred to as a "stroked curve". For this purpose, the "maxOffset"
// version is useful in maintaining a minimal representation of
// the curve appropriate for the display device being targeted. This
// function is also useful in preparing to transform a curve from
// one coordinate reference system to another by transforming its
// control points. In this case, the "maxSpacing" version is more
// appropriate. Allowing both parameters to default to zero does not
// seem to have any useful geographic nor geometric
// interpretation unless further information is known about how the
// curves were constructed.
List<Primitive> primitives = this.getElements();
if (primitives == null || primitives.isEmpty())
return null;
// JR error: parameter maxSpacing and maxOffset were not passed
LineStringImpl result = ((CurveImpl) primitives.get(0)).asLineString(
maxSpacing, maxOffset);
for (int i = 1; i < primitives.size(); ++i) {
CurveImpl curve = ((CurveImpl) primitives.get(i));
result = result.merge(curve.asLineString(maxSpacing, maxOffset));
}
return result;
}
/**
* @return length
*/
public double getLength() {
List<OrientableCurve> tmpCurves = this.getGenerators();
if (tmpCurves.isEmpty())
return 0.0;
double length = 0.0;
/* Add envelopes of the other Curves */
for (int i = 1; i < tmpCurves.size(); ++i) {
length = DoubleOperation.add(length, ((CurveImpl) tmpCurves.get(i)).length());
//length += ((CurveImpl) tmpCurves.get(i)).length();
}
return length;
}
/**
* @param distance
*/
public void split(double distance) {
for (Primitive primitive : this.getElements()) {
((CurveImpl) primitive).split(distance);
}
}
/* (non-Javadoc)
* @see org.geotools.geometry.featgeom.root.GeometryImpl#clone()
*/
public CompositeCurveImpl clone() throws CloneNotSupportedException {
// TODO Test
Iterator<Curve> elementIter = (Iterator<Curve>) this.elements.iterator();
List<OrientableCurve> newElements = new ArrayList<OrientableCurve>();
while (elementIter.hasNext()) {
newElements.add((Curve) elementIter.next().clone());
}
return (CompositeCurveImpl) new CompositeCurveImpl(newElements);
//return (CompositeCurveImpl) this.getFeatGeometryFactory().getComplexFactory().createCompositeCurve(newElements);
}
/* (non-Javadoc)
* @see org.opengis.geometry.coordinate.root.Geometry#isSimple()
*/
public boolean isSimple() {
// TODO semantic SJ, JR
// TODO implementation
// TODO test
// TODO documentation
return false;
}
/* (non-Javadoc)
* @see org.opengis.geometry.primitive.OrientableCurve#getComposite()
*/
public CompositeCurve getComposite() {
// TODO semantic SJ, JR
// TODO implementation
// TODO test
// TODO documentation
return null;
}
/* (non-Javadoc)
* @see org.opengis.geometry.primitive.OrientablePrimitive#getOrientation()
*/
public int getOrientation() {
// TODO semantic SJ, JR
// TODO implementation
// TODO test
// TODO documentation
return 0;
}
/* (non-Javadoc)
* @see org.opengis.geometry.primitive.OrientablePrimitive#getPrimitive()
*/
public Curve getPrimitive() {
// TODO semantic SJ, JR
// TODO implementation
// TODO test
// TODO documentation
return null;
}
/* (non-Javadoc)
* @see org.opengis.geometry.primitive.Primitive#getContainedPrimitives()
*/
public Set getContainedPrimitives() {
// TODO semantic SJ, JR
// TODO implementation
// TODO test
// TODO documentation
return null;
}
/* (non-Javadoc)
* @see org.opengis.geometry.primitive.Primitive#getContainingPrimitives()
*/
public Set getContainingPrimitives() {
// TODO semantic SJ, JR
// TODO implementation
// TODO test
// TODO documentation
return null;
}
/* (non-Javadoc)
* @see org.opengis.geometry.primitive.Primitive#getComplexes()
*/
public Set getComplexes() {
// TODO semantic SJ, JR
// TODO implementation
// TODO test
// TODO documentation
return null;
}
/* (non-Javadoc)
* @see org.opengis.geometry.primitive.Primitive#getProxy()
*/
public OrientablePrimitive[] getProxy() {
// TODO semantic SJ, JR
// TODO implementation
// TODO test
// TODO documentation
return null;
}
/* (non-Javadoc)
* @see org.opengis.geometry.complex.Composite#getGenerators()
*/
public List<OrientableCurve> getGenerators() {
// ok
// Return the curves which define this CompositeCurve
return (List<OrientableCurve>) this.elements;
}
/* (non-Javadoc)
* @see org.geotools.geometry.featgeom.root.GeometryImpl#getDimension(org.opengis.geometry.coordinate.DirectPosition)
*/
public int getDimension(DirectPosition point) {
// TODO documentation
return 1;
}
/* (non-Javadoc)
* @see org.geotools.geometry.featgeom.root.GeometryImpl#getRepresentativePoint()
*/
public DirectPosition getRepresentativePoint() {
// Use representative point of the first curve of the generator list
return this.elements.get(0).getRepresentativePoint();
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
public String toString() {
return GeometryToString.getString(this);
}
@Override
public int hashCode() {
final int PRIME = 31;
int result = 1;
result = PRIME * result + ((envelope == null) ? 0 : envelope.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final CompositeCurveImpl other = (CompositeCurveImpl) obj;
if (envelope == null) {
if (other.envelope != null)
return false;
} else if (!envelope.equals(other.envelope))
return false;
return true;
}
}