Statistics
| Revision:

root / trunk / libraries / libFMap / src / com / vividsolutions / jts / geomgraph / SnappingGeometryGraph.java @ 7591

History | View | Annotate | Download (12.6 KB)

1
/*
2
 * Created on 12-sep-2006 by azabala
3
 *
4
 */
5
package com.vividsolutions.jts.geomgraph;
6

    
7
import java.util.Collection;
8
import java.util.HashMap;
9
import java.util.Iterator;
10
import java.util.List;
11
import java.util.Map;
12

    
13
import com.iver.cit.gvsig.fmap.topology.geomgraph.SnappingPlanarGraph;
14
import com.vividsolutions.jts.geom.*;
15
import com.vividsolutions.jts.geomgraph.*;
16
import com.vividsolutions.jts.geomgraph.index.EdgeSetIntersector;
17
import com.vividsolutions.jts.geomgraph.index.SegmentIntersector;
18
import com.vividsolutions.jts.geomgraph.index.SimpleMCSweepLineIntersector;
19
import com.vividsolutions.jts.util.Assert;
20
import com.vividsolutions.jts.algorithm.*;
21

    
22
/**
23
 */
24
public class SnappingGeometryGraph extends SnappingPlanarGraph {
25
        
26
        /**
27
         * This method implements the Boundary Determination Rule
28
         * for determining whether
29
         * a component (node or edge) that appears multiple times in elements
30
         * of a MultiGeometry is in the boundary or the interior of the Geometry
31
         * <br>
32
         * The SFS uses the "Mod-2 Rule", which this function implements
33
         * <br>
34
         * An alternative (and possibly more intuitive) rule would be
35
         * the "At Most One Rule":
36
         *    isInBoundary = (componentCount == 1)
37
         */
38
          public static boolean isInBoundary(int boundaryCount)
39
          {
40
            // the "Mod-2 Rule"
41
            return boundaryCount % 2 == 1;
42
          }
43
          
44
          
45
          
46
          public static int determineBoundary(int boundaryCount)
47
          {
48
            return isInBoundary(boundaryCount) ? Location.BOUNDARY : Location.INTERIOR;
49
          }
50

    
51
          
52
          private Geometry parentGeom;
53
          
54
          
55
          /**
56
           * The lineEdgeMap is a map of the linestring components of the
57
           * parentGeometry to the edges which are derived from them.
58
           * This is used to efficiently perform findEdge queries
59
           */
60
          private Map lineEdgeMap = new HashMap();
61
          
62
          /**
63
           * If this flag is true, the Boundary Determination Rule will used when deciding
64
           * whether nodes are in the boundary or not
65
           */
66
          private boolean useBoundaryDeterminationRule = false;
67
          
68
          private int argIndex;  // the index of this geometry as an argument to a spatial function (used for labelling)
69
          
70
          private Collection boundaryNodes;
71
          
72
          private boolean hasTooFewPoints = false;
73
          
74
          private Coordinate invalidPoint = null;
75

    
76
          
77
          private EdgeSetIntersector createEdgeSetIntersector()
78
          {
79
          // various options for computing intersections, from slowest to fastest
80

    
81
          //private EdgeSetIntersector esi = new SimpleEdgeSetIntersector();
82
          //private EdgeSetIntersector esi = new MonotoneChainIntersector();
83
          //private EdgeSetIntersector esi = new NonReversingChainIntersector();
84
          //private EdgeSetIntersector esi = new SimpleSweepLineIntersector();
85
          //private EdgeSetIntersector esi = new MCSweepLineIntersector();
86

    
87
            //return new SimpleEdgeSetIntersector();
88
            return new SimpleMCSweepLineIntersector();
89
          }
90

    
91
          /**
92
           * It builds a geometry graph from the parent geometry, applying snapping with
93
           * the specified snap tolerance
94
           * 
95
           * @param argIndex
96
           * @param parentGeom
97
           * @param snapTolerance
98
           */
99
          public SnappingGeometryGraph(int argIndex, Geometry parentGeom, double snapTolerance) {
100
                  super(snapTolerance);            
101
                  this.argIndex = argIndex;
102
            this.parentGeom = parentGeom;
103
            if (parentGeom != null) {
104
                    add(parentGeom);
105
            }
106
          }
107

    
108
          
109
          public boolean hasTooFewPoints() { 
110
                  return hasTooFewPoints; 
111
          }
112
          
113
          
114
          public Coordinate getInvalidPoint() { 
115
                  return invalidPoint; 
116
          }
117

    
118
          
119
          public Geometry getGeometry() { 
120
                  return parentGeom; 
121
          }
122

    
123
          
124
          
125
          public Collection getBoundaryNodes()
126
          {
127
            if (boundaryNodes == null)
128
              boundaryNodes = nodes.getBoundaryNodes(argIndex);
129
            return boundaryNodes;
130
          }
131

    
132
          public Coordinate[] getBoundaryPoints()
133
          {
134
            Collection coll = getBoundaryNodes();
135
            Coordinate[] pts = new Coordinate[coll.size()];
136
            int i = 0;
137
            for (Iterator it = coll.iterator(); it.hasNext(); ) {
138
              Node node = (Node) it.next();
139
              pts[i++] = (Coordinate) node.getCoordinate().clone();
140
            }
141
            return pts;
142
          }
143

    
144
          public Edge findEdge(LineString line)
145
          {
146
            return (Edge) lineEdgeMap.get(line);
147
          }
148

    
149
          public void computeSplitEdges(List edgelist)
150
          {
151
            for (Iterator i = edges.iterator(); i.hasNext(); ) {
152
              Edge e = (Edge) i.next();
153
              e.eiList.addSplitEdges(edgelist);
154
            }
155
          }
156
          private void add(Geometry g)
157
          {
158
            if (g.isEmpty()) return;
159

    
160
            // check if this Geometry should obey the Boundary Determination Rule
161
            // all collections except MultiPolygons obey the rule
162
            if (g instanceof GeometryCollection
163
                && ! (g instanceof MultiPolygon))
164
                    useBoundaryDeterminationRule = true;
165

    
166
            if (g instanceof Polygon)                 addPolygon((Polygon) g);
167
                                // LineString also handles LinearRings
168
            else if (g instanceof LineString)         addLineString((LineString) g);
169
            else if (g instanceof Point)              addPoint((Point) g);
170
            else if (g instanceof MultiPoint)         addCollection((MultiPoint) g);
171
            else if (g instanceof MultiLineString)    addCollection((MultiLineString) g);
172
            else if (g instanceof MultiPolygon)       addCollection((MultiPolygon) g);
173
            else if (g instanceof GeometryCollection) addCollection((GeometryCollection) g);
174
            else  throw new UnsupportedOperationException(g.getClass().getName());
175
          }
176

    
177
          private void addCollection(GeometryCollection gc)
178
          {
179
            for (int i = 0; i < gc.getNumGeometries(); i++) {
180
              Geometry g = gc.getGeometryN(i);
181
              add(g);
182
            }
183
          }
184
          /**
185
           * Add a Point to the graph.
186
           */
187
          private void addPoint(Point p)
188
          {
189
            Coordinate coord = p.getCoordinate();
190
            insertPoint(argIndex, coord, Location.INTERIOR);
191
          }
192
          /**
193
           * The left and right topological location arguments assume that the ring is oriented CW.
194
           * If the ring is in the opposite orientation,
195
           * the left and right locations must be interchanged.
196
           */
197
          private void addPolygonRing(LinearRing lr, int cwLeft, int cwRight)
198
          {
199
            Coordinate[] coord = CoordinateArrays.removeRepeatedPoints(lr.getCoordinates());
200

    
201
            if (coord.length < 4) {
202
              hasTooFewPoints = true;
203
              invalidPoint = coord[0];
204
              return;
205
            }
206

    
207
            int left  = cwLeft;
208
            int right = cwRight;
209
            if (cga.isCCW(coord)) {
210
              left = cwRight;
211
              right = cwLeft;
212
            }
213
            Edge e = new Edge(coord,
214
                                new Label(argIndex, Location.BOUNDARY, left, right));
215
            lineEdgeMap.put(lr, e);
216

    
217
            insertEdge(e);
218
            // insert the endpoint as a node, to mark that it is on the boundary
219
            insertPoint(argIndex, coord[0], Location.BOUNDARY);
220
          }
221

    
222
          private void addPolygon(Polygon p)
223
          {
224
            addPolygonRing(
225
                    (LinearRing) p.getExteriorRing(),
226
                    Location.EXTERIOR,
227
                    Location.INTERIOR);
228

    
229
            for (int i = 0; i < p.getNumInteriorRing(); i++) {
230
              // Holes are topologically labelled opposite to the shell, since
231
              // the interior of the polygon lies on their opposite side
232
              // (on the left, if the hole is oriented CW)
233
              addPolygonRing(
234
                    (LinearRing) p.getInteriorRingN(i),
235
                    Location.INTERIOR,
236
                    Location.EXTERIOR);
237
            }
238
          }
239

    
240
          private void addLineString(LineString line)
241
          {
242
            Coordinate[] coord = CoordinateArrays.removeRepeatedPoints(line.getCoordinates());
243

    
244
            if (coord.length < 2) {
245
              hasTooFewPoints = true;
246
              invalidPoint = coord[0];
247
              return;
248
            }
249

    
250
            // add the edge for the LineString
251
            // line edges do not have locations for their left and right sides
252
            Edge e = new Edge(coord, new Label(argIndex, Location.INTERIOR));
253
            lineEdgeMap.put(line, e);
254
            insertEdge(e);
255
            /**
256
             * Add the boundary points of the LineString, if any.
257
             * Even if the LineString is closed, add both points as if they were endpoints.
258
             * This allows for the case that the node already exists and is a boundary point.
259
             */
260
            Assert.isTrue(coord.length >= 2, "found LineString with single point");
261
            insertBoundaryPoint(argIndex, coord[0]);
262
            insertBoundaryPoint(argIndex, coord[coord.length - 1]);
263

    
264
          }
265

    
266
          /**
267
           * Add an Edge computed externally.  The label on the Edge is assumed
268
           * to be correct.
269
           */
270
          public void addEdge(Edge e)
271
          {
272
            insertEdge(e);
273
            Coordinate[] coord = e.getCoordinates();
274
            // insert the endpoint as a node, to mark that it is on the boundary
275
            insertPoint(argIndex, coord[0], Location.BOUNDARY);
276
            insertPoint(argIndex, coord[coord.length - 1], Location.BOUNDARY);
277
          }
278

    
279
          /**
280
           * Add a point computed externally.  The point is assumed to be a
281
           * Point Geometry part, which has a location of INTERIOR.
282
           */
283
          public void addPoint(Coordinate pt)
284
          {
285
            insertPoint(argIndex, pt, Location.INTERIOR);
286
          }
287

    
288
          /**
289
           * Compute self-nodes, taking advantage of the Geometry type to
290
           * minimize the number of intersection tests.  (E.g. rings are
291
           * not tested for self-intersection, since they are assumed to be valid).
292
           * @param li the LineIntersector to use
293
           * @param computeRingSelfNodes if <false>, intersection checks are optimized to not test rings for self-intersection
294
           * @return the SegmentIntersector used, containing information about the intersections found
295
           */
296
          public SegmentIntersector computeSelfNodes(LineIntersector li, boolean computeRingSelfNodes)
297
          {
298
            SegmentIntersector si = new SegmentIntersector(li, true, false);
299
            EdgeSetIntersector esi = createEdgeSetIntersector();
300
            // optimized test for Polygons and Rings
301
            if (! computeRingSelfNodes
302
                && (parentGeom instanceof LinearRing
303
                || parentGeom instanceof Polygon
304
                || parentGeom instanceof MultiPolygon)) {
305
              esi.computeIntersections(edges, si, false);
306
            }
307
            else {
308
              esi.computeIntersections(edges, si, true);
309
            }
310
//        System.out.println("SegmentIntersector # tests = " + si.numTests);
311
            addSelfIntersectionNodes(argIndex);
312
            return si;
313
          }
314

    
315
        /* NOT USED
316
          public SegmentIntersector computeSelfNodes(LineIntersector li)
317
          {
318
            return computeSelfNodes(li, false);
319
          }
320
        */
321
          public SegmentIntersector computeEdgeIntersections(
322
            GeometryGraph g,
323
            LineIntersector li,
324
            boolean includeProper)
325
          {
326
            SegmentIntersector si = new SegmentIntersector(li, includeProper, true);
327
            si.setBoundaryNodes(this.getBoundaryNodes(), g.getBoundaryNodes());
328

    
329
            EdgeSetIntersector esi = createEdgeSetIntersector();
330
            esi.computeIntersections(edges, g.edges, si);
331
        /*
332
        for (Iterator i = g.edges.iterator(); i.hasNext();) {
333
        Edge e = (Edge) i.next();
334
        Debug.print(e.getEdgeIntersectionList());
335
        }
336
        */
337
            return si;
338
          }
339

    
340
          private void insertPoint(int argIndex, Coordinate coord, int onLocation)
341
          {
342
            Node n = nodes.addNode(coord);
343
            Label lbl = n.getLabel();
344
            if (lbl == null) {
345
              n.label = new Label(argIndex, onLocation);
346
            }
347
            else
348
              lbl.setLocation(argIndex, onLocation);
349
          }
350

    
351
          /**
352
           * Adds points using the mod-2 rule of SFS.  This is used to add the boundary
353
           * points of dim-1 geometries (Curves/MultiCurves).  According to the SFS,
354
           * an endpoint of a Curve is on the boundary
355
           * iff if it is in the boundaries of an odd number of Geometries
356
           */
357
          private void insertBoundaryPoint(int argIndex, Coordinate coord)
358
          {
359
            Node n = nodes.addNode(coord);
360
            Label lbl = n.getLabel();
361
            // the new point to insert is on a boundary
362
            int boundaryCount = 1;
363
            // determine the current location for the point (if any)
364
            int loc = Location.NONE;
365
            if (lbl != null) loc = lbl.getLocation(argIndex, Position.ON);
366
            if (loc == Location.BOUNDARY) boundaryCount++;
367

    
368
            // determine the boundary status of the point according to the Boundary Determination Rule
369
            int newLoc = determineBoundary(boundaryCount);
370
            lbl.setLocation(argIndex, newLoc);
371
          }
372

    
373
          private void addSelfIntersectionNodes(int argIndex)
374
          {
375
            for (Iterator i = edges.iterator(); i.hasNext(); ) {
376
              Edge e = (Edge) i.next();
377
              int eLoc = e.getLabel().getLocation(argIndex);
378
              for (Iterator eiIt = e.eiList.iterator(); eiIt.hasNext(); ) {
379
                EdgeIntersection ei = (EdgeIntersection) eiIt.next();
380
                addSelfIntersectionNode(argIndex, ei.coord, eLoc);
381
              }
382
            }
383
          }
384
          /**
385
           * Add a node for a self-intersection.
386
           * If the node is a potential boundary node (e.g. came from an edge which
387
           * is a boundary) then insert it as a potential boundary node.
388
           * Otherwise, just add it as a regular node.
389
           */
390
          private void addSelfIntersectionNode(int argIndex, Coordinate coord, int loc)
391
          {
392
            // if this node is already a boundary node, don't change it
393
            if (isBoundaryNode(argIndex, coord)) return;
394
            if (loc == Location.BOUNDARY && useBoundaryDeterminationRule)
395
                insertBoundaryPoint(argIndex, coord);
396
            else
397
              insertPoint(argIndex, coord, loc);
398
          }
399

    
400

    
401
}