/* * 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.primitive; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Set; import org.geotools.geometry.iso.io.GeometryToString; import org.opengis.geometry.DirectPosition; import org.opengis.geometry.Envelope; import org.opengis.geometry.complex.Complex; import org.opengis.geometry.primitive.Ring; import org.opengis.geometry.primitive.SurfaceBoundary; import org.opengis.referencing.crs.CoordinateReferenceSystem; /** * The boundary of Surfaces shall be represented as SurfaceBoundary. * * @author Jackson Roehrig & Sanjay Jena * * * * @source $URL$ */ public class SurfaceBoundaryImpl extends PrimitiveBoundaryImpl implements SurfaceBoundary { private static final long serialVersionUID = -1357747117053853492L; /** * A SurfaceBoundary consists of some number of Rings, corresponding to the * various components of its boundary. In the normal 2D case, one of these * rings is distinguished as being the exterior boundary. In a general * manifold this is not always possible, in which case all boundaries shall * be listed as interior boundaries, and the exterior will be empty. * * SurfaceBoundary::exterior[0,1] : Ring; * * SurfaceBoundary::interior[0..n] : Ring; * * NOTE The use of exterior and interior here is not intended to invoke the * definitions of "interior" and "exterior" of geometric objects. The terms * are in common usage, and reflect a linguistic metaphor that uses the same * linguistic constructs for the concept of being inside an object to being * inside a container. In normal mathematical terms, the exterior boundary * is the one that appears in the Jordan Separation Theorem (Jordan Curve * Theorem extended beyond 2D). The exterior boundary is the one that * separates the surface (or solid in 3D) from infinite space. The interior * boundaries separate the object at hand from other bounded objects. The * uniqueness of the exterior comes from the uniqueness of unbounded space. * Essentially, the Jordan Separation Theorem shows that normal 2D or 3D * space separates into bounded and unbounded pieces by the insertion of a * ring or shell, respectively. It goes beyond that, but this standard is * restricted to at most 3 dimensions. * * EXAMPLE 1 If the underlying manifold is an infinite cylinder, then two * transverse cuts of the cylinder define a compact surface between the * cuts, and two separate unbounded portions of the cylinders. In this case, * either cut could reasonably be called exterior. In cases of such * ambiguity, the standard chooses to list all boundaries in the "interior" * set. The only guarantee of an exterior boundary being unique is in the * 2-dimensional plane, E2. * * EXAMPLE 2 Taking the equator of a sphere, and generating a 1 meter * buffer, we have a surface with two isomorphic boundary components. There * is no unbiased manner to distinguish one of these as an exterior. * */ private Ring exterior = null; private List<Ring> interior = null; /** * * @param crs * @param exterior * @param interior */ public SurfaceBoundaryImpl(CoordinateReferenceSystem crs, Ring exterior, List<Ring> interior) { super(crs); // TODO The consisty need to checked: Interior rings cannot cross each other or the exterior ring this.exterior = exterior; this.interior = interior; } // /** // * @param factory // * @param patch // */ // public SurfaceBoundaryImpl(FeatGeomFactoryImpl factory, // ArrayList<? extends SurfacePatchImpl> patch) { // super(factory); // if (patch == null || patch.isEmpty()) { // assert (false); // throw new IllegalArgumentException("Constructor not implemented"); //$NON-NLS-1$ // } // // The Exterior Ring of the Surfaceboundary should be calculated considering ALL patches! // this.exterior = patch.get(0).getBoundary().getExterior(); // // this.interior = patch[0].boundary().getInterior(); // // } /* (non-Javadoc) * @see org.geotools.geometry.featgeom.root.GeometryImpl#clone() */ public SurfaceBoundaryImpl clone() throws CloneNotSupportedException { // Test OK // Clone exterior ring and interior rings Ring newExterior = (Ring) this.getExterior().clone(); List<Ring> newInteriors = new ArrayList<Ring>(); Iterator<Ring> interiors = this.getInteriors().iterator(); while (interiors.hasNext()) { newInteriors.add((Ring) interiors.next().clone()); } // Use the cloned rings to create a new SurfaceBoundary return new SurfaceBoundaryImpl(getCoordinateReferenceSystem(), newExterior, newInteriors); } /* (non-Javadoc) * @see org.opengis.geometry.primitive.SurfaceBoundary#getExterior() */ public Ring getExterior() { // Return exterior ring of this boundary return this.exterior; } /* (non-Javadoc) * @see org.opengis.geometry.primitive.SurfaceBoundary#getInteriors() */ public List<Ring> getInteriors() { // Return interior rings of this boundary return this.interior; } /* (non-Javadoc) * @see org.geotools.geometry.featgeom.root.GeometryImpl#getEnvelope() */ public Envelope getEnvelope() { /* Return Envelope of the exterior Ring */ return this.exterior.getEnvelope(); } /* (non-Javadoc) * @see org.geotools.geometry.featgeom.complex.ComplexImpl#createBoundary() */ public Set<Complex> createBoundary() { // Return NULL, because a Boundary does not have a boundary return null; } /* * (non-Javadoc) * * @see org.opengis.geometry.coordinate.root.Geometry#isSimple() */ public boolean isSimple() { // TODO semantic JR, SJ // the boundary of a surface does not have too be simple in all cases // for example, when an interior ring touches the exterior ring // question is, whether this is allowed or not // TODO implementation // TODO test // TODO documentation return false; } /* (non-Javadoc) * @see org.geotools.geometry.featgeom.root.GeometryImpl#getDimension(org.opengis.geometry.coordinate.DirectPosition) */ public int getDimension(DirectPosition point) { // TODO What is going to happen with the point parameter?! // The Dimension of a SurfaceBoundary is 1, because a SurfaceBoundary consists of Rings. return 1; } /* (non-Javadoc) * @see org.geotools.geometry.featgeom.root.GeometryImpl#getRepresentativePoint() */ public DirectPosition getRepresentativePoint() { // ok // Return first point of the exterior ring of the surface boundary return ((CurveImpl)this.getExterior().getGenerators().iterator().next()).getStartPoint(); } /* (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 + ((exterior == null) ? 0 : exterior.hashCode()); result = PRIME * result + ((interior == null) ? 0 : interior.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 SurfaceBoundaryImpl other = (SurfaceBoundaryImpl) obj; if (exterior == null) { if (other.exterior != null) return false; } else if (!exterior.equals(other.exterior)) return false; if (interior == null) { if (other.interior != null) return false; } else if (!interior.equals(other.interior)) return false; return true; } // /** // * @return the length of the ring // */ // public double getLength() { // // not tested // double len = 0.0; // if (this.exterior != null) // len = this.getExterior().getLength(); // if (this.interior != null) { // for (int i = 0; i < this.interior.size(); ++i) { // RingImpl ring = (RingImpl) this.interior.get(i); // len = len + ring.getLength(); // } // } // return len; // } //Not used! // /** // * @param distance // */ // public void split(double distance) { // ((RingImpl) this.exterior).split(distance); // if (this.interior != null) { // for (Ring ring : this.interior) { // ((RingImpl) ring).split(distance); // } // } // } }