/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2001-2006 Vivid Solutions
* (C) 2001-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.operation;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
import org.geotools.geometry.iso.primitive.CurveImpl;
import org.geotools.geometry.iso.primitive.SurfaceImpl;
import org.geotools.geometry.iso.root.GeometryImpl;
import org.geotools.geometry.iso.topograph2D.Coordinate;
import org.geotools.geometry.iso.topograph2D.Edge;
import org.geotools.geometry.iso.topograph2D.EdgeIntersection;
import org.geotools.geometry.iso.topograph2D.GeometryGraph;
import org.geotools.geometry.iso.topograph2D.index.SegmentIntersector;
import org.geotools.geometry.iso.util.algorithm2D.LineIntersector;
import org.geotools.geometry.iso.util.algorithm2D.RobustLineIntersector;
import org.opengis.geometry.Geometry;
/**
* Tests whether a <code>Geometry</code> is simple. In general, the SFS
* specification of simplicity follows the rule:
* <UL>
* <LI> A Geometry is simple iff the only self-intersections are at boundary
* points.
* </UL>
* Simplicity is defined for each {@link Geometry} subclass as follows:
* <ul>
* <li>Valid polygonal geometries are simple by definition, so
* <code>isSimple</code> trivially returns true.
* <li>Linear geometries are simple iff they do not self-intersect at points
* other than boundary points.
* <li>Zero-dimensional geometries (points) are simple iff they have no
* repeated points.
* <li>Empty <code>Geometry</code>s are always simple
* <ul>
*
*
*
* @source $URL$
*/
public class IsSimpleOp {
public IsSimpleOp() {
}
public boolean isSimple(CurveImpl geom) {
return isSimpleLinearGeometry(geom);
}
public boolean isSimple(SurfaceImpl geom) {
return isSimpleLinearGeometry(geom);
}
// public boolean isSimple(MultiLineString geom) {
// return isSimpleLinearGeometry(geom);
// }
/**
* A MultiPoint is simple iff it has no repeated points
*/
// public boolean isSimple(MultiPoint mp) {
// if (mp.isEmpty())
// return true;
// Set points = new TreeSet();
// for (int i = 0; i < mp.getNumGeometries(); i++) {
// Point pt = (Point) mp.getGeometryN(i);
// Coordinate p = pt.getCoordinate();
// if (points.contains(p))
// return false;
// points.add(p);
// }
// return true;
// }
/**
* Works for:
* - Curve
* - ...
*
* @param geom
* @return
*/
private boolean isSimpleLinearGeometry(GeometryImpl geom) {
// TODO auskommentiert; checken!
// if (geom.isEmpty())
// return true;
GeometryGraph graph = new GeometryGraph(0, geom);
LineIntersector li = new RobustLineIntersector();
SegmentIntersector si = graph.computeSelfNodes(li, true);
// if no self-intersection, must be simple
// Primitives can be simple even if their boundary intersect
// Complexes are not simple if their boundary intersect
// TODO Bedeutung der attribute checken und entsprechend anpassen!
if (!si.hasIntersection())
return true;
if (si.hasProperIntersection())
return false;
if (this.hasNonEndpointIntersection(graph))
return false;
if (this.hasClosedEndpointIntersection(graph))
return false;
return true;
}
/**
* For all edges, check if there are any intersections which are NOT at an
* endpoint. The Geometry is not simple if there are intersections not at
* endpoints.
*/
private boolean hasNonEndpointIntersection(GeometryGraph graph) {
for (Iterator i = graph.getEdgeIterator(); i.hasNext();) {
Edge e = (Edge) i.next();
int maxSegmentIndex = e.getMaximumSegmentIndex();
for (Iterator eiIt = e.getEdgeIntersectionList().iterator(); eiIt
.hasNext();) {
EdgeIntersection ei = (EdgeIntersection) eiIt.next();
if (!ei.isEndPoint(maxSegmentIndex))
return true;
}
}
return false;
}
class EndpointInfo {
Coordinate pt;
boolean isClosed;
int degree;
EndpointInfo(Coordinate pt) {
this.pt = pt;
isClosed = false;
degree = 0;
}
void addEndpoint(boolean isClosed) {
degree++;
this.isClosed |= isClosed;
}
}
/**
* Test that no edge intersection is the endpoint of a closed line. To check
* this we compute the degree of each endpoint. The degree of endpoints of
* closed lines must be exactly 2.
*/
private boolean hasClosedEndpointIntersection(GeometryGraph graph) {
Map endPoints = new TreeMap();
for (Iterator i = graph.getEdgeIterator(); i.hasNext();) {
Edge e = (Edge) i.next();
int maxSegmentIndex = e.getMaximumSegmentIndex();
boolean isClosed = e.isClosed();
Coordinate p0 = e.getCoordinate(0);
addEndpoint(endPoints, p0, isClosed);
Coordinate p1 = e.getCoordinate(e.getNumPoints() - 1);
addEndpoint(endPoints, p1, isClosed);
}
for (Iterator i = endPoints.values().iterator(); i.hasNext();) {
EndpointInfo eiInfo = (EndpointInfo) i.next();
if (eiInfo.isClosed && eiInfo.degree != 2)
return true;
}
return false;
}
/**
* Add an endpoint to the map, creating an entry for it if none exists
*/
private void addEndpoint(Map endPoints, Coordinate p, boolean isClosed) {
EndpointInfo eiInfo = (EndpointInfo) endPoints.get(p);
if (eiInfo == null) {
eiInfo = new EndpointInfo(p);
endPoints.put(p, eiInfo);
}
eiInfo.addEndpoint(isClosed);
}
}