Class EdgeEndBundle

  1  
  2  
  3  
  4 /*
  5  * Copyright (c) 2016 Vivid Solutions.
  6  *
  7  * All rights reserved. This program and the accompanying materials
  8  * are made available under the terms of the Eclipse Public License 2.0
  9  * and Eclipse Distribution License v. 1.0 which accompanies this distribution.
 10  * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html
 11  * and the Eclipse Distribution License is available at
 12  *
 13  * http://www.eclipse.org/org/documents/edl-v10.php.
 14  */
 15 package org.locationtech.jts.operation.relate;
 16  
 17 import java.io.PrintStream;
 18 import java.util.ArrayList;
 19 import java.util.Iterator;
 20 import java.util.List;
 21  
 22 import org.locationtech.jts.algorithm.BoundaryNodeRule;
 23 import org.locationtech.jts.geom.IntersectionMatrix;
 24 import org.locationtech.jts.geom.Location;
 25 import org.locationtech.jts.geomgraph.Edge;
 26 import org.locationtech.jts.geomgraph.EdgeEnd;
 27 import org.locationtech.jts.geomgraph.GeometryGraph;
 28 import org.locationtech.jts.geomgraph.Label;
 29 import org.locationtech.jts.geomgraph.Position;
 30  
 31 /**
 32  * A collection of {@link EdgeEnd}s which obey the following invariant:
 33  * They originate at the same node and have the same direction.
 34  *
 35  * @version 1.7
 36  */
 37 public class EdgeEndBundle
 38   extends EdgeEnd
 39 {
 40 //  private BoundaryNodeRule boundaryNodeRule;
 41   private List edgeEnds = new ArrayList();
 42  
 43   public EdgeEndBundle(BoundaryNodeRule boundaryNodeRule, EdgeEnd e)
 44   {
 45     super(e.getEdge(), e.getCoordinate(), e.getDirectedCoordinate(), new Label(e.getLabel()));
 46     insert(e);
 47     /*
 48     if (boundaryNodeRule != null)
 49       this.boundaryNodeRule = boundaryNodeRule;
 50     else
 51       boundaryNodeRule = BoundaryNodeRule.OGC_SFS_BOUNDARY_RULE;
 52     */
 53   }
 54  
 55   public EdgeEndBundle(EdgeEnd e)
 56   {
 57     this(null, e);
 58   }
 59  
 60   public Label getLabel() { return label; }
 61   public Iterator iterator() { return edgeEnds.iterator(); }
 62   public List getEdgeEnds() { return edgeEnds; }
 63  
 64   public void insert(EdgeEnd e)
 65   {
 66     // Assert: start point is the same
 67     // Assert: direction is the same
 68     edgeEnds.add(e);
 69   }
 70   /**
 71    * This computes the overall edge label for the set of
 72    * edges in this EdgeStubBundle.  It essentially merges
 73    * the ON and side labels for each edge.  These labels must be compatible
 74    */
 75   public void computeLabel(BoundaryNodeRule boundaryNodeRule)
 76   {
 77     // create the label.  If any of the edges belong to areas,
 78     // the label must be an area label
 79     boolean isArea = false;
 80     for (Iterator it = iterator(); it.hasNext(); ) {
 81       EdgeEnd e = (EdgeEnd) it.next();
 82       if (e.getLabel().isArea()) isArea = true;
 83     }
 84     if (isArea)
 85       label = new Label(Location.NONE, Location.NONE, Location.NONE);
 86     else
 87       label = new Label(Location.NONE);
 88  
 89     // compute the On label, and the side labels if present
 90     for (int i = 0; i < 2; i++) {
 91       computeLabelOn(i, boundaryNodeRule);
 92       if (isArea)
 93         computeLabelSides(i);
 94     }
 95   }
 96  
 97   /**
 98    * Compute the overall ON location for the list of EdgeStubs.
 99    * (This is essentially equivalent to computing the self-overlay of a single Geometry)
100    * edgeStubs can be either on the boundary (e.g. Polygon edge)
101    * OR in the interior (e.g. segment of a LineString)
102    * of their parent Geometry.
103    * In addition, GeometryCollections use a {@link BoundaryNodeRule} to determine
104    * whether a segment is on the boundary or not.
105    * Finally, in GeometryCollections it can occur that an edge is both
106    * on the boundary and in the interior (e.g. a LineString segment lying on
107    * top of a Polygon edge.) In this case the Boundary is given precedence.
108    * <br>
109    * These observations result in the following rules for computing the ON location:
110    * <ul>
111    * <li> if there are an odd number of Bdy edges, the attribute is Bdy
112    * <li> if there are an even number >= 2 of Bdy edges, the attribute is Int
113    * <li> if there are any Int edges, the attribute is Int
114    * <li> otherwise, the attribute is NULL.
115    * </ul>
116    */
117   private void computeLabelOn(int geomIndex, BoundaryNodeRule boundaryNodeRule)
118   {
119     // compute the ON location value
120     int boundaryCount = 0;
121     boolean foundInterior = false;
122  
123     for (Iterator it = iterator(); it.hasNext(); ) {
124       EdgeEnd e = (EdgeEnd) it.next();
125       int loc = e.getLabel().getLocation(geomIndex);
126       if (loc == Location.BOUNDARY) boundaryCount++;
127       if (loc == Location.INTERIOR) foundInterior = true;
128     }
129     int loc = Location.NONE;
130     if (foundInterior)  loc = Location.INTERIOR;
131     if (boundaryCount > 0) {
132       loc = GeometryGraph.determineBoundary(boundaryNodeRule, boundaryCount);
133     }
134     label.setLocation(geomIndex, loc);
135  
136   }
137   /**
138    * Compute the labelling for each side
139    */
140   private void computeLabelSides(int geomIndex)
141   {
142     computeLabelSide(geomIndex, Position.LEFT);
143     computeLabelSide(geomIndex, Position.RIGHT);
144   }
145  
146   /**
147    * To compute the summary label for a side, the algorithm is:
148    *   FOR all edges
149    *     IF any edge's location is INTERIOR for the side, side location = INTERIOR
150    *     ELSE IF there is at least one EXTERIOR attribute, side location = EXTERIOR
151    *     ELSE  side location = NULL
152    *  <br>
153    *  Note that it is possible for two sides to have apparently contradictory information
154    *  i.e. one edge side may indicate that it is in the interior of a geometry, while
155    *  another edge side may indicate the exterior of the same geometry.  This is
156    *  not an incompatibility - GeometryCollections may contain two Polygons that touch
157    *  along an edge.  This is the reason for Interior-primacy rule above - it
158    *  results in the summary label having the Geometry interior on <b>both</b> sides.
159    */
160   private void computeLabelSide(int geomIndex, int side)
161   {
162     for (Iterator it = iterator(); it.hasNext(); ) {
163       EdgeEnd e = (EdgeEnd) it.next();
164       if (e.getLabel().isArea()) {
165         int loc = e.getLabel().getLocation(geomIndex, side);
166         if (loc == Location.INTERIOR) {
167             label.setLocation(geomIndex, side, Location.INTERIOR);
168             return;
169         }
170         else if (loc == Location.EXTERIOR)
171               label.setLocation(geomIndex, side, Location.EXTERIOR);
172       }
173     }
174   }
175  
176   /**
177    * Update the IM with the contribution for the computed label for the EdgeStubs.
178    */
179   void updateIM(IntersectionMatrix im)
180   {
181     Edge.updateIM(label, im);
182   }
183   public void print(PrintStream out)
184   {
185     out.println("EdgeEndBundle--> Label: " + label);
186     for (Iterator it = iterator(); it.hasNext(); ) {
187       EdgeEnd ee = (EdgeEnd) it.next();
188       ee.print(out);
189       out.println();
190     }
191   }
192 }
193