Statistics
| Revision:

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

History | View | Annotate | Download (15.4 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.vividsolutions.jts.algorithm.CGAlgorithms;
14
import com.vividsolutions.jts.algorithm.LineIntersector;
15
import com.vividsolutions.jts.geom.Coordinate;
16
import com.vividsolutions.jts.geom.CoordinateArrays;
17
import com.vividsolutions.jts.geom.Geometry;
18
import com.vividsolutions.jts.geom.GeometryCollection;
19
import com.vividsolutions.jts.geom.LineString;
20
import com.vividsolutions.jts.geom.LinearRing;
21
import com.vividsolutions.jts.geom.Location;
22
import com.vividsolutions.jts.geom.MultiLineString;
23
import com.vividsolutions.jts.geom.MultiPoint;
24
import com.vividsolutions.jts.geom.MultiPolygon;
25
import com.vividsolutions.jts.geom.Point;
26
import com.vividsolutions.jts.geom.Polygon;
27
import com.vividsolutions.jts.geomgraph.index.SegmentIntersector;
28
import com.vividsolutions.jts.geomgraph.index.SnapSimpleMCSweepLineIntersector;
29
import com.vividsolutions.jts.util.Assert;
30

    
31
/**
32
 */
33
public class SnappingGeometryGraph extends GeometryGraph {
34

    
35
        // Properties of PlanarGraph that we want to overwrite to
36
        // use snapping
37
        public static final CGAlgorithms cga = new CGAlgorithms();
38

    
39
        protected SnappingNodeMap nodes;
40

    
41

    
42
        // overwrite to catch them when returned by Snapping map
43
        protected Collection boundaryNodes;
44

    
45
        protected int argIndex;
46

    
47
        private boolean useBoundaryDeterminationRule = false;
48

    
49
        private boolean hasTooFewPoints = false;
50

    
51
        private Coordinate invalidPoint;
52

    
53
        private Map lineEdgeMap = new HashMap(); 
54
        
55
        private Geometry parentGeometry;
56
        
57
        double snapTolerance;
58

    
59
        public SnappingGeometryGraph(NodeFactory nodeFact, double tolerance,
60
                        int argIndex, Geometry parentGeometry) {
61
                //le pasamos al constructor padre GeometryCollection vacia para que
62
                //no replique la construccion del grafo
63
                super(argIndex, new GeometryCollection(new Geometry[0], parentGeometry.getFactory()));
64
                nodes = new SnappingNodeMap(nodeFact, tolerance);
65
                this.argIndex = argIndex;
66
                this.parentGeometry = parentGeometry;
67
                this.snapTolerance = tolerance;
68
                add(parentGeometry);
69
        }
70
        
71
        
72
        public Geometry getGeometry(){
73
                return this.parentGeometry;
74
        }
75
        
76
        
77
        public void dumpNodes(){
78
                this.nodes.dump();
79
        }
80

    
81
        public SnappingGeometryGraph(double tolerance, int argIndex, Geometry parent) {
82
                super(argIndex, new GeometryCollection(new Geometry[0], parent.getFactory()));
83
                nodes = new SnappingNodeMap(new NodeFactory(), tolerance);
84
                this.argIndex = argIndex;
85
                this.snapTolerance = tolerance;
86
                this.parentGeometry = parent;
87
                add(parent);
88
        }
89

    
90
        public Collection getBoundaryNodes() {
91
                if (boundaryNodes == null)
92
                        boundaryNodes = nodes.getBoundaryNodes(argIndex);
93
                return boundaryNodes;
94
        }
95

    
96
        public Coordinate[] getBoundaryPoints() {
97
                Collection coll = getBoundaryNodes();
98
                Coordinate[] pts = new Coordinate[coll.size()];
99
                int i = 0;
100
                for (Iterator it = coll.iterator(); it.hasNext();) {
101
                        Node node = (Node) it.next();
102
                        pts[i++] = (Coordinate) node.getCoordinate().clone();
103
                }
104
                return pts;
105
        }
106

    
107
        public Iterator getNodeIterator() {
108
                return nodes.iterator();
109
        }
110

    
111
        public Collection getNodes() {
112
                return nodes.values();
113
        }
114

    
115
        public Node addNode(Node node) {
116
                return nodes.addNode(node);
117
        }
118

    
119
        public Node addNode(Coordinate coord) {
120
                return nodes.addNode(coord);
121
        }
122

    
123
        /**
124
         * @return the node if found; null otherwise
125
         */
126
        public Node find(Coordinate coord) {
127
                return nodes.find(coord);
128
        }
129
        
130
        public boolean isBoundaryNode(int geomIndex, Coordinate coord) {
131
                Node node = nodes.find(coord);
132
                if (node == null)
133
                        return false;
134
                Label label = node.getLabel();
135
                if (label != null && label.getLocation(geomIndex) == Location.BOUNDARY)
136
                        return true;
137
                return false;
138
        }
139

    
140
        
141

    
142
        public void add(EdgeEnd e) {
143
                nodes.add(e);
144
                edgeEndList.add(e);
145
        }
146
        
147
        
148

    
149
        /**
150
         * Link the DirectedEdges at the nodes of the graph. This allows clients to
151
         * link only a subset of nodes in the graph, for efficiency (because they
152
         * know that only a subset is of interest).
153
         */
154
        public void linkResultDirectedEdges() {
155
                for (Iterator nodeit = nodes.iterator(); nodeit.hasNext();) {
156
                        Node node = (Node) nodeit.next();
157
                        ((DirectedEdgeStar) node.getEdges()).linkResultDirectedEdges();
158
                }
159
        }
160

    
161
        /**
162
         * Link the DirectedEdges at the nodes of the graph. This allows clients to
163
         * link only a subset of nodes in the graph, for efficiency (because they
164
         * know that only a subset is of interest).
165
         */
166
        public void linkAllDirectedEdges() {
167
                for (Iterator nodeit = nodes.iterator(); nodeit.hasNext();) {
168
                        Node node = (Node) nodeit.next();
169
                        ((DirectedEdgeStar) node.getEdges()).linkAllDirectedEdges();
170
                }
171
        }
172

    
173
        /**
174
         * Returns the EdgeEnd which has edge e as its base edge (MD 18 Feb 2002 -
175
         * this should return a pair of edges)
176
         * 
177
         * @return the edge, if found <code>null</code> if the edge was not found
178
         */
179
        public EdgeEnd findEdgeEnd(Edge e) {
180
                for (Iterator i = getEdgeEnds().iterator(); i.hasNext();) {
181
                        EdgeEnd ee = (EdgeEnd) i.next();
182
                        if (ee.getEdge() == e)
183
                                return ee;
184
                }
185
                return null;
186
        }
187

    
188
        /**
189
         * Returns the edge whose first two coordinates are p0 and p1
190
         * 
191
         * @return the edge, if found <code>null</code> if the edge was not found
192
         */
193
        public Edge findEdge(Coordinate p0, Coordinate p1) {
194
                for (int i = 0; i < edges.size(); i++) {
195
                        Edge e = (Edge) edges.get(i);
196
                        Coordinate[] eCoord = e.getCoordinates();
197
                        if (p0.equals(eCoord[0]) && p1.equals(eCoord[1]))
198
                                return e;
199
                }
200
                return null;
201
        }
202

    
203
        /**
204
         * Returns the edge which starts at p0 and whose first segment is parallel
205
         * to p1
206
         * 
207
         * @return the edge, if found <code>null</code> if the edge was not found
208
         */
209
        public Edge findEdgeInSameDirection(Coordinate p0, Coordinate p1) {
210
                for (int i = 0; i < edges.size(); i++) {
211
                        Edge e = (Edge) edges.get(i);
212

    
213
                        Coordinate[] eCoord = e.getCoordinates();
214
                        if (matchInSameDirection(p0, p1, eCoord[0], eCoord[1]))
215
                                return e;
216

    
217
                        if (matchInSameDirection(p0, p1, eCoord[eCoord.length - 1],
218
                                        eCoord[eCoord.length - 2]))
219
                                return e;
220
                }
221
                return null;
222
        }
223

    
224
        /**
225
         * The coordinate pairs match if they define line segments lying in the same
226
         * direction. E.g. the segments are parallel and in the same quadrant (as
227
         * opposed to parallel and opposite!).
228
         */
229
        private boolean matchInSameDirection(Coordinate p0, Coordinate p1,
230
                        Coordinate ep0, Coordinate ep1) {
231
                if (!p0.equals(ep0))
232
                        return false;
233

    
234
                if (CGAlgorithms.computeOrientation(p0, p1, ep1) == CGAlgorithms.COLLINEAR
235
                                && Quadrant.quadrant(p0, p1) == Quadrant.quadrant(ep0, ep1))
236
                        return true;
237
                return false;
238
        }
239

    
240
        
241
        private void add(Geometry g)
242
          {
243
            if (g.isEmpty()) return;
244

    
245
            // check if this Geometry should obey the Boundary Determination Rule
246
            // all collections except MultiPolygons obey the rule
247
            if (g instanceof GeometryCollection
248
                && ! (g instanceof MultiPolygon))
249
                    useBoundaryDeterminationRule = true;
250

    
251
            if (g instanceof Polygon)                 addPolygon((Polygon) g);
252
                                // LineString also handles LinearRings
253
            else if (g instanceof LineString)         addLineString((LineString) g);
254
            else if (g instanceof Point)              addPoint((Point) g);
255
            else if (g instanceof MultiPoint)         addCollection((MultiPoint) g);
256
            else if (g instanceof MultiLineString)    addCollection((MultiLineString) g);
257
            else if (g instanceof MultiPolygon)       addCollection((MultiPolygon) g);
258
            else if (g instanceof GeometryCollection) addCollection((GeometryCollection) g);
259
            else  throw new UnsupportedOperationException(g.getClass().getName());
260
          }
261

    
262
          private void addCollection(GeometryCollection gc)
263
          {
264
            for (int i = 0; i < gc.getNumGeometries(); i++) {
265
              Geometry g = gc.getGeometryN(i);
266
              add(g);
267
            }
268
          }
269
          /**
270
           * Add a Point to the graph.
271
           */
272
          private void addPoint(Point p)
273
          {
274
            Coordinate coord = p.getCoordinate();
275
            insertPoint(argIndex, coord, Location.INTERIOR);
276
          }
277
          /**
278
           * The left and right topological location arguments assume that the ring is oriented CW.
279
           * If the ring is in the opposite orientation,
280
           * the left and right locations must be interchanged.
281
           */
282
          private void addPolygonRing(LinearRing lr, int cwLeft, int cwRight)
283
          {
284
            Coordinate[] coord = CoordinateArrays.removeRepeatedPoints(lr.getCoordinates());
285

    
286
            if (coord.length < 4) {
287
              hasTooFewPoints = true;
288
              invalidPoint = coord[0];
289
              return;
290
            }
291

    
292
            int left  = cwLeft;
293
            int right = cwRight;
294
            if (cga.isCCW(coord)) {
295
              left = cwRight;
296
              right = cwLeft;
297
            }
298
            SnappingEdge e = new SnappingEdge(coord,
299
                                new Label(argIndex, Location.BOUNDARY, left, right), this.snapTolerance);
300
            lineEdgeMap.put(lr, e);
301

    
302
            insertEdge(e);
303
            // insert the endpoint as a node, to mark that it is on the boundary
304
            insertPoint(argIndex, coord[0], Location.BOUNDARY);
305
          }
306

    
307
          private void addPolygon(Polygon p)
308
          {
309
            addPolygonRing(
310
                    (LinearRing) p.getExteriorRing(),
311
                    Location.EXTERIOR,
312
                    Location.INTERIOR);
313

    
314
            for (int i = 0; i < p.getNumInteriorRing(); i++) {
315
              // Holes are topologically labelled opposite to the shell, since
316
              // the interior of the polygon lies on their opposite side
317
              // (on the left, if the hole is oriented CW)
318
              addPolygonRing(
319
                    (LinearRing) p.getInteriorRingN(i),
320
                    Location.INTERIOR,
321
                    Location.EXTERIOR);
322
            }
323
          }
324

    
325
          private void addLineString(LineString line)
326
          {
327
            Coordinate[] coord = CoordinateArrays.removeRepeatedPoints(line.getCoordinates());
328

    
329
            if (coord.length < 2) {
330
              hasTooFewPoints = true;
331
              invalidPoint = coord[0];
332
              return;
333
            }
334

    
335
            // add the edge for the LineString
336
            // line edges do not have locations for their left and right sides
337
            SnappingEdge e = new SnappingEdge(coord, new Label(argIndex, Location.INTERIOR) , this.snapTolerance);
338
            lineEdgeMap.put(line, e);
339
            insertEdge(e);
340
            /**
341
             * Add the boundary points of the LineString, if any.
342
             * Even if the LineString is closed, add both points as if they were endpoints.
343
             * This allows for the case that the node already exists and is a boundary point.
344
             */
345
            Assert.isTrue(coord.length >= 2, "found LineString with single point");
346
            insertBoundaryPoint(argIndex, coord[0]);
347
            insertBoundaryPoint(argIndex, coord[coord.length - 1]);
348

    
349
          }
350

    
351
          /**
352
           * Add an Edge computed externally.  The label on the Edge is assumed
353
           * to be correct.
354
           */
355
          public void addEdge(Edge e)
356
          {
357
            insertEdge(e);
358
            Coordinate[] coord = e.getCoordinates();
359
            // insert the endpoint as a node, to mark that it is on the boundary
360
            insertPoint(argIndex, coord[0], Location.BOUNDARY);
361
            insertPoint(argIndex, coord[coord.length - 1], Location.BOUNDARY);
362
          }
363

    
364
          /**
365
           * Add a point computed externally.  The point is assumed to be a
366
           * Point Geometry part, which has a location of INTERIOR.
367
           */
368
          public void addPoint(Coordinate pt)
369
          {
370
            insertPoint(argIndex, pt, Location.INTERIOR);
371
          }
372
          
373
          
374
          /**
375
           * Compute self-nodes, taking advantage of the Geometry type to
376
           * minimize the number of intersection tests.  (E.g. rings are
377
           * not tested for self-intersection, since they are assumed to be valid).
378
           * @param li the LineIntersector to use
379
           * @param computeRingSelfNodes if <false>, intersection checks are optimized to not test rings for self-intersection
380
           * @return the SegmentIntersector used, containing information about the intersections found
381
           */
382
          public SegmentIntersector computeSelfNodes(LineIntersector li, boolean computeRingSelfNodes)
383
          {
384
            SegmentIntersector si = new SegmentIntersector(li, true, false);
385
            SnapSimpleMCSweepLineIntersector esi = new SnapSimpleMCSweepLineIntersector();
386
            // optimized test for Polygons and Rings
387
            if (! computeRingSelfNodes
388
                && (parentGeometry instanceof LinearRing
389
                || parentGeometry instanceof Polygon
390
                || parentGeometry instanceof MultiPolygon)) {
391
              esi.computeIntersections(edges, si, false);
392
            }
393
            else {
394
              esi.computeIntersections(edges, si, true);
395
            }
396
            addSelfIntersectionNodes(argIndex);
397
            return si;
398
          }
399

    
400

    
401
          
402
          public SegmentIntersector computeEdgeIntersections(
403
            GeometryGraph g,
404
            LineIntersector li,
405
            boolean includeProper)
406
          {
407
            SegmentIntersector siSnap = new SegmentIntersector(li, includeProper, true);
408
            siSnap.setBoundaryNodes(this.getBoundaryNodes(), g.getBoundaryNodes());
409

    
410
            SnapSimpleMCSweepLineIntersector esiSnap = new SnapSimpleMCSweepLineIntersector();
411
            esiSnap.computeIntersections(edges, g.edges, siSnap);
412
            return siSnap;
413
                  
414
//                  return super.computeEdgeIntersections(g, li, includeProper);
415
          }
416

    
417
         
418
         
419

    
420
          private void addSelfIntersectionNodes(int argIndex)
421
          {
422
            for (Iterator i = edges.iterator(); i.hasNext(); ) {
423
              Edge e = (Edge) i.next();
424
              int eLoc = e.getLabel().getLocation(argIndex);
425
              for (Iterator eiIt = e.eiList.iterator(); eiIt.hasNext(); ) {
426
                EdgeIntersection ei = (EdgeIntersection) eiIt.next();
427
                addSelfIntersectionNode(argIndex, ei.coord, eLoc);
428
              }
429
            }
430
          }
431
          /**
432
           * Add a node for a self-intersection.
433
           * If the node is a potential boundary node (e.g. came from an edge which
434
           * is a boundary) then insert it as a potential boundary node.
435
           * Otherwise, just add it as a regular node.
436
           */
437
          private void addSelfIntersectionNode(int argIndex, Coordinate coord, int loc)
438
          {
439
            // if this node is already a boundary node, don't change it
440
            if (isBoundaryNode(argIndex, coord)) return;
441
            if (loc == Location.BOUNDARY && useBoundaryDeterminationRule)
442
                insertBoundaryPoint(argIndex, coord);
443
            else
444
              insertPoint(argIndex, coord, loc);
445
          }
446

    
447
          private void insertPoint(int argIndex, Coordinate coord, int onLocation)
448
          {
449
            Node n = nodes.addNode(coord);
450
            Label lbl = n.getLabel();
451
            if (lbl == null) {
452
              n.label = new Label(argIndex, onLocation);
453
            }
454
            else
455
              lbl.setLocation(argIndex, onLocation);
456
          }
457
          
458
          /**
459
           * Adds points using the mod-2 rule of SFS.  This is used to add the boundary
460
           * points of dim-1 geometries (Curves/MultiCurves).  According to the SFS,
461
           * an endpoint of a Curve is on the boundary
462
           * iff if it is in the boundaries of an odd number of Geometries
463
           */
464
          private void insertBoundaryPoint(int argIndex, Coordinate coord)
465
          {
466
            Node n = nodes.addNode(coord);
467
            Label lbl = n.getLabel();
468
            // the new point to insert is on a boundary
469
            int boundaryCount = 1;
470
            // determine the current location for the point (if any)
471
            int loc = Location.NONE;
472
            if (lbl != null) loc = lbl.getLocation(argIndex, Position.ON);
473
            if (loc == Location.BOUNDARY) boundaryCount++;
474

    
475
            // determine the boundary status of the point according to the Boundary Determination Rule
476
            int newLoc = determineBoundary(boundaryCount);
477
            lbl.setLocation(argIndex, newLoc);
478
          }
479
          
480
          
481
          public void computeSplitEdges(List edgelist)
482
          {
483
            for (Iterator i = edges.iterator(); i.hasNext(); ) {
484
              SnappingEdge e = (SnappingEdge) i.next();
485
              e.getEdgeIntersectionList().addSplitEdges(edgelist);
486
            }
487
          }
488
          
489
          /**
490
           * Borra las intersecciones registradas en los nodos
491
           * (util de cara a reutilizar un GeometryGraph en multiples intersecciones)
492
           * */
493
          public void clearIntersections(){
494
                  for (Iterator i = edges.iterator(); i.hasNext(); ) {
495
                      SnappingEdge e = (SnappingEdge) i.next();
496
                      e.clearIntersections();
497
                    }
498
          }
499
}