Class ConsistentPolygonRingChecker

  1 /*
  2  * Copyright (c) 2016 Vivid Solutions.
  3  *
  4  * All rights reserved. This program and the accompanying materials
  5  * are made available under the terms of the Eclipse Public License 2.0
  6  * and Eclipse Distribution License v. 1.0 which accompanies this distribution.
  7  * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html
  8  * and the Eclipse Distribution License is available at
  9  *
 10  * http://www.eclipse.org/org/documents/edl-v10.php.
 11  */
 12  
 13 package org.locationtech.jts.operation.overlay;
 14  
 15 import java.util.ArrayList;
 16 import java.util.Iterator;
 17 import java.util.List;
 18  
 19 import org.locationtech.jts.geom.TopologyException;
 20 import org.locationtech.jts.geomgraph.DirectedEdge;
 21 import org.locationtech.jts.geomgraph.DirectedEdgeStar;
 22 import org.locationtech.jts.geomgraph.GeometryGraph;
 23 import org.locationtech.jts.geomgraph.Label;
 24 import org.locationtech.jts.geomgraph.Node;
 25 import org.locationtech.jts.geomgraph.PlanarGraph;
 26 import org.locationtech.jts.geomgraph.Position;
 27  
 28 /**
 29  * Tests whether the polygon rings in a {@link GeometryGraph}
 30  * are consistent.
 31  * Used for checking if Topology errors are present after noding.
 32  *
 33  * @author Martin Davis
 34  * @version 1.7
 35  */
 36 public class ConsistentPolygonRingChecker
 37 {
 38   private PlanarGraph graph;
 39  
 40   public ConsistentPolygonRingChecker(PlanarGraph graph) {
 41     this.graph = graph;
 42   }
 43  
 44   public void checkAll()
 45   {
 46     check(OverlayOp.INTERSECTION);
 47     check(OverlayOp.DIFFERENCE);
 48     check(OverlayOp.UNION);
 49     check(OverlayOp.SYMDIFFERENCE);
 50   }
 51  
 52   /**
 53    * Tests whether the result geometry is consistent
 54    *
 55    * @throws TopologyException if inconsistent topology is found
 56    */
 57   public void check(int opCode)
 58   {
 59     for (Iterator nodeit = graph.getNodeIterator(); nodeit.hasNext(); ) {
 60       Node node = (Node) nodeit.next();
 61       testLinkResultDirectedEdges((DirectedEdgeStar) node.getEdges(), opCode);
 62     }
 63   }
 64  
 65   private List getPotentialResultAreaEdges(DirectedEdgeStar deStar, int opCode)
 66   {
 67 //print(System.out);
 68     List resultAreaEdgeList = new ArrayList();
 69     for (Iterator it = deStar.iterator(); it.hasNext(); ) {
 70       DirectedEdge de = (DirectedEdge) it.next();
 71       if (isPotentialResultAreaEdge(de, opCode) || isPotentialResultAreaEdge(de.getSym(), opCode) )
 72         resultAreaEdgeList.add(de);
 73     }
 74     return resultAreaEdgeList;
 75   }
 76  
 77   private boolean isPotentialResultAreaEdge(DirectedEdge de, int opCode)
 78   {
 79     // mark all dirEdges with the appropriate label
 80     Label label = de.getLabel();
 81     if (label.isArea()
 82         && ! de.isInteriorAreaEdge()
 83         && OverlayOp.isResultOfOp(
 84         label.getLocation(0, Position.RIGHT),
 85         label.getLocation(1, Position.RIGHT),
 86         opCode)
 87       ) {
 88         return true;
 89 //Debug.print("in result "); Debug.println(de);
 90       }
 91       return false;
 92     }
 93  
 94   private final int SCANNING_FOR_INCOMING = 1;
 95   private final int LINKING_TO_OUTGOING = 2;
 96  
 97   private void testLinkResultDirectedEdges(DirectedEdgeStar deStar, int opCode)
 98   {
 99     // make sure edges are copied to resultAreaEdges list
100     List ringEdges = getPotentialResultAreaEdges(deStar, opCode);
101     // find first area edge (if any) to start linking at
102     DirectedEdge firstOut = null;
103     DirectedEdge incoming = null;
104     int state = SCANNING_FOR_INCOMING;
105     // link edges in CCW order
106     for (int i = 0; i < ringEdges.size(); i++) {
107       DirectedEdge nextOut = (DirectedEdge) ringEdges.get(i);
108       DirectedEdge nextIn = nextOut.getSym();
109  
110       // skip de's that we're not interested in
111       if (! nextOut.getLabel().isArea()) continue;
112  
113       // record first outgoing edge, in order to link the last incoming edge
114       if (firstOut == null
115           && isPotentialResultAreaEdge(nextOut, opCode))
116         firstOut = nextOut;
117       // assert: sym.isInResult() == false, since pairs of dirEdges should have been removed already
118  
119       switch (state) {
120       case SCANNING_FOR_INCOMING:
121         if (! isPotentialResultAreaEdge(nextIn, opCode)) continue;
122         incoming = nextIn;
123         state = LINKING_TO_OUTGOING;
124         break;
125       case LINKING_TO_OUTGOING:
126         if (! isPotentialResultAreaEdge(nextOut, opCode)) continue;
127         //incoming.setNext(nextOut);
128         state = SCANNING_FOR_INCOMING;
129         break;
130       }
131     }
132 //Debug.print(this);
133     if (state == LINKING_TO_OUTGOING) {
134 //Debug.print(firstOut == null, this);
135       if (firstOut == null)
136         throw new TopologyException("no outgoing dirEdge found", deStar.getCoordinate());
137     }
138  
139   }
140  
141  
142  
143  
144 }
145