/*
* The JTS Topology Suite is a collection of Java classes that
* implement the fundamental operations required to validate a given
* geo-spatial data set to a known topological specification.
*
* Copyright (C) 2001 Vivid Solutions
*
* 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; either
* version 2.1 of the License, or (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* For more information, contact:
*
* Vivid Solutions
* Suite #1A
* 2328 Government Street
* Victoria BC V8T 5G5
* Canada
*
* (250)385-6040
* www.vividsolutions.com
*/
package com.revolsys.geometry.operation.overlay;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import com.revolsys.geometry.geomgraph.DirectedEdge;
import com.revolsys.geometry.geomgraph.DirectedEdgeStar;
import com.revolsys.geometry.geomgraph.GeometryGraph;
import com.revolsys.geometry.geomgraph.Label;
import com.revolsys.geometry.geomgraph.Node;
import com.revolsys.geometry.geomgraph.PlanarGraph;
import com.revolsys.geometry.geomgraph.Position;
import com.revolsys.geometry.model.TopologyException;
/**
* Tests whether the polygon rings in a {@link GeometryGraph}
* are consistent.
* Used for checking if Topology errors are present after noding.
*
* @author Martin Davis
* @version 1.7
*/
public class ConsistentPolygonRingChecker {
private final PlanarGraph graph;
private final int LINKING_TO_OUTGOING = 2;
private final int SCANNING_FOR_INCOMING = 1;
public ConsistentPolygonRingChecker(final PlanarGraph graph) {
this.graph = graph;
}
/**
* Tests whether the result geometry is consistent
*
* @throws TopologyException if inconsistent topology is found
*/
public void check(final int opCode) {
for (final Iterator nodeit = this.graph.getNodeIterator(); nodeit.hasNext();) {
final Node node = (Node)nodeit.next();
testLinkResultDirectedEdges((DirectedEdgeStar)node.getEdges(), opCode);
}
}
public void checkAll() {
check(OverlayOp.INTERSECTION);
check(OverlayOp.DIFFERENCE);
check(OverlayOp.UNION);
check(OverlayOp.SYMDIFFERENCE);
}
private List getPotentialResultAreaEdges(final DirectedEdgeStar deStar, final int opCode) {
// print(System.out);
final List resultAreaEdgeList = new ArrayList();
for (final Object element : deStar) {
final DirectedEdge de = (DirectedEdge)element;
if (isPotentialResultAreaEdge(de, opCode) || isPotentialResultAreaEdge(de.getSym(), opCode)) {
resultAreaEdgeList.add(de);
}
}
return resultAreaEdgeList;
}
private boolean isPotentialResultAreaEdge(final DirectedEdge de, final int opCode) {
// mark all dirEdges with the appropriate label
final Label label = de.getLabel();
if (label.isArea() && !de.isInteriorAreaEdge() && OverlayOp.isResultOfOp(
label.getLocation(0, Position.RIGHT), label.getLocation(1, Position.RIGHT), opCode)) {
return true;
// Debug.print("in result "); Debug.println(de);
}
return false;
}
private void testLinkResultDirectedEdges(final DirectedEdgeStar deStar, final int opCode) {
// make sure edges are copied to resultAreaEdges list
final List ringEdges = getPotentialResultAreaEdges(deStar, opCode);
// find first area edge (if any) to start linking at
DirectedEdge firstOut = null;
DirectedEdge incoming = null;
int state = this.SCANNING_FOR_INCOMING;
// link edges in CCW order
for (int i = 0; i < ringEdges.size(); i++) {
final DirectedEdge nextOut = (DirectedEdge)ringEdges.get(i);
final DirectedEdge nextIn = nextOut.getSym();
// skip de's that we're not interested in
if (!nextOut.getLabel().isArea()) {
continue;
}
// record first outgoing edge, in order to link the last incoming edge
if (firstOut == null && isPotentialResultAreaEdge(nextOut, opCode)) {
firstOut = nextOut;
// assert: sym.isInResult() == false, since pairs of dirEdges should
// have been removed already
}
switch (state) {
case SCANNING_FOR_INCOMING:
if (!isPotentialResultAreaEdge(nextIn, opCode)) {
continue;
}
incoming = nextIn;
state = this.LINKING_TO_OUTGOING;
break;
case LINKING_TO_OUTGOING:
if (!isPotentialResultAreaEdge(nextOut, opCode)) {
continue;
}
// incoming.setNext(nextOut);
state = this.SCANNING_FOR_INCOMING;
break;
}
}
// Debug.print(this);
if (state == this.LINKING_TO_OUTGOING) {
// Debug.print(firstOut == null, this);
if (firstOut == null) {
throw new TopologyException("no outgoing dirEdge found", deStar.getCoordinate());
}
}
}
}