Statistics
| Revision:

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

History | View | Annotate | Download (15.5 KB)

1 9178 azabala
/*
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 13685 azabala
import com.vividsolutions.jts.algorithm.BoundaryNodeRule;
14 9178 azabala
import com.vividsolutions.jts.algorithm.CGAlgorithms;
15
import com.vividsolutions.jts.algorithm.LineIntersector;
16 10627 caballero
import com.vividsolutions.jts.geom.Coordinate;
17
import com.vividsolutions.jts.geom.CoordinateArrays;
18
import com.vividsolutions.jts.geom.Geometry;
19
import com.vividsolutions.jts.geom.GeometryCollection;
20
import com.vividsolutions.jts.geom.LineString;
21
import com.vividsolutions.jts.geom.LinearRing;
22
import com.vividsolutions.jts.geom.Location;
23
import com.vividsolutions.jts.geom.MultiLineString;
24
import com.vividsolutions.jts.geom.MultiPoint;
25
import com.vividsolutions.jts.geom.MultiPolygon;
26
import com.vividsolutions.jts.geom.Point;
27
import com.vividsolutions.jts.geom.Polygon;
28 9178 azabala
import com.vividsolutions.jts.geomgraph.index.SegmentIntersector;
29
import com.vividsolutions.jts.geomgraph.index.SnapSimpleMCSweepLineIntersector;
30
import com.vividsolutions.jts.util.Assert;
31
32
/**
33
 */
34
public class SnappingGeometryGraph extends GeometryGraph {
35
36
        // Properties of PlanarGraph that we want to overwrite to
37
        // use snapping
38
        public static final CGAlgorithms cga = new CGAlgorithms();
39
40
        protected SnappingNodeMap nodes;
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 13685 azabala
        private Map lineEdgeMap = new HashMap();
54
55 9178 azabala
        private Geometry parentGeometry;
56 13685 azabala
57
        private BoundaryNodeRule boundaryNodeRule = null;
58
59 9178 azabala
        double snapTolerance;
60
61
        public SnappingGeometryGraph(NodeFactory nodeFact, double tolerance,
62 13685 azabala
                        int argIndex, BoundaryNodeRule boundaryNodeRule,
63
                        Geometry parentGeometry) {
64
                // le pasamos al constructor padre GeometryCollection vacia para que
65
                // no replique la construccion del grafo
66
                super(argIndex, new GeometryCollection(new Geometry[0], parentGeometry
67
                                .getFactory()), boundaryNodeRule);
68 9178 azabala
                nodes = new SnappingNodeMap(nodeFact, tolerance);
69
                this.argIndex = argIndex;
70
                this.parentGeometry = parentGeometry;
71
                this.snapTolerance = tolerance;
72 13685 azabala
                this.boundaryNodeRule = boundaryNodeRule;
73 9178 azabala
                add(parentGeometry);
74
        }
75 13685 azabala
76
        public SnappingGeometryGraph(double tolerance, int argIndex, Geometry parent) {
77
                this(new NodeFactory(), tolerance, argIndex, parent);
78
        }
79
80
        public SnappingGeometryGraph(NodeFactory nodeFact, double tolerance,
81
                        int argIndex, Geometry parentGeometry) {
82
                this(nodeFact, tolerance, argIndex,
83
                                BoundaryNodeRule.OGC_SFS_BOUNDARY_RULE, parentGeometry);
84
        }
85
86
        public Geometry getGeometry() {
87 9178 azabala
                return this.parentGeometry;
88
        }
89 13685 azabala
90
        public void dumpNodes() {
91 9178 azabala
                this.nodes.dump();
92
        }
93
94
        public Collection getBoundaryNodes() {
95
                if (boundaryNodes == null)
96
                        boundaryNodes = nodes.getBoundaryNodes(argIndex);
97
                return boundaryNodes;
98
        }
99
100
        public Coordinate[] getBoundaryPoints() {
101
                Collection coll = getBoundaryNodes();
102
                Coordinate[] pts = new Coordinate[coll.size()];
103
                int i = 0;
104
                for (Iterator it = coll.iterator(); it.hasNext();) {
105
                        Node node = (Node) it.next();
106
                        pts[i++] = (Coordinate) node.getCoordinate().clone();
107
                }
108
                return pts;
109
        }
110
111
        public Iterator getNodeIterator() {
112
                return nodes.iterator();
113
        }
114
115
        public Collection getNodes() {
116
                return nodes.values();
117
        }
118
119
        public Node addNode(Node node) {
120
                return nodes.addNode(node);
121
        }
122
123
        public Node addNode(Coordinate coord) {
124
                return nodes.addNode(coord);
125
        }
126
127
        /**
128
         * @return the node if found; null otherwise
129
         */
130
        public Node find(Coordinate coord) {
131
                return nodes.find(coord);
132
        }
133 13685 azabala
134 9178 azabala
        public boolean isBoundaryNode(int geomIndex, Coordinate coord) {
135
                Node node = nodes.find(coord);
136
                if (node == null)
137
                        return false;
138
                Label label = node.getLabel();
139
                if (label != null && label.getLocation(geomIndex) == Location.BOUNDARY)
140
                        return true;
141
                return false;
142
        }
143
144
        public void add(EdgeEnd e) {
145
                nodes.add(e);
146
                edgeEndList.add(e);
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 13685 azabala
        private void add(Geometry g) {
241
                if (g.isEmpty())
242
                        return;
243 9178 azabala
244 13685 azabala
                // check if this Geometry should obey the Boundary Determination Rule
245
                // all collections except MultiPolygons obey the rule
246
                if (g instanceof GeometryCollection && !(g instanceof MultiPolygon))
247
                        useBoundaryDeterminationRule = true;
248 9178 azabala
249 13685 azabala
                if (g instanceof Polygon)
250
                        addPolygon((Polygon) g);
251
                // LineString also handles LinearRings
252
                else if (g instanceof LineString)
253
                        addLineString((LineString) g);
254
                else if (g instanceof Point)
255
                        addPoint((Point) g);
256
                else if (g instanceof MultiPoint)
257
                        addCollection((MultiPoint) g);
258
                else if (g instanceof MultiLineString)
259
                        addCollection((MultiLineString) g);
260
                else if (g instanceof MultiPolygon)
261
                        addCollection((MultiPolygon) g);
262
                else if (g instanceof GeometryCollection)
263
                        addCollection((GeometryCollection) g);
264
                else
265
                        throw new UnsupportedOperationException(g.getClass().getName());
266
        }
267 9178 azabala
268 13685 azabala
        private void addCollection(GeometryCollection gc) {
269
                for (int i = 0; i < gc.getNumGeometries(); i++) {
270
                        Geometry g = gc.getGeometryN(i);
271
                        add(g);
272
                }
273
        }
274 9178 azabala
275 13685 azabala
        /**
276
         * Add a Point to the graph.
277
         */
278
        private void addPoint(Point p) {
279
                Coordinate coord = p.getCoordinate();
280
                insertPoint(argIndex, coord, Location.INTERIOR);
281
        }
282 9178 azabala
283 13685 azabala
        /**
284
         * The left and right topological location arguments assume that the ring is
285
         * oriented CW. If the ring is in the opposite orientation, the left and
286
         * right locations must be interchanged.
287
         */
288
        private void addPolygonRing(LinearRing lr, int cwLeft, int cwRight) {
289
                Coordinate[] coord = CoordinateArrays.removeRepeatedPoints(lr
290
                                .getCoordinates());
291 9178 azabala
292 13685 azabala
                if (coord.length < 4) {
293
                        hasTooFewPoints = true;
294
                        invalidPoint = coord[0];
295
                        return;
296
                }
297 9178 azabala
298 13685 azabala
                int left = cwLeft;
299
                int right = cwRight;
300
                if (CGAlgorithms.isCCW(coord)) {
301
                        left = cwRight;
302
                        right = cwLeft;
303
                }
304
                SnappingEdge e = new SnappingEdge(coord, new Label(argIndex,
305
                                Location.BOUNDARY, left, right), this.snapTolerance);
306
                lineEdgeMap.put(lr, e);
307 9178 azabala
308 13685 azabala
                insertEdge(e);
309
                // insert the endpoint as a node, to mark that it is on the boundary
310
                insertPoint(argIndex, coord[0], Location.BOUNDARY);
311
        }
312 9178 azabala
313 13685 azabala
        private void addPolygon(Polygon p) {
314
                addPolygonRing((LinearRing) p.getExteriorRing(), Location.EXTERIOR,
315
                                Location.INTERIOR);
316 9178 azabala
317 13685 azabala
                for (int i = 0; i < p.getNumInteriorRing(); i++) {
318
                        // Holes are topologically labelled opposite to the shell, since
319
                        // the interior of the polygon lies on their opposite side
320
                        // (on the left, if the hole is oriented CW)
321
                        addPolygonRing((LinearRing) p.getInteriorRingN(i),
322
                                        Location.INTERIOR, Location.EXTERIOR);
323
                }
324
        }
325 9178 azabala
326 13685 azabala
        private void addLineString(LineString line) {
327
                Coordinate[] coord = CoordinateArrays.removeRepeatedPoints(line
328
                                .getCoordinates());
329 9178 azabala
330 13685 azabala
                if (coord.length < 2) {
331
                        hasTooFewPoints = true;
332
                        invalidPoint = coord[0];
333
                        return;
334
                }
335 9178 azabala
336 13685 azabala
                // add the edge for the LineString
337
                // line edges do not have locations for their left and right sides
338
                SnappingEdge e = new SnappingEdge(coord, new Label(argIndex,
339
                                Location.INTERIOR), this.snapTolerance);
340
                lineEdgeMap.put(line, e);
341
                insertEdge(e);
342
                /**
343
                 * Add the boundary points of the LineString, if any. Even if the
344
                 * LineString is closed, add both points as if they were endpoints. This
345
                 * allows for the case that the node already exists and is a boundary
346
                 * point.
347
                 */
348
                Assert.isTrue(coord.length >= 2, "found LineString with single point");
349
                insertBoundaryPoint(argIndex, coord[0]);
350
                insertBoundaryPoint(argIndex, coord[coord.length - 1]);
351 9178 azabala
352 13685 azabala
        }
353 9178 azabala
354 13685 azabala
        /**
355
         * Add an Edge computed externally. The label on the Edge is assumed to be
356
         * correct.
357
         */
358
        public void addEdge(Edge e) {
359
                insertEdge(e);
360
                Coordinate[] coord = e.getCoordinates();
361
                // insert the endpoint as a node, to mark that it is on the boundary
362
                insertPoint(argIndex, coord[0], Location.BOUNDARY);
363
                insertPoint(argIndex, coord[coord.length - 1], Location.BOUNDARY);
364
        }
365 9178 azabala
366 13685 azabala
        /**
367
         * Add a point computed externally. The point is assumed to be a Point
368
         * Geometry part, which has a location of INTERIOR.
369
         */
370
        public void addPoint(Coordinate pt) {
371
                insertPoint(argIndex, pt, Location.INTERIOR);
372
        }
373 9178 azabala
374 13685 azabala
        /**
375
         * Compute self-nodes, taking advantage of the Geometry type to minimize the
376
         * number of intersection tests. (E.g. rings are not tested for
377
         * self-intersection, since they are assumed to be valid).
378
         *
379
         * @param li
380
         *            the LineIntersector to use
381
         * @param computeRingSelfNodes
382
         *            if <false>, intersection checks are optimized to not test
383
         *            rings for self-intersection
384
         * @return the SegmentIntersector used, containing information about the
385
         *         intersections found
386
         */
387
        public SegmentIntersector computeSelfNodes(LineIntersector li,
388
                        boolean computeRingSelfNodes) {
389
                SegmentIntersector si = new SegmentIntersector(li, true, false);
390
                SnapSimpleMCSweepLineIntersector esi = new SnapSimpleMCSweepLineIntersector();
391
                // optimized test for Polygons and Rings
392
                if (!computeRingSelfNodes
393
                                && (parentGeometry instanceof LinearRing
394
                                                || parentGeometry instanceof Polygon || parentGeometry instanceof MultiPolygon)) {
395
                        esi.computeIntersections(edges, si, false);
396
                } else {
397
                        esi.computeIntersections(edges, si, true);
398
                }
399
                addSelfIntersectionNodes(argIndex);
400
                return si;
401
        }
402 9178 azabala
403 13685 azabala
        public SegmentIntersector computeEdgeIntersections(GeometryGraph g,
404
                        LineIntersector li, boolean includeProper) {
405
                SegmentIntersector siSnap = new SegmentIntersector(li, includeProper,
406
                                true);
407
                siSnap.setBoundaryNodes(this.getBoundaryNodes(), g.getBoundaryNodes());
408 9178 azabala
409 13685 azabala
                SnapSimpleMCSweepLineIntersector esiSnap = new SnapSimpleMCSweepLineIntersector();
410
                esiSnap.computeIntersections(edges, g.edges, siSnap);
411
                return siSnap;
412 9178 azabala
413 13685 azabala
                // return super.computeEdgeIntersections(g, li, includeProper);
414
        }
415 9178 azabala
416 13685 azabala
        private void addSelfIntersectionNodes(int argIndex) {
417
                for (Iterator i = edges.iterator(); i.hasNext();) {
418
                        Edge e = (Edge) i.next();
419
                        int eLoc = e.getLabel().getLocation(argIndex);
420
                        for (Iterator eiIt = e.eiList.iterator(); eiIt.hasNext();) {
421
                                EdgeIntersection ei = (EdgeIntersection) eiIt.next();
422
                                addSelfIntersectionNode(argIndex, ei.coord, eLoc);
423
                        }
424
                }
425
        }
426
427
        /**
428
         * Add a node for a self-intersection. If the node is a potential boundary
429
         * node (e.g. came from an edge which is a boundary) then insert it as a
430
         * potential boundary node. Otherwise, just add it as a regular node.
431
         */
432
        private void addSelfIntersectionNode(int argIndex, Coordinate coord, int loc) {
433
                // if this node is already a boundary node, don't change it
434
                if (isBoundaryNode(argIndex, coord))
435
                        return;
436
                if (loc == Location.BOUNDARY && useBoundaryDeterminationRule)
437
                        insertBoundaryPoint(argIndex, coord);
438
                else
439
                        insertPoint(argIndex, coord, loc);
440
        }
441
442
        private void insertPoint(int argIndex, Coordinate coord, int onLocation) {
443
                Node n = nodes.addNode(coord);
444
                Label lbl = n.getLabel();
445
                if (lbl == null) {
446
                        n.label = new Label(argIndex, onLocation);
447
                } else
448
                        lbl.setLocation(argIndex, onLocation);
449
        }
450
451
        /**
452
         * Adds points using the mod-2 rule of SFS. This is used to add the boundary
453
         * points of dim-1 geometries (Curves/MultiCurves). According to the SFS, an
454
         * endpoint of a Curve is on the boundary iff if it is in the boundaries of
455
         * an odd number of Geometries
456
         */
457
458
        // JTS 1.7 VERSION
459
        // private void insertBoundaryPoint(int argIndex, Coordinate coord)
460
        // {
461
        // Node n = nodes.addNode(coord);
462
        // Label lbl = n.getLabel();
463
        // // the new point to insert is on a boundary
464
        // int boundaryCount = 1;
465
        // // determine the current location for the point (if any)
466
        // int loc = Location.NONE;
467
        // if (lbl != null) loc = lbl.getLocation(argIndex, Position.ON);
468
        // if (loc == Location.BOUNDARY) boundaryCount++;
469
        //
470
        // // determine the boundary status of the point according to the Boundary
471
        // Determination Rule
472
        // int newLoc = determineBoundary(boundaryCount);
473
        // lbl.setLocation(argIndex, newLoc);
474
        // }
475
        private void insertBoundaryPoint(int argIndex, Coordinate coord) {
476
                Node n = nodes.addNode(coord);
477
                Label lbl = n.getLabel();
478
                // the new point to insert is on a boundary
479
                int boundaryCount = 1;
480
                // determine the current location for the point (if any)
481
                int loc = Location.NONE;
482
                if (lbl != null)
483
                        loc = lbl.getLocation(argIndex, Position.ON);
484
                if (loc == Location.BOUNDARY)
485
                        boundaryCount++;
486
487
                // determine the boundary status of the point according to the Boundary
488
                // Determination Rule
489
                int newLoc = determineBoundary(boundaryNodeRule, boundaryCount);
490
                lbl.setLocation(argIndex, newLoc);
491
        }
492
493
        public void computeSplitEdges(List edgelist) {
494
                for (Iterator i = edges.iterator(); i.hasNext();) {
495
                        SnappingEdge e = (SnappingEdge) i.next();
496
                        e.getEdgeIntersectionList().addSplitEdges(edgelist);
497
                }
498
        }
499
500
        /**
501
         * Borra las intersecciones registradas en los nodos (util de cara a
502
         * reutilizar un GeometryGraph en multiples intersecciones)
503
         */
504
        public void clearIntersections() {
505
                for (Iterator i = edges.iterator(); i.hasNext();) {
506
                        SnappingEdge e = (SnappingEdge) i.next();
507
                        e.clearIntersections();
508
                }
509
        }
510 9178 azabala
}