com.hazelcast.shaded.org.locationtech.jts.operation.IsSimpleOp Maven / Gradle / Ivy
/*
* Copyright (c) 2016 Vivid Solutions.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* and Eclipse Distribution License v. 1.0 which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
*
* http://www.eclipse.org/org/documents/edl-v10.php.
*/
package com.hazelcast.shaded.org.locationtech.jts.operation;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import com.hazelcast.shaded.org.locationtech.jts.algorithm.BoundaryNodeRule;
import com.hazelcast.shaded.org.locationtech.jts.algorithm.LineIntersector;
import com.hazelcast.shaded.org.locationtech.jts.algorithm.RobustLineIntersector;
import com.hazelcast.shaded.org.locationtech.jts.geom.Coordinate;
import com.hazelcast.shaded.org.locationtech.jts.geom.Geometry;
import com.hazelcast.shaded.org.locationtech.jts.geom.GeometryCollection;
import com.hazelcast.shaded.org.locationtech.jts.geom.LineString;
import com.hazelcast.shaded.org.locationtech.jts.geom.Lineal;
import com.hazelcast.shaded.org.locationtech.jts.geom.LinearRing;
import com.hazelcast.shaded.org.locationtech.jts.geom.MultiLineString;
import com.hazelcast.shaded.org.locationtech.jts.geom.MultiPoint;
import com.hazelcast.shaded.org.locationtech.jts.geom.Point;
import com.hazelcast.shaded.org.locationtech.jts.geom.Polygonal;
import com.hazelcast.shaded.org.locationtech.jts.geom.util.LinearComponentExtracter;
import com.hazelcast.shaded.org.locationtech.jts.geomgraph.Edge;
import com.hazelcast.shaded.org.locationtech.jts.geomgraph.EdgeIntersection;
import com.hazelcast.shaded.org.locationtech.jts.geomgraph.GeometryGraph;
import com.hazelcast.shaded.org.locationtech.jts.geomgraph.index.SegmentIntersector;
/**
* Tests whether a Geometry
is simple.
* In general, the SFS specification of simplicity
* follows the rule:
*
* - A Geometry is simple if and only if the only self-intersections are at
* boundary points.
*
*
* Simplicity is defined for each {@link Geometry} type as follows:
*
* - Polygonal geometries are simple by definition, so
*
isSimple
trivially returns true.
* (Note: this means that isSimple cannot be used to test
* for (invalid) self-intersections in Polygons.
* In order to check if a Polygonal geometry has self-intersections,
* use {@link Geometry#isValid()}).
* - Linear geometries are simple if they do not self-intersect at interior points
* (i.e. points other than boundary points).
* This is equivalent to saying that no two linear components satisfy the SFS {@link Geometry#touches(Geometry)}
* predicate.
*
- Zero-dimensional (point) geometries are simple if and only if they have no
* repeated points.
*
- Empty geometries are always simple, by definition
*
* For {@link Lineal} geometries the evaluation of simplicity
* can be customized by supplying a {@link BoundaryNodeRule}
* to define how boundary points are determined.
* The default is the SFS-standard {@link BoundaryNodeRule#MOD2_BOUNDARY_RULE}.
* Note that under the Mod-2 rule, closed LineStrings (rings)
* will never satisfy the touches predicate at their endpoints, since these are
* interior points, not boundary points.
* If it is required to test whether a set of LineString
s touch
* only at their endpoints, use IsSimpleOp
with {@link BoundaryNodeRule#ENDPOINT_BOUNDARY_RULE}.
* For example, this can be used to validate that a set of lines form a topologically valid
* linear network.
*
* @see BoundaryNodeRule
*
* @version 1.7
*
* @deprecated Replaced by org.locationtech.jts.operation.valid.IsSimpleOp
*/
public class IsSimpleOp
{
private Geometry inputGeom;
private boolean isClosedEndpointsInInterior = true;
private Coordinate nonSimpleLocation = null;
/**
* Creates a simplicity checker using the default SFS Mod-2 Boundary Node Rule
*
* @deprecated use IsSimpleOp(Geometry)
*/
public IsSimpleOp() {
}
/**
* Creates a simplicity checker using the default SFS Mod-2 Boundary Node Rule
*
* @param geom the geometry to test
*/
public IsSimpleOp(Geometry geom) {
this.inputGeom = geom;
}
/**
* Creates a simplicity checker using a given {@link BoundaryNodeRule}
*
* @param geom the geometry to test
* @param boundaryNodeRule the rule to use.
*/
public IsSimpleOp(Geometry geom, BoundaryNodeRule boundaryNodeRule)
{
this.inputGeom = geom;
isClosedEndpointsInInterior = ! boundaryNodeRule.isInBoundary(2);
}
/**
* Tests whether the geometry is simple.
*
* @return true if the geometry is simple
*/
public boolean isSimple()
{
nonSimpleLocation = null;
return computeSimple(inputGeom);
}
private boolean computeSimple(Geometry geom)
{
nonSimpleLocation = null;
if (geom.isEmpty()) return true;
if (geom instanceof LineString) return isSimpleLinearGeometry(geom);
if (geom instanceof MultiLineString) return isSimpleLinearGeometry(geom);
if (geom instanceof MultiPoint) return isSimpleMultiPoint((MultiPoint) geom);
if (geom instanceof Polygonal) return isSimplePolygonal(geom);
if (geom instanceof GeometryCollection) return isSimpleGeometryCollection(geom);
// all other geometry types are simple by definition
return true;
}
/**
* Gets a coordinate for the location where the geometry
* fails to be simple.
* (i.e. where it has a non-boundary self-intersection).
* {@link #isSimple} must be called before this method is called.
*
* @return a coordinate for the location of the non-boundary self-intersection
* or null if the geometry is simple
*/
public Coordinate getNonSimpleLocation()
{
return nonSimpleLocation;
}
/**
* Reports whether a {@link LineString} is simple.
*
* @param geom the lineal geometry to test
* @return true if the geometry is simple
* @deprecated use isSimple()
*/
public boolean isSimple(LineString geom)
{
return isSimpleLinearGeometry(geom);
}
/**
* Reports whether a {@link MultiLineString} geometry is simple.
*
* @param geom the lineal geometry to test
* @return true if the geometry is simple
* @deprecated use isSimple()
*/
public boolean isSimple(MultiLineString geom)
{
return isSimpleLinearGeometry(geom);
}
/**
* A MultiPoint is simple if it has no repeated points
* @deprecated use isSimple()
*/
public boolean isSimple(MultiPoint mp)
{
return isSimpleMultiPoint(mp);
}
private boolean isSimpleMultiPoint(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)) {
nonSimpleLocation = p;
return false;
}
points.add(p);
}
return true;
}
/**
* Computes simplicity for polygonal geometries.
* Polygonal geometries are simple if and only if
* all of their component rings are simple.
*
* @param geom a Polygonal geometry
* @return true if the geometry is simple
*/
private boolean isSimplePolygonal(Geometry geom)
{
List rings = LinearComponentExtracter.getLines(geom);
for (Iterator i = rings.iterator(); i.hasNext(); ) {
LinearRing ring = (LinearRing) i.next();
if (! isSimpleLinearGeometry(ring))
return false;
}
return true;
}
/**
* Semantics for GeometryCollection is
* simple if all components are simple.
*
* @param geom
* @return true if the geometry is simple
*/
private boolean isSimpleGeometryCollection(Geometry geom)
{
for (int i = 0; i < geom.getNumGeometries(); i++ ) {
Geometry comp = geom.getGeometryN(i);
if (! computeSimple(comp))
return false;
}
return true;
}
private boolean isSimpleLinearGeometry(Geometry geom)
{
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
if (! si.hasIntersection()) return true;
if (si.hasProperIntersection()) {
nonSimpleLocation = si.getProperIntersectionPoint();
return false;
}
if (hasNonEndpointIntersection(graph)) return false;
if (isClosedEndpointsInInterior) {
if (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)) {
nonSimpleLocation = ei.getCoordinate();
return true;
}
}
}
return false;
}
private static class EndpointInfo {
Coordinate pt;
boolean isClosed;
int degree;
public EndpointInfo(Coordinate pt)
{
this.pt = pt;
isClosed = false;
degree = 0;
}
public Coordinate getCoordinate() { return pt; }
public void addEndpoint(boolean isClosed)
{
degree++;
this.isClosed |= isClosed;
}
}
/**
* Tests that no edge intersection is the endpoint of a closed line.
* This ensures that closed lines are not touched at their endpoint,
* which is an interior point according to the Mod-2 rule
* 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();
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) {
nonSimpleLocation = eiInfo.getCoordinate();
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);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy