Statistics
| Revision:

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

History | View | Annotate | Download (14.4 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.iver.cit.gvsig.fmap.topology.geomgraph.SnappingEdge;
15
import com.iver.cit.gvsig.fmap.topology.geomgraph.SnappingNodeMap;
16
import com.vividsolutions.jts.algorithm.CGAlgorithms;
17
import com.vividsolutions.jts.algorithm.LineIntersector;
18
import com.vividsolutions.jts.geom.*;
19
import com.vividsolutions.jts.geomgraph.index.SegmentIntersector;
20
import com.vividsolutions.jts.geomgraph.index.SimpleMCSweepLineIntersector;
21
import com.vividsolutions.jts.geomgraph.index.SnapSimpleMCSweepLineIntersector;
22
import com.vividsolutions.jts.util.Assert;
23

    
24
/**
25
 */
26
public class SnappingGeometryGraph extends GeometryGraph {
27

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

    
32
        protected SnappingNodeMap nodes;
33

    
34

    
35
        // overwrite to catch them when returned by Snapping map
36
        protected Collection boundaryNodes;
37

    
38
        protected int argIndex;
39

    
40
        private boolean useBoundaryDeterminationRule = false;
41

    
42
        private boolean hasTooFewPoints = false;
43

    
44
        private Coordinate invalidPoint;
45

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

    
52
        public SnappingGeometryGraph(NodeFactory nodeFact, double tolerance,
53
                        int argIndex, Geometry parentGeometry) {
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 void dumpNodes(){
64
                this.nodes.dump();
65
        }
66

    
67
        public SnappingGeometryGraph(double tolerance, int argIndex, Geometry parent) {
68
                super(argIndex, new GeometryCollection(new Geometry[0], parent.getFactory()));
69
                nodes = new SnappingNodeMap(new NodeFactory(), tolerance);
70
                this.argIndex = argIndex;
71
                this.snapTolerance = tolerance;
72
                add(parent);
73
        }
74

    
75
        public Collection getBoundaryNodes() {
76
                if (boundaryNodes == null)
77
                        boundaryNodes = nodes.getBoundaryNodes(argIndex);
78
                return boundaryNodes;
79
        }
80

    
81
        public Coordinate[] getBoundaryPoints() {
82
                Collection coll = getBoundaryNodes();
83
                Coordinate[] pts = new Coordinate[coll.size()];
84
                int i = 0;
85
                for (Iterator it = coll.iterator(); it.hasNext();) {
86
                        Node node = (Node) it.next();
87
                        pts[i++] = (Coordinate) node.getCoordinate().clone();
88
                }
89
                return pts;
90
        }
91

    
92
        public Iterator getNodeIterator() {
93
                return nodes.iterator();
94
        }
95

    
96
        public Collection getNodes() {
97
                return nodes.values();
98
        }
99

    
100
        public Node addNode(Node node) {
101
                return nodes.addNode(node);
102
        }
103

    
104
        public Node addNode(Coordinate coord) {
105
                return nodes.addNode(coord);
106
        }
107

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

    
125
        
126

    
127
        public void add(EdgeEnd e) {
128
                nodes.add(e);
129
                edgeEndList.add(e);
130
        }
131
        
132
        
133

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

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

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

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

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

    
198
                        Coordinate[] eCoord = e.getCoordinates();
199
                        if (matchInSameDirection(p0, p1, eCoord[0], eCoord[1]))
200
                                return e;
201

    
202
                        if (matchInSameDirection(p0, p1, eCoord[eCoord.length - 1],
203
                                        eCoord[eCoord.length - 2]))
204
                                return e;
205
                }
206
                return null;
207
        }
208

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

    
219
                if (CGAlgorithms.computeOrientation(p0, p1, ep1) == CGAlgorithms.COLLINEAR
220
                                && Quadrant.quadrant(p0, p1) == Quadrant.quadrant(ep0, ep1))
221
                        return true;
222
                return false;
223
        }
224

    
225
        
226
        private void add(Geometry g)
227
          {
228
            if (g.isEmpty()) return;
229

    
230
            // check if this Geometry should obey the Boundary Determination Rule
231
            // all collections except MultiPolygons obey the rule
232
            if (g instanceof GeometryCollection
233
                && ! (g instanceof MultiPolygon))
234
                    useBoundaryDeterminationRule = true;
235

    
236
            if (g instanceof Polygon)                 addPolygon((Polygon) g);
237
                                // LineString also handles LinearRings
238
            else if (g instanceof LineString)         addLineString((LineString) g);
239
            else if (g instanceof Point)              addPoint((Point) g);
240
            else if (g instanceof MultiPoint)         addCollection((MultiPoint) g);
241
            else if (g instanceof MultiLineString)    addCollection((MultiLineString) g);
242
            else if (g instanceof MultiPolygon)       addCollection((MultiPolygon) g);
243
            else if (g instanceof GeometryCollection) addCollection((GeometryCollection) g);
244
            else  throw new UnsupportedOperationException(g.getClass().getName());
245
          }
246

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

    
271
            if (coord.length < 4) {
272
              hasTooFewPoints = true;
273
              invalidPoint = coord[0];
274
              return;
275
            }
276

    
277
            int left  = cwLeft;
278
            int right = cwRight;
279
            if (cga.isCCW(coord)) {
280
              left = cwRight;
281
              right = cwLeft;
282
            }
283
            SnappingEdge e = new SnappingEdge(coord,
284
                                new Label(argIndex, Location.BOUNDARY, left, right), this.snapTolerance);
285
            lineEdgeMap.put(lr, e);
286

    
287
            insertEdge(e);
288
            // insert the endpoint as a node, to mark that it is on the boundary
289
            insertPoint(argIndex, coord[0], Location.BOUNDARY);
290
          }
291

    
292
          private void addPolygon(Polygon p)
293
          {
294
            addPolygonRing(
295
                    (LinearRing) p.getExteriorRing(),
296
                    Location.EXTERIOR,
297
                    Location.INTERIOR);
298

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

    
310
          private void addLineString(LineString line)
311
          {
312
            Coordinate[] coord = CoordinateArrays.removeRepeatedPoints(line.getCoordinates());
313

    
314
            if (coord.length < 2) {
315
              hasTooFewPoints = true;
316
              invalidPoint = coord[0];
317
              return;
318
            }
319

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

    
334
          }
335

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

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

    
385

    
386
          
387
          public SegmentIntersector computeEdgeIntersections(
388
            GeometryGraph g,
389
            LineIntersector li,
390
            boolean includeProper)
391
          {
392
            SegmentIntersector si = new SegmentIntersector(li, includeProper, true);
393
            si.setBoundaryNodes(this.getBoundaryNodes(), g.getBoundaryNodes());
394

    
395
            SnapSimpleMCSweepLineIntersector esi = new SnapSimpleMCSweepLineIntersector();
396
            esi.computeIntersections(edges, g.edges, si);
397
            return si;
398
          }
399

    
400
         
401
         
402

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

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

    
458
            // determine the boundary status of the point according to the Boundary Determination Rule
459
            int newLoc = determineBoundary(boundaryCount);
460
            lbl.setLocation(argIndex, newLoc);
461
          }
462
          
463
          
464
          public void computeSplitEdges(List edgelist)
465
          {
466
            for (Iterator i = edges.iterator(); i.hasNext(); ) {
467
              SnappingEdge e = (SnappingEdge) i.next();
468
              e.getEdgeIntersectionList().addSplitEdges(edgelist);
469
            }
470
          }
471
}