Statistics
| Revision:

svn-gvsig-desktop / trunk / libraries / libFMap / src / com / vividsolutions / jts / geomgraph / SnappingGeometryGraph.java @ 9178

History | View | Annotate | Download (14.9 KB)

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

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

    
14
import com.vividsolutions.jts.algorithm.CGAlgorithms;
15
import com.vividsolutions.jts.algorithm.LineIntersector;
16
import com.vividsolutions.jts.geom.*;
17
import com.vividsolutions.jts.geomgraph.index.SegmentIntersector;
18
import com.vividsolutions.jts.geomgraph.index.SimpleMCSweepLineIntersector;
19
import com.vividsolutions.jts.geomgraph.index.SnapSimpleMCSweepLineIntersector;
20
import com.vividsolutions.jts.util.Assert;
21

    
22
/**
23
 */
24
public class SnappingGeometryGraph extends GeometryGraph {
25

    
26
        // Properties of PlanarGraph that we want to overwrite to
27
        // use snapping
28
        public static final CGAlgorithms cga = new CGAlgorithms();
29

    
30
        protected SnappingNodeMap nodes;
31

    
32

    
33
        // overwrite to catch them when returned by Snapping map
34
        protected Collection boundaryNodes;
35

    
36
        protected int argIndex;
37

    
38
        private boolean useBoundaryDeterminationRule = false;
39

    
40
        private boolean hasTooFewPoints = false;
41

    
42
        private Coordinate invalidPoint;
43

    
44
        private Map lineEdgeMap = new HashMap(); 
45
        
46
        private Geometry parentGeometry;
47
        
48
        double snapTolerance;
49

    
50
        public SnappingGeometryGraph(NodeFactory nodeFact, double tolerance,
51
                        int argIndex, Geometry parentGeometry) {
52
                //le pasamos al constructor padre GeometryCollection vacia para que
53
                //no replique la construccion del grafo
54
                super(argIndex, new GeometryCollection(new Geometry[0], parentGeometry.getFactory()));
55
                nodes = new SnappingNodeMap(nodeFact, tolerance);
56
                this.argIndex = argIndex;
57
                this.parentGeometry = parentGeometry;
58
                this.snapTolerance = tolerance;
59
                add(parentGeometry);
60
        }
61
        
62
        
63
        public Geometry getGeometry(){
64
                return this.parentGeometry;
65
        }
66
        
67
        
68
        public void dumpNodes(){
69
                this.nodes.dump();
70
        }
71

    
72
        public SnappingGeometryGraph(double tolerance, int argIndex, Geometry parent) {
73
                super(argIndex, new GeometryCollection(new Geometry[0], parent.getFactory()));
74
                nodes = new SnappingNodeMap(new NodeFactory(), tolerance);
75
                this.argIndex = argIndex;
76
                this.snapTolerance = tolerance;
77
                this.parentGeometry = parent;
78
                add(parent);
79
        }
80

    
81
        public Collection getBoundaryNodes() {
82
                if (boundaryNodes == null)
83
                        boundaryNodes = nodes.getBoundaryNodes(argIndex);
84
                return boundaryNodes;
85
        }
86

    
87
        public Coordinate[] getBoundaryPoints() {
88
                Collection coll = getBoundaryNodes();
89
                Coordinate[] pts = new Coordinate[coll.size()];
90
                int i = 0;
91
                for (Iterator it = coll.iterator(); it.hasNext();) {
92
                        Node node = (Node) it.next();
93
                        pts[i++] = (Coordinate) node.getCoordinate().clone();
94
                }
95
                return pts;
96
        }
97

    
98
        public Iterator getNodeIterator() {
99
                return nodes.iterator();
100
        }
101

    
102
        public Collection getNodes() {
103
                return nodes.values();
104
        }
105

    
106
        public Node addNode(Node node) {
107
                return nodes.addNode(node);
108
        }
109

    
110
        public Node addNode(Coordinate coord) {
111
                return nodes.addNode(coord);
112
        }
113

    
114
        /**
115
         * @return the node if found; null otherwise
116
         */
117
        public Node find(Coordinate coord) {
118
                return nodes.find(coord);
119
        }
120
        
121
        public boolean isBoundaryNode(int geomIndex, Coordinate coord) {
122
                Node node = nodes.find(coord);
123
                if (node == null)
124
                        return false;
125
                Label label = node.getLabel();
126
                if (label != null && label.getLocation(geomIndex) == Location.BOUNDARY)
127
                        return true;
128
                return false;
129
        }
130

    
131
        
132

    
133
        public void add(EdgeEnd e) {
134
                nodes.add(e);
135
                edgeEndList.add(e);
136
        }
137
        
138
        
139

    
140
        /**
141
         * Link the DirectedEdges at the nodes of the graph. This allows clients to
142
         * link only a subset of nodes in the graph, for efficiency (because they
143
         * know that only a subset is of interest).
144
         */
145
        public void linkResultDirectedEdges() {
146
                for (Iterator nodeit = nodes.iterator(); nodeit.hasNext();) {
147
                        Node node = (Node) nodeit.next();
148
                        ((DirectedEdgeStar) node.getEdges()).linkResultDirectedEdges();
149
                }
150
        }
151

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

    
164
        /**
165
         * Returns the EdgeEnd which has edge e as its base edge (MD 18 Feb 2002 -
166
         * this should return a pair of edges)
167
         * 
168
         * @return the edge, if found <code>null</code> if the edge was not found
169
         */
170
        public EdgeEnd findEdgeEnd(Edge e) {
171
                for (Iterator i = getEdgeEnds().iterator(); i.hasNext();) {
172
                        EdgeEnd ee = (EdgeEnd) i.next();
173
                        if (ee.getEdge() == e)
174
                                return ee;
175
                }
176
                return null;
177
        }
178

    
179
        /**
180
         * Returns the edge whose first two coordinates are p0 and p1
181
         * 
182
         * @return the edge, if found <code>null</code> if the edge was not found
183
         */
184
        public Edge findEdge(Coordinate p0, Coordinate p1) {
185
                for (int i = 0; i < edges.size(); i++) {
186
                        Edge e = (Edge) edges.get(i);
187
                        Coordinate[] eCoord = e.getCoordinates();
188
                        if (p0.equals(eCoord[0]) && p1.equals(eCoord[1]))
189
                                return e;
190
                }
191
                return null;
192
        }
193

    
194
        /**
195
         * Returns the edge which starts at p0 and whose first segment is parallel
196
         * to p1
197
         * 
198
         * @return the edge, if found <code>null</code> if the edge was not found
199
         */
200
        public Edge findEdgeInSameDirection(Coordinate p0, Coordinate p1) {
201
                for (int i = 0; i < edges.size(); i++) {
202
                        Edge e = (Edge) edges.get(i);
203

    
204
                        Coordinate[] eCoord = e.getCoordinates();
205
                        if (matchInSameDirection(p0, p1, eCoord[0], eCoord[1]))
206
                                return e;
207

    
208
                        if (matchInSameDirection(p0, p1, eCoord[eCoord.length - 1],
209
                                        eCoord[eCoord.length - 2]))
210
                                return e;
211
                }
212
                return null;
213
        }
214

    
215
        /**
216
         * The coordinate pairs match if they define line segments lying in the same
217
         * direction. E.g. the segments are parallel and in the same quadrant (as
218
         * opposed to parallel and opposite!).
219
         */
220
        private boolean matchInSameDirection(Coordinate p0, Coordinate p1,
221
                        Coordinate ep0, Coordinate ep1) {
222
                if (!p0.equals(ep0))
223
                        return false;
224

    
225
                if (CGAlgorithms.computeOrientation(p0, p1, ep1) == CGAlgorithms.COLLINEAR
226
                                && Quadrant.quadrant(p0, p1) == Quadrant.quadrant(ep0, ep1))
227
                        return true;
228
                return false;
229
        }
230

    
231
        
232
        private void add(Geometry g)
233
          {
234
            if (g.isEmpty()) return;
235

    
236
            // check if this Geometry should obey the Boundary Determination Rule
237
            // all collections except MultiPolygons obey the rule
238
            if (g instanceof GeometryCollection
239
                && ! (g instanceof MultiPolygon))
240
                    useBoundaryDeterminationRule = true;
241

    
242
            if (g instanceof Polygon)                 addPolygon((Polygon) g);
243
                                // LineString also handles LinearRings
244
            else if (g instanceof LineString)         addLineString((LineString) g);
245
            else if (g instanceof Point)              addPoint((Point) g);
246
            else if (g instanceof MultiPoint)         addCollection((MultiPoint) g);
247
            else if (g instanceof MultiLineString)    addCollection((MultiLineString) g);
248
            else if (g instanceof MultiPolygon)       addCollection((MultiPolygon) g);
249
            else if (g instanceof GeometryCollection) addCollection((GeometryCollection) g);
250
            else  throw new UnsupportedOperationException(g.getClass().getName());
251
          }
252

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

    
277
            if (coord.length < 4) {
278
              hasTooFewPoints = true;
279
              invalidPoint = coord[0];
280
              return;
281
            }
282

    
283
            int left  = cwLeft;
284
            int right = cwRight;
285
            if (cga.isCCW(coord)) {
286
              left = cwRight;
287
              right = cwLeft;
288
            }
289
            SnappingEdge e = new SnappingEdge(coord,
290
                                new Label(argIndex, Location.BOUNDARY, left, right), this.snapTolerance);
291
            lineEdgeMap.put(lr, e);
292

    
293
            insertEdge(e);
294
            // insert the endpoint as a node, to mark that it is on the boundary
295
            insertPoint(argIndex, coord[0], Location.BOUNDARY);
296
          }
297

    
298
          private void addPolygon(Polygon p)
299
          {
300
            addPolygonRing(
301
                    (LinearRing) p.getExteriorRing(),
302
                    Location.EXTERIOR,
303
                    Location.INTERIOR);
304

    
305
            for (int i = 0; i < p.getNumInteriorRing(); i++) {
306
              // Holes are topologically labelled opposite to the shell, since
307
              // the interior of the polygon lies on their opposite side
308
              // (on the left, if the hole is oriented CW)
309
              addPolygonRing(
310
                    (LinearRing) p.getInteriorRingN(i),
311
                    Location.INTERIOR,
312
                    Location.EXTERIOR);
313
            }
314
          }
315

    
316
          private void addLineString(LineString line)
317
          {
318
            Coordinate[] coord = CoordinateArrays.removeRepeatedPoints(line.getCoordinates());
319

    
320
            if (coord.length < 2) {
321
              hasTooFewPoints = true;
322
              invalidPoint = coord[0];
323
              return;
324
            }
325

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

    
340
          }
341

    
342
          /**
343
           * Add an Edge computed externally.  The label on the Edge is assumed
344
           * to be correct.
345
           */
346
          public void addEdge(Edge e)
347
          {
348
            insertEdge(e);
349
            Coordinate[] coord = e.getCoordinates();
350
            // insert the endpoint as a node, to mark that it is on the boundary
351
            insertPoint(argIndex, coord[0], Location.BOUNDARY);
352
            insertPoint(argIndex, coord[coord.length - 1], Location.BOUNDARY);
353
          }
354

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

    
391

    
392
          
393
          public SegmentIntersector computeEdgeIntersections(
394
            GeometryGraph g,
395
            LineIntersector li,
396
            boolean includeProper)
397
          {
398
            SegmentIntersector siSnap = new SegmentIntersector(li, includeProper, true);
399
            siSnap.setBoundaryNodes(this.getBoundaryNodes(), g.getBoundaryNodes());
400

    
401
            SnapSimpleMCSweepLineIntersector esiSnap = new SnapSimpleMCSweepLineIntersector();
402
            esiSnap.computeIntersections(edges, g.edges, siSnap);
403
            return siSnap;
404
                  
405
//                  return super.computeEdgeIntersections(g, li, includeProper);
406
          }
407

    
408
         
409
         
410

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

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

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