Statistics
| Revision:

root / trunk / extensions / extGraph_predes / src / com / iver / cit / gvsig / topology / geomgraph / SnappingNodeMap.java @ 8004

History | View | Annotate | Download (16.3 KB)

1
/*
2
 * Created on 11-sep-2006 by azabala
3
 *
4
 */
5
package com.iver.cit.gvsig.topology.geomgraph;
6

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

    
15
import com.iver.cit.gvsig.topology.algorithms.SnapSimplePointInAreaLocator;
16
import com.vividsolutions.jts.geom.Coordinate;
17
import com.vividsolutions.jts.geom.Envelope;
18
import com.vividsolutions.jts.geom.Location;
19
import com.vividsolutions.jts.geom.TopologyException;
20
import com.vividsolutions.jts.geomgraph.DirectedEdge;
21
import com.vividsolutions.jts.geomgraph.DirectedEdgeStar;
22
import com.vividsolutions.jts.geomgraph.Edge;
23
import com.vividsolutions.jts.geomgraph.EdgeEnd;
24
import com.vividsolutions.jts.geomgraph.EdgeEndStar;
25
import com.vividsolutions.jts.geomgraph.GeometryGraph;
26
import com.vividsolutions.jts.geomgraph.Label;
27
import com.vividsolutions.jts.geomgraph.Node;
28
import com.vividsolutions.jts.geomgraph.NodeFactory;
29
import com.vividsolutions.jts.geomgraph.NodeMap;
30
import com.vividsolutions.jts.geomgraph.Position;
31
import com.vividsolutions.jts.util.Assert;
32

    
33
/**
34
 * Repository of nodes of a PlanarGraph that applies a snap tolerance.
35
 * 
36
 * If we ask for a node, and it founds a node in the tolerance distance it
37
 * returns the found node.
38
 * 
39
 * 
40
 * 
41
 * @author alzabord
42
 */
43
public class SnappingNodeMap extends NodeMap {
44

    
45
        // private Quadtree spatialIndex;
46

    
47
        // Esto es una mierda, y va a ir muy lento
48
        // buscar otros mecanismos (indice espacial, o hashmap)
49
        protected HashMap nodeMap;
50

    
51
        private class SnapDirectedEdgeStar extends DirectedEdgeStar {
52

    
53
                /**
54
                 * The location of the point for this star in Geometry i Areas
55
                 */
56
                private int[] ptInAreaLocation = { Location.NONE, Location.NONE };
57

    
58
                private Label label;
59

    
60
                List resultAreaEdgeList = null;
61

    
62
                List getResultAreaEdges() {
63
                        // print(System.out);
64
                        if (resultAreaEdgeList != null)
65
                                return resultAreaEdgeList;
66
                        resultAreaEdgeList = new ArrayList();
67
                        for (Iterator it = iterator(); it.hasNext();) {
68
                                DirectedEdge de = (DirectedEdge) it.next();
69
                                if (de.isInResult() || de.getSym().isInResult())
70
                                        resultAreaEdgeList.add(de);
71
                        }
72
                        return resultAreaEdgeList;
73
                }
74

    
75
                private final int SCANNING_FOR_INCOMING = 1;
76

    
77
                private final int LINKING_TO_OUTGOING = 2;
78

    
79
                private SnapDirectedEdgeStar() {
80
                        super();
81

    
82
                        edgeList = new ArrayList();
83

    
84
                }
85

    
86
                /**
87
                 * Traverse the star of DirectedEdges, linking the included edges
88
                 * together. To link two dirEdges, the <next> pointer for an incoming
89
                 * dirEdge is set to the next outgoing edge.
90
                 * <p>
91
                 * DirEdges are only linked if:
92
                 * <ul>
93
                 * <li>they belong to an area (i.e. they have sides)
94
                 * <li>they are marked as being in the result
95
                 * </ul>
96
                 * <p>
97
                 * Edges are linked in CCW order (the order they are stored). This means
98
                 * that rings have their face on the Right (in other words, the
99
                 * topological location of the face is given by the RHS label of the
100
                 * DirectedEdge)
101
                 * <p>
102
                 * PRECONDITION: No pair of dirEdges are both marked as being in the
103
                 * result
104
                 */
105
                public void linkResultDirectedEdges() {
106
                        // make sure edges are copied to resultAreaEdges list
107
                        List resultEdges = getResultAreaEdges();
108
                        // find first area edge (if any) to start linking at
109
                        DirectedEdge firstOut = null;
110
                        DirectedEdge incoming = null;
111
                        int state = SCANNING_FOR_INCOMING;
112
                        // link edges in CCW order
113
                        for (int i = 0; i < resultAreaEdgeList.size(); i++) {
114
                                DirectedEdge nextOut = (DirectedEdge) resultEdges.get(i);
115
                                DirectedEdge nextIn = nextOut.getSym();
116

    
117
                                // skip de's that we're not interested in
118
                                if (!nextOut.getLabel().isArea())
119
                                        continue;
120

    
121
                                // record first outgoing edge, in order to link the last
122
                                // incoming edge
123
                                if (firstOut == null && nextOut.isInResult())
124
                                        firstOut = nextOut;
125
                                // assert: sym.isInResult() == false, since pairs of dirEdges
126
                                // should have been removed already
127

    
128
                                switch (state) {
129
                                case SCANNING_FOR_INCOMING:
130
                                        if (!nextIn.isInResult())
131
                                                continue;
132
                                        incoming = nextIn;
133
                                        state = LINKING_TO_OUTGOING;
134
                                        break;
135
                                case LINKING_TO_OUTGOING:
136
                                        if (!nextOut.isInResult())
137
                                                continue;
138
                                        incoming.setNext(nextOut);
139
                                        state = SCANNING_FOR_INCOMING;
140
                                        break;
141
                                }
142
                        }// for
143
                        if (state == LINKING_TO_OUTGOING) {
144
                                // Debug.print(firstOut == null, this);
145
                                if (firstOut == null)
146
                                        throw new TopologyException("no outgoing dirEdge found",
147
                                                        getCoordinate());
148
                                // Assert.isTrue(firstOut != null, "no outgoing dirEdge found
149
                                // (at " + getCoordinate() );
150
                                Assert.isTrue(firstOut.isInResult(),
151
                                                "unable to link last incoming dirEdge");
152
                                incoming.setNext(firstOut);
153
                        }
154
                }
155

    
156
                /**
157
                 * Insert an EdgeEnd into the map, and clear the edgeList cache, since
158
                 * the list of edges has now changed
159
                 */
160
                protected void insertEdgeEnd(EdgeEnd e, Object obj) {
161
                        edgeMap.put(e, obj);
162
                        if (edgeList == null)
163
                                edgeList = new ArrayList();
164
                        edgeList.add(e);
165

    
166
                        // Necesitamos que la "estrella" de Ejes asociada a un Nodo conserve
167
                        // siempre un orden antihorario. Por eso, cada vez que se a?ada un
168
                        // nuevo
169
                        // eje solo se inserta en el Map (que mantiene el orden)
170
                        // y la cache se pone a null
171

    
172
                        // edgeList = null;
173
                }
174

    
175
                public Iterator iterator() {
176
                        return getEdges().iterator();
177
                }
178

    
179
                public List getEdges() {
180
                        Collections.sort(edgeList, new Comparator() {
181
                                public int compare(Object arg0, Object arg1) {
182
                                        EdgeEnd e0 = (EdgeEnd) arg0;
183
                                        EdgeEnd e1 = (EdgeEnd) arg1;
184
                                        return e0.compareTo(e1);
185
                                }
186
                        });
187
                        // if (edgeList == null) {
188
                        // edgeList = new ArrayList(edgeMap.values());
189
                        // }
190
                        return edgeList;
191
                }
192

    
193
                public Label getLabel() {
194
                        return label;
195
                }
196

    
197
                void computeEdgeEndLabels() {
198
                        // Compute edge label for each EdgeEnd
199
                        for (Iterator it = iterator(); it.hasNext();) {
200
                                EdgeEnd ee = (EdgeEnd) it.next();
201
                                ee.computeLabel();
202
                        }
203
                }
204

    
205
                void propagateSideLabels(int geomIndex) {
206
                        // Since edges are stored in CCW order around the node,
207
                        // As we move around the ring we move from the right to the left
208
                        // side of the edge
209
                        int startLoc = Location.NONE;
210
                        // initialize loc to location of last L side (if any)
211
                        // System.out.println("finding start location");
212
                        for (Iterator it = iterator(); it.hasNext();) {
213
                                EdgeEnd e = (EdgeEnd) it.next();
214
                                Label label = e.getLabel();
215
                                if (label.isArea(geomIndex)
216
                                                && label.getLocation(geomIndex, Position.LEFT) != Location.NONE)
217
                                        startLoc = label.getLocation(geomIndex, Position.LEFT);
218
                        }
219
                        // no labelled sides found, so no labels to propagate
220
                        if (startLoc == Location.NONE)
221
                                return;
222

    
223
                        int currLoc = startLoc;
224
                        for (Iterator it = iterator(); it.hasNext();) {
225
                                EdgeEnd e = (EdgeEnd) it.next();
226
                                Label label = e.getLabel();
227
                                // set null ON values to be in current location
228
                                if (label.getLocation(geomIndex, Position.ON) == Location.NONE)
229
                                        label.setLocation(geomIndex, Position.ON, currLoc);
230
                                // set side labels (if any)
231
                                // if (label.isArea()) { //ORIGINAL
232
                                if (label.isArea(geomIndex)) {
233
                                        int leftLoc = label.getLocation(geomIndex, Position.LEFT);
234
                                        int rightLoc = label.getLocation(geomIndex, Position.RIGHT);
235
                                        // if there is a right location, that is the next location
236
                                        // to propagate
237
                                        if (rightLoc != Location.NONE) {
238
                                                // Debug.print(rightLoc != currLoc, this);
239
                                                if (rightLoc != currLoc)
240
                                                        throw new TopologyException(
241
                                                                        "side location conflict", e.getCoordinate());
242
                                                if (leftLoc == Location.NONE) {
243
                                                        Assert
244
                                                                        .shouldNeverReachHere("found single null side (at "
245
                                                                                        + e.getCoordinate() + ")");
246
                                                }
247
                                                currLoc = leftLoc;
248
                                        } else {
249
                                                /**
250
                                                 * RHS is null - LHS must be null too. This must be an
251
                                                 * edge from the other geometry, which has no location
252
                                                 * labelling for this geometry. This edge must lie
253
                                                 * wholly inside or outside the other geometry (which is
254
                                                 * determined by the current location). Assign both
255
                                                 * sides to be the current location.
256
                                                 */
257
                                                Assert.isTrue(label.getLocation(geomIndex,
258
                                                                Position.LEFT) == Location.NONE,
259
                                                                "found single null side");
260
                                                label.setLocation(geomIndex, Position.RIGHT, currLoc);
261
                                                label.setLocation(geomIndex, Position.LEFT, currLoc);
262
                                        }
263
                                }
264
                        }
265
                }
266

    
267
                int getLocation(int geomIndex, Coordinate p, GeometryGraph[] geom) {
268
                        // compute location only on demand
269
                        if (ptInAreaLocation[geomIndex] == Location.NONE) {
270
                                ptInAreaLocation[geomIndex] = SnapSimplePointInAreaLocator
271
                                                .locate(p, geom[geomIndex].getGeometry(), snapTolerance);
272
                        }
273
                        return ptInAreaLocation[geomIndex];
274
                }
275

    
276
                public void mergeSymLabels() {
277
                        for (Iterator it = iterator(); it.hasNext();) {
278
                                DirectedEdge de = (DirectedEdge) it.next();
279
                                Label label = de.getLabel();
280
                                Label symLabel = de.getSym().getLabel();
281
                                label.merge(symLabel);
282
                                System.currentTimeMillis();
283
                        }
284
                }
285

    
286
                /**
287
                 * Update incomplete dirEdge labels from the labelling for the node
288
                 */
289
                public void updateLabelling(Label nodeLabel) {
290
                        for (Iterator it = iterator(); it.hasNext();) {
291
                                DirectedEdge de = (DirectedEdge) it.next();
292
                                Label label = de.getLabel();
293
                                label.setAllLocationsIfNull(0, nodeLabel.getLocation(0));
294
                                label.setAllLocationsIfNull(1, nodeLabel.getLocation(1));
295
                        }
296
                }
297

    
298
                public void computeLabelling(GeometryGraph[] geom) {
299
                        computeEdgeEndLabels();
300
                        propagateSideLabels(0);
301
                        propagateSideLabels(1);
302
                        boolean[] hasDimensionalCollapseEdge = { false, false };
303
                        for (Iterator it = iterator(); it.hasNext();) {
304
                                EdgeEnd e = (EdgeEnd) it.next();
305
                                Label label = e.getLabel();
306
                                for (int geomi = 0; geomi < 2; geomi++) {
307
                                        if (label.isLine(geomi)
308
                                                        && label.getLocation(geomi) == Location.BOUNDARY)
309
                                                hasDimensionalCollapseEdge[geomi] = true;
310
                                }
311
                        }
312
                        for (Iterator it = iterator(); it.hasNext();) {
313
                                EdgeEnd e = (EdgeEnd) it.next();
314
                                Label label = e.getLabel();
315
                                for (int geomi = 0; geomi < 2; geomi++) {
316
                                        if (label.isAnyNull(geomi)) {
317
                                                int loc = Location.NONE;
318
                                                if (hasDimensionalCollapseEdge[geomi]) {
319
                                                        loc = Location.EXTERIOR;
320
                                                } else {
321
                                                        Coordinate p = e.getCoordinate();
322
                                                        loc = getLocation(geomi, p, geom);
323
                                                }
324
                                                label.setAllLocationsIfNull(geomi, loc);
325
                                        }// if
326
                                }// for
327
                        }// for
328

    
329
                        // determine the overall labelling for this DirectedEdgeStar
330
                        // (i.e. for the node it is based at)
331
                        label = new Label(Location.NONE);
332
                        for (Iterator it = iterator(); it.hasNext();) {
333
                                EdgeEnd ee = (EdgeEnd) it.next();
334
                                Edge e = ee.getEdge();
335
                                Label eLabel = e.getLabel();
336
                                for (int i = 0; i < 2; i++) {
337
                                        int eLoc = eLabel.getLocation(i);
338
                                        if (eLoc == Location.INTERIOR || eLoc == Location.BOUNDARY)
339
                                                label.setLocation(i, Location.INTERIOR);
340
                                }
341
                        }
342
                }
343
        }
344

    
345
        class SnapNode extends Node {
346

    
347
                public SnapNode(Coordinate arg0, EdgeEndStar arg1) {
348
                        super(arg0, arg1);
349
                }
350

    
351
                public boolean equals(Object obj) {
352
                        SnapNode other = (SnapNode) obj;
353
                        return other.coord.distance(this.coord) <= snapTolerance;
354
                }
355

    
356
                public int hashCode() {
357
                        return 1; // esto no es eficiente
358
                }
359
        }
360

    
361
        private double snapTolerance;
362

    
363
        public SnappingNodeMap(NodeFactory nodeFactory, double snapTolerance) {
364
                super(nodeFactory);
365
                this.snapTolerance = snapTolerance;
366
                // spatialIndex = new Quadtree();
367
                nodeMap = new HashMap();
368
        }
369

    
370
        class MinDistCoordComparator implements Comparator {
371
                Coordinate coord;
372

    
373
                MinDistCoordComparator(Coordinate coord) {
374
                        this.coord = coord;
375
                }
376

    
377
                public int compare(Object arg0, Object arg1) {
378
                        Coordinate c1 = ((Node) arg0).getCoordinate();
379
                        Coordinate c2 = ((Node) arg1).getCoordinate();
380

    
381
                        double d1 = c1.distance(coord);
382
                        double d2 = c2.distance(coord);
383

    
384
                        if (d1 < d2)
385
                                return 1;
386
                        if (d1 > d2)
387
                                return -1;
388
                        else
389
                                return 0;
390
                }
391
        }
392

    
393
        private Envelope getQueryRect(Coordinate coord) {
394
                double xmin = coord.x - snapTolerance;
395
                double ymin = coord.y - snapTolerance;
396
                double xmax = coord.x + snapTolerance;
397
                double ymax = coord.y + snapTolerance;
398
                Envelope queryRect = new Envelope(xmin, xmax, ymin, ymax);
399
                return queryRect;
400
        }
401

    
402
        public Node addNode(final Coordinate coord) {
403
                SnapNode candidate = null;
404
                DirectedEdgeStar edgeStar = new SnapDirectedEdgeStar();
405
                candidate = new SnapNode(coord, edgeStar);
406

    
407
                /*
408
                 * PRUEBA PARA VER POR QU? DA ERRORES CON POLIGONOS
409
                 * 
410
                 * 
411
                 * 
412
                 */
413

    
414
                Node stored = (Node) nodeMap.get(candidate);
415
                if (stored != null)
416
                        return stored;
417
                else {
418
                        nodeMap.put(candidate, candidate);
419
                        return candidate;
420
                }
421
                // Envelope queryRect = getQueryRect(coord);
422
                // List nodes = spatialIndex.query(queryRect);
423
                // Spatial Index est? devolviendo candidatos fuera del rectangulo
424
                // ser? problema del quadtree
425
                // if(nodes.size() != 0){
426
                // Collections.sort(nodes, new MinDistCoordComparator(coord));
427
                // Node candidate = (Node) nodes.get(0);
428
                // if(candidate.getCoordinate().distance(coord) < snapTolerance )
429
                // return candidate;
430
                // }else{
431
                // System.out.println("El nodo "+coord.x+","+coord.y+" no tiene entradas
432
                // analogas en el quadtree");
433
                // List all = this.spatialIndex.queryAll();
434
                // System.out.println("en el quadtree "+all.size());
435
                // for(int i = 0; i < all.size(); i++){
436
                // Node node = (Node) all.get(i);
437
                // Coordinate coord2 = node.getCoordinate();
438
                // System.out.println(coord2.x + "," + coord2.y);
439
                // }
440
                // }
441
                // Node solution = nodeFactory.createNode(coord);
442
                // spatialIndex.insert(queryRect, solution);
443
                // return solution;
444
        }
445

    
446
        // FIXME: REVISAR SI HABR?A QUE HACER UN MERGE LABEL ARRIBA O AQU?
447
        public Node addNode(Node n) {
448

    
449
                Node node = addNode(n.getCoordinate());
450
                node.mergeLabel(n);
451
                return node;
452
        }
453

    
454
        /**
455
         * Adds a node for the start point of this EdgeEnd (if one does not already
456
         * exist in this map). Adds the EdgeEnd to the (possibly new) node.
457
         */
458
        public void add(EdgeEnd e) {
459
                Coordinate p = e.getCoordinate();
460
                System.out.println("Verificando nodo " + p.toString());
461
                Node n = addNode(p);
462
                System.out.println("antes de a?adir...");
463
                List list = n.getEdges().getEdges();
464
                System.out.println(list.size());
465
                for (int i = 0; i < list.size(); i++) {
466
                        EdgeEnd ee = (EdgeEnd) list.get(i);
467
                        System.out.println("edge" + i);
468
                        Coordinate[] coords = ee.getEdge().getCoordinates();
469
                        String ctx = "(";
470
                        for (int j = 0; j < coords.length; j++) {
471
                                ctx += coords[j].x + " " + coords[j].y + ",";
472
                        }
473
                        ctx += ")";
474
                        System.out.println(ctx);
475
                }
476
                System.out.println("despues de a?adir...");
477
                n.add(e);// Si el nodo ya existe, se le a?ade una arista
478
                list = n.getEdges().getEdges();
479
                System.out.println(list.size());
480
                for (int i = 0; i < list.size(); i++) {
481
                        EdgeEnd ee = (EdgeEnd) list.get(i);
482
                        System.out.println("edge" + i);
483
                        Coordinate[] coords = ee.getEdge().getCoordinates();
484
                        String ctx = "(";
485
                        for (int j = 0; j < coords.length; j++) {
486
                                ctx += coords[j].x + " " + coords[j].y + ",";
487
                        }
488
                        ctx += ")";
489
                        System.out.println(ctx);
490
                }
491
        }
492

    
493
        /**
494
         * @return the node if found; null otherwise
495
         */
496
        public Node find(Coordinate coord) {
497
                // Envelope queryRect = getQueryRect(coord);
498
                // List nodes = spatialIndex.query(getQueryRect(coord));
499
                // Collections.sort(nodes, new MinDistCoordComparator(coord));
500
                // Node candidate = (Node) nodes.get(0);
501
                return (Node) nodeMap.get(new SnapNode(coord, null));
502
                // return candidate;
503
        }
504

    
505
        public Iterator iterator() {
506
                return nodeMap.values().iterator();
507
                // return spatialIndex.queryAll().iterator();
508
        }
509

    
510
        public Collection values() {
511
                return nodeMap.values();
512
                // return spatialIndex.queryAll();
513
        }
514

    
515
        public Collection getBoundaryNodes(int geomIndex) {
516
                Collection bdyNodes = new ArrayList();
517
                for (Iterator i = iterator(); i.hasNext();) {
518
                        Node node = (Node) i.next();
519
                        if (node.getLabel().getLocation(geomIndex) == Location.BOUNDARY)
520
                                bdyNodes.add(node);
521
                }
522
                return bdyNodes;
523
        }
524

    
525
        public void dump() {
526
                System.out.println(this.toString());
527
                Iterator it = iterator();
528
                while (it.hasNext()) {
529
                        Node node = (Node) it.next();
530
                        Coordinate coord = node.getCoordinate();
531
                        System.out.println("x= " + coord.x + ", y= " + coord.y);
532
                }
533
        }
534

    
535
        public static void main(String[] args) {
536
                SnappingNodeMap nodeMap = new SnappingNodeMap(new NodeFactory(), 0.01);
537
                nodeMap.addNode(new Coordinate(0.001, 0.001));
538
                nodeMap.addNode(new Coordinate(0.002, 0.002));
539
                Iterator it = nodeMap.iterator();
540
                while (it.hasNext()) {
541
                        Node node = (Node) it.next();
542
                        Coordinate coord = node.getCoordinate();
543
                        System.out.println("x= " + coord.x + ", y= " + coord.y);
544
                }
545
        }
546
}