Statistics
| Revision:

svn-gvsig-desktop / trunk / libraries / libTopology / src / org / gvsig / topology / Topology.java @ 17778

History | View | Annotate | Download (35.4 KB)

1
/*
2
 * Created on 07-sep-2007
3
 *
4
 * gvSIG. Sistema de Informaci?n Geogr?fica de la Generalitat Valenciana
5
 *
6
 * Copyright (C) 2004 IVER T.I. and Generalitat Valenciana.
7
 *
8
 * This program is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU General Public License
10
 * as published by the Free Software Foundation; either version 2
11
 * of the License, or (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program; if not, write to the Free Software
20
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
21
 *
22
 * For more information, contact:
23
 *
24
 *  Generalitat Valenciana
25
 *   Conselleria d'Infraestructures i Transport
26
 *   Av. Blasco Ib??ez, 50
27
 *   46010 VALENCIA
28
 *   SPAIN
29
 *
30
 *      +34 963862235
31
 *   gvsig@gva.es
32
 *      www.gvsig.gva.es
33
 *
34
 *    or
35
 *
36
 *   IVER T.I. S.A
37
 *   Salamanca 50
38
 *   46005 Valencia
39
 *   Spain
40
 *
41
 *   +34 963163400
42
 *   dac@iver.es
43
 */
44
/* CVS MESSAGES:
45
 *
46
 * $Id: 
47
 * $Log: 
48
 */
49
package org.gvsig.topology;
50

    
51
import java.awt.Image;
52
import java.awt.geom.Rectangle2D;
53
import java.util.ArrayList;
54
import java.util.Collection;
55
import java.util.Collections;
56
import java.util.HashMap;
57
import java.util.Iterator;
58
import java.util.List;
59
import java.util.Map;
60

    
61
import javax.swing.ImageIcon;
62

    
63
import org.apache.log4j.Logger;
64
import org.cresques.cts.IProjection;
65
import org.gvsig.topology.topologyrules.MustBeLargerThanClusterTolerance;
66

    
67
import com.hardcode.gdbms.driver.exceptions.ReadDriverException;
68
import com.iver.cit.gvsig.exceptions.expansionfile.ExpansionFileReadException;
69
import com.iver.cit.gvsig.fmap.MapContext;
70
import com.iver.cit.gvsig.fmap.core.FShape;
71
import com.iver.cit.gvsig.fmap.layers.CancelationException;
72
import com.iver.cit.gvsig.fmap.layers.FLayer;
73
import com.iver.cit.gvsig.fmap.layers.FLayers;
74
import com.iver.cit.gvsig.fmap.layers.FLyrVect;
75
import com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent;
76
import com.iver.cit.gvsig.fmap.layers.XMLException;
77
import com.iver.utiles.IPersistence;
78
import com.iver.utiles.XMLEntity;
79
import com.iver.utiles.swing.threads.CancellableProgressTask;
80

    
81
/**
82
 * 
83
 * This class represents a Topology, as a group of vectorial layers
84
 * and topology rules that checks geometries and their spatial relationships of
85
 * these layers.
86
 * 
87
 * It extends FLayers to reuse all already existent code related with
88
 * groups of layers (TOC related, etc.)
89
 * 
90
 * TODO Study if we must syncronize writing methods (validate, markAsException, addError,
91
 * ruleId++, etc)
92
 *
93
 *
94
 * @author azabala
95
 *
96
 */
97
public class Topology extends FLayers implements ITopologyStatus, ITopologyErrorContainer {
98

    
99
        private static Logger logger = Logger.getLogger(Topology.class.getName());
100
                
101
        private final static ImageIcon NOT_VALIDATED_TOC_ICON = 
102
                new ImageIcon(Topology.class.getResource("images/topoicon.png"));
103
        
104
        private final static ImageIcon VALIDATED_TOC_ICON = 
105
                new ImageIcon(Topology.class.getResource("images/topoicon_validated.png"));
106
        
107
        private final static ImageIcon VALIDATED_WITH_ERRORS_TOC_ICON = 
108
                new ImageIcon(Topology.class.getResource("images/topoicon_validated_with_errors.png"));
109
         
110
        private final static ImageIcon EMPTY_TOC_ICON = 
111
                new ImageIcon(Topology.class.getResource("images/topoicon_empty.png"));
112

    
113
        private final static ImageIcon VALIDATED_WITH_DIRTY_ZONES_TOC_ICON = 
114
                new ImageIcon(Topology.class.getResource("images/topoicon_validated_with_dirty_zones.png"));
115
        
116
        private final static ImageIcon VALIDATING_TOC_ICON = 
117
                new ImageIcon(Topology.class.getResource("images/topoicon_validating.png"));
118
        
119
        private ImageIcon statusIcon = EMPTY_TOC_ICON;
120
        
121
        /**
122
         * topology name
123
         */
124
        private String name;
125
        /**
126
         * cluster tolerance of the topology
127
         */
128
        
129
        private double clusterTolerance;
130

    
131
        /**
132
         * validation status of the topology
133
         */
134
        private byte status = EMPTY;
135

    
136
        
137
        /**
138
         * If during validation process a topoly exceeds this parameters, validation
139
         * will be stopped.
140
         * 
141
         */
142
        private int maxNumberOfErrors = -1;
143

    
144
        
145
        /**
146
         * topology rules of the topology
147
         */
148
        private List<ITopologyRule> rules;
149

    
150
        
151
        /**
152
         * Each layer of a topology must have a cluster tolerance rule
153
         */
154
        private List<MustBeLargerThanClusterTolerance> clusterToleranceRules;
155

    
156
        
157
        /**
158
         * Regions of the topology to be validated
159
         */
160
        private List<Rectangle2D> dirtyZones;
161

    
162
        /**
163
         * Error container in which topology delegates error container
164
         * responsability.
165
         */
166
        private ITopologyErrorContainer errorContainer;
167
        
168
        /**
169
         * Registered class to listen for change status events.
170
         */
171
        private List<ITopologyStatusListener> statusListeners;
172
        
173
        
174
        /**
175
         * Map that relates a FLyrVect of the Topology with its rank (weight when we
176
         * are going to snap many coordinates)
177
         */
178
        private Map<FLyrVect, XYZLayerRank> layerRanks;
179
        
180
        /**
181
         * Numerical identifier for rules of a topology
182
         */
183
        private int ruleId = 0;
184
        
185
        /**
186
         * Interface for all of these classes interested in listening topology change status
187
         * events.
188
         * @author Alvaro Zabala
189
         *
190
         */
191
        public interface ITopologyStatusListener{
192
                public void statusChange(TopologyStatusEvent event);
193
        }
194
        
195
        /**
196
         * Topology change status event. It contains old and new topology status.
197
         * @author Alvaro Zabala
198
         *
199
         */
200
        public class TopologyStatusEvent{
201
                int newStatus;
202
                int prevStatus;
203
        }
204
        
205
        /**
206
         * Default constructor for a topology as a FLayers
207
         * 
208
         * @param fmap
209
         * @param parent
210
         */
211
        public Topology(MapContext fmap, 
212
                                        FLayers parent, 
213
                                        double clusterTolerance, 
214
                                        int numberOfErrors,
215
                                        ITopologyErrorContainer errorContainer) {
216
                super(fmap, parent);
217
                this.clusterTolerance = clusterTolerance;
218
                this.maxNumberOfErrors = numberOfErrors;
219
                this.errorContainer = errorContainer;
220
                
221
                rules = new ArrayList<ITopologyRule>();
222
                clusterToleranceRules = new ArrayList<MustBeLargerThanClusterTolerance>();
223
                dirtyZones = new ArrayList<Rectangle2D>();
224
                layerRanks = new HashMap<FLyrVect, XYZLayerRank>();
225
                
226
                statusListeners = new ArrayList<ITopologyStatusListener>();
227
                //This listener updates the icon status returneb by this kind of layer
228
                statusListeners.add(new ITopologyStatusListener(){
229
                        public void statusChange(TopologyStatusEvent event) {
230
                                switch(event.newStatus){
231
                                case ITopologyStatus.VALIDATED:
232
                                        statusIcon = VALIDATED_TOC_ICON;
233
                                        break;
234
                                        
235
                                case ITopologyStatus.NOT_VALIDATED:
236
                                        statusIcon = NOT_VALIDATED_TOC_ICON;
237
                                        break;
238
                                        
239
                                case ITopologyStatus.VALIDATED_WITH_DIRTY_ZONES:
240
                                        statusIcon = VALIDATED_WITH_DIRTY_ZONES_TOC_ICON;
241
                                        break;
242
                                        
243
                                case ITopologyStatus.VALIDATED_WITH_ERRORS:
244
                                        statusIcon = VALIDATED_WITH_ERRORS_TOC_ICON;
245
                                        break;
246
                                        
247
                                case ITopologyStatus.VALIDATING:
248
                                        statusIcon = VALIDATING_TOC_ICON;
249
                                        break;
250
                                }
251
                        }});
252
        }
253
        
254
        public Topology(MapContext mapContext, FLayers layers){
255
                this(mapContext, layers, 0d, 1000, new SimpleTopologyErrorContainer());
256
        }
257
        
258
        
259
        public void addStatusListener(ITopologyStatusListener statusListener){
260
                this.statusListeners.add(statusListener);
261
        }
262
        
263
        /**
264
         * Creates a topology from its XML representation
265
         * @param xmlEntity
266
         * @return
267
         */
268
        public static Topology createFromXML(MapContext mapContext, XMLEntity xmlEntity){
269
                FLayers rootLyr = mapContext.getLayers();
270
                Topology solution = new Topology(mapContext, rootLyr);
271
                
272
                try {
273
                        solution.setXMLEntity(xmlEntity);
274
                        if(solution.getErrorContainer() == null){
275
                                solution.setErrorContainer(new SimpleTopologyErrorContainer());
276
                        }
277
                } catch (XMLException e) {
278
                        logger.error("Error al reconstruir la topologia desde fichero xml", e);
279
                }
280
                return solution;
281
        }
282
        
283
        public ImageIcon getTocImageIcon() {
284
                return statusIcon;
285
        }
286
        
287
        public Image getTocStatusImage() {
288
                return statusIcon.getImage();
289
        }
290

    
291
        /**
292
         * Changes the cluster tolerance of the topology
293
         * 
294
         * 
295
         * This operation resets the status of the topology (it clears
296
         * errors and dirty zones)
297
         * 
298
         * @param clusterTolerance
299
         */
300
        public void setClusterTolerance(double clusterTolerance) {
301
                if(status == VALIDATING)
302
                        return;//maybe we could launch an exception??
303
                this.clusterTolerance = clusterTolerance;
304
                resetStatus();
305
                Iterator<MustBeLargerThanClusterTolerance> rulesIt = this.clusterToleranceRules.iterator();
306
                while(rulesIt.hasNext()){
307
                        MustBeLargerThanClusterTolerance rule = rulesIt.next();
308
                        rule.setClusterTolerance(clusterTolerance);
309
                }
310
                
311
        }
312

    
313
        public double getClusterTolerance() {
314
                return clusterTolerance;
315
        }
316
        
317
        public void resetStatus(){
318
                setStatus(NOT_VALIDATED);
319
                this.clear();
320
                this.dirtyZones.clear();
321
                
322
        }
323

    
324
        /**
325
         * Adds a new topology rule to the topology.
326
         * 
327
         * The layers referenced by the rule must exist in the topology.
328
         * 
329
         * The state of the topology changes to "NON_VALIDATED", and a new dirty zone
330
         * is added for the scope of the new rule (envelope of one or two layers).
331
         * 
332
         * 
333
         * @throws TopologyRuleDefinitionException
334
         */
335
        public void addRule(ITopologyRule rule) throws RuleNotAllowedException,
336
                        TopologyRuleDefinitionException {
337

    
338
                if(status == VALIDATING)
339
                        throw new RuleNotAllowedException("No se puede a?adir una regla si la topologia est? siendo validada");
340
                
341
                
342
                Rectangle2D ruleScope = null;
343
                try {
344
                        if (rule instanceof IOneLyrRule) {
345
                                FLyrVect originLyr = ((IOneLyrRule) rule).getOriginLyr();
346
                                if (getLayer(originLyr.getName()) == null) {
347
                                        throw new RuleNotAllowedException(
348
                                                        "Regla con capa  que no forma parte de la topologia");
349
                                }
350
                                ruleScope = originLyr.getFullExtent();
351
                        }
352
                        if (rule instanceof ITwoLyrRule) {
353
                                FLyrVect destLyr = ((ITwoLyrRule) rule).getDestinationLyr();
354
                                if (getLayer(destLyr.getName()) == null) {
355
                                        throw new RuleNotAllowedException(
356
                                                        "Regla con capa  que no forma parte de la topologia");
357
                                }
358
                                if (ruleScope != null)
359
                                        ruleScope.add(destLyr.getFullExtent());
360
                        }
361
                } catch (ExpansionFileReadException e) {
362
                        e.printStackTrace();
363
                        throw new TopologyRuleDefinitionException(e);
364
                } catch (ReadDriverException e) {
365
                        e.printStackTrace();
366
                        throw new TopologyRuleDefinitionException(e);
367
                }
368
                
369
                // before to add the rule we check if it verifies preconditions
370
                rule.checkPreconditions();
371
                rules.add(rule);
372
                
373
//   Si se a?ade una nueva regla, no es posible conservar los errores
374
//   y las zonas sucias previas
375
//                if(status == EMPTY)
376
//                        status = NOT_VALIDATED;
377
//                else if(status == VALIDATED ){
378
//                        status = VALIDATED_WITH_DIRTY_ZONES;
379
//                        addDirtyZone(ruleScope);
380
//                }else if(status == VALIDATED_WITH_ERRORS){
381
//                        //we dont change the status, but add a new dirty zone
382
//                        addDirtyZone(ruleScope);
383
//                }
384
                resetStatus();
385
                rule.setTopologyErrorContainer(this);
386
                rule.setId(this.ruleId++);
387
        }
388
        
389
        
390
        
391

    
392
        /*
393
         * Overwrited implementations of FLayers methods
394
         */
395
        
396
        public void addLayer(FLayer layer) {
397
                addLayer(layers.size(), layer);
398
        }
399

    
400
        public void addLayer(int pos, FLayer layer)  {
401
                if (!(layer instanceof FLyrVect))
402
                        throw new WrongLyrForTopologyException(
403
                                        "Intentando a?adir capa no vectorial a una topologia");
404
                super.addLayer(pos, layer);
405
                setRank((FLyrVect) layer, 1, 1);
406
                
407
                int shapeType = -1;
408
                try {
409
                        shapeType = ((FLyrVect)layer).getShapeType();
410
                        if( (shapeType == FShape.POINT) || (shapeType == FShape.MULTIPOINT) || (shapeType == FShape.TEXT))
411
                                return;
412
                } catch (ReadDriverException e) {
413
                        e.printStackTrace();
414
                        throw new WrongLyrForTopologyException("Error al intentar verificar el tipo de geometria de la capa", e);
415
                }
416
                
417
                
418
                MustBeLargerThanClusterTolerance rule = new 
419
                        MustBeLargerThanClusterTolerance(this, (FLyrVect) layer, clusterTolerance);
420
                rule.setId(this.ruleId++);
421
                Rectangle2D ruleScope;
422
                try {
423
                        ruleScope = layer.getFullExtent();
424
//                 before to add the rule we check if it verifies preconditions
425
                        rule.checkPreconditions();
426
                        
427
                        if(status == EMPTY)
428
                                setStatus(NOT_VALIDATED);
429
                        else if(status == VALIDATED ){
430
                                setStatus(VALIDATED_WITH_DIRTY_ZONES);
431
                                addDirtyZone(ruleScope);
432
                        }else if(status == VALIDATED_WITH_ERRORS){
433
                                //we dont change the status, but add a new dirty zone
434
//                                addDirtyZone(ruleScope);
435
                                //si habia errores, la reevaluacion haria que se repitiesen
436
                                resetStatus();
437
                        }
438
                        rule.setTopologyErrorContainer(this);
439
                        clusterToleranceRules.add(rule);
440
                        
441
                } catch (ExpansionFileReadException e) {
442
                        e.printStackTrace();
443
                        throw new WrongLyrForTopologyException("No es posible acceder all FullExtent de la capa", e);
444
                } catch (ReadDriverException e) {
445
                        e.printStackTrace();
446
                        throw new WrongLyrForTopologyException("No es posible acceder all FullExtent de la capa", e);
447
                } catch (TopologyRuleDefinitionException e) {
448
                        e.printStackTrace();
449
                        throw new WrongLyrForTopologyException("Regla topologica mal definida", e);
450
                }
451
        }
452

    
453
        /**
454
         * Sets the rank/importance of a layer in xy and z planes.
455
         * 
456
         * @param lyr layer
457
         * 
458
         * @param xyRank importance of this layer coordinates in xy plane
459
         * 
460
         * @param zRank importante of this layer coordinates in z plane
461
         */
462
        public void setRank(FLyrVect lyr, int xyRank, int zRank) {
463
                XYZLayerRank rank = new XYZLayerRank(lyr.getName(), xyRank, zRank);
464
                layerRanks.put(lyr, rank);
465
        }
466
        
467
        
468
        public XYZLayerRank getRank(FLyrVect lyr){
469
                return layerRanks.get(lyr);
470
        }
471

    
472
        /**
473
         * Adds a layer to the topology. If the topology has been validated, changes
474
         * topology status to NON-VALIDATED and adds a dirty zone equals to the
475
         * layer extent.
476
         */
477
        public void addLayer(FLyrVect layer, int xyRank, int zRank) {
478
                this.addLayer(layer);
479
                setRank(layer, xyRank, zRank);
480
        }
481
        
482
        /**
483
         * Remove a layer from a topology.
484
         * 
485
         * This task is more complex than removing a layer from a LayerCollection:
486
         * -must remove all rules which references to this layer.
487
         * -must recompute status and dirty zones.
488
         * etc.
489
         * 
490
         * TODO Implement remove layer as a geoprocess.
491
         * 
492
         */
493
        public void removeLayer(FLayer lyr) throws CancelationException {
494
                callLayerRemoving(LayerCollectionEvent.createLayerRemovingEvent(lyr));
495
                
496
                //remove cluster rules related with the layer
497
                Iterator<MustBeLargerThanClusterTolerance> clusterRulesIt = clusterToleranceRules.iterator();
498
                while(clusterRulesIt.hasNext()){
499
                        MustBeLargerThanClusterTolerance rule = clusterRulesIt.next();
500
                        if(rule.getOriginLyr().equals(lyr)){
501
                                clusterRulesIt.remove();
502
                        }
503
                }//while
504
                
505
                //Remove normal rules related with the layer
506
                Iterator<ITopologyRule> rulesIt = this.rules.iterator();
507
                while(rulesIt.hasNext()){
508
                        ITopologyRule rule =  rulesIt.next();
509
                        if(rule instanceof IOneLyrRule){
510
                                IOneLyrRule oneLyrRule = (IOneLyrRule) rule;
511
                                if(oneLyrRule.getOriginLyr().equals(lyr)){
512
                                        rulesIt.remove();
513
                                        continue;
514
                                }
515
                        }
516
                        
517
                        if(rule instanceof ITwoLyrRule){
518
                                ITwoLyrRule twoLyrRule = (ITwoLyrRule) rule;
519
                                if(twoLyrRule.getOriginLyr().equals(lyr)){
520
                                        rulesIt.remove();
521
                                }
522
                        }
523
                }//while
524
                
525
                this.errorContainer.removeErrorsByLayer((FLyrVect) lyr);
526
                this.layerRanks.remove(lyr);                
527
                this.updateDirtyZones();
528
                callLayerRemoved(LayerCollectionEvent.createLayerRemovedEvent(lyr));
529
        }
530
        
531
        public void removeLayer(int idLyr){
532
                FLayer lyr = (FLayer) layers.get(idLyr);
533
                removeLayer(lyr);
534
        }
535
        
536
        public void removeRule(ITopologyRule rule){
537
                if(rules.contains(rule)){
538
                        rules.remove(rule);
539
                }else if(clusterToleranceRules.contains(rule)){
540
                        clusterToleranceRules.remove(rule);
541
                }
542
                this.errorContainer.removeErrorsByRule(rule.getName());
543
                
544
                this.updateDirtyZones();
545
        }
546
        
547
        
548
        
549
        private void updateDirtyZones() {
550
//                this.dirtyZones.clear(); //FIXME REVISAR SI ES NECESARIO BORRAR LAS ZONAS SUCIAS
551
                int errorNum = errorContainer.getNumberOfErrors();
552
                for(int i = 0; i < errorNum; i++){
553
                        TopologyError topologyError = errorContainer.getTopologyError(i);
554
                        Rectangle2D rect = topologyError.getGeometry().getBounds2D();
555
                        addDirtyZone(rect);
556
                }
557
        }
558

    
559
        /**
560
         * Ranks (in xy plane and z plane) for layers of the topology.
561
         * 
562
         * The rank of layer marks its weight for computing weihgted average
563
         * coordinates.
564
         * 
565
         * @author azabala
566
         * 
567
         */
568
        class XYZLayerRank implements IPersistence {
569
                int xyRank;
570
                int zRank;
571
                String layerName;
572

    
573
                XYZLayerRank(String layerName, int xyRank, int zRank) {
574
                        this.layerName = layerName;
575
                        this.xyRank = xyRank;
576
                        this.zRank = zRank;
577
                }
578
                
579
                XYZLayerRank(){}
580

    
581
                public String getClassName() {
582
                        return this.getClass().getName();
583
                }
584

    
585
                public XMLEntity getXMLEntity() {
586
                        XMLEntity solution = new XMLEntity();
587
                        solution.putProperty("layerName", layerName);
588
                        solution.putProperty("xyRank", xyRank);
589
                        solution.putProperty("zRank", zRank);
590
                        return solution;
591
                }
592

    
593
                public void setXMLEntity(XMLEntity xml) {
594
                        if(xml.contains("layerName"))
595
                                layerName = xml.getStringProperty("layerName");
596
                        
597
                        if(xml.contains("xyRank"))
598
                                xyRank = xml.getIntProperty("xyRank");
599
                        
600
                        if(xml.contains("zRank"))
601
                                zRank = xml.getIntProperty("zRank");
602
                        
603
                }
604
        }
605

    
606
        public void setStatus(byte newStatus) {
607
                TopologyStatusEvent newStatusEvent = new TopologyStatusEvent();
608
                newStatusEvent.prevStatus = this.status;
609
                newStatusEvent.newStatus = newStatus;
610
                this.status = newStatus;
611
                
612
                fireStatusChange(newStatusEvent);
613
        }
614

    
615
        public byte getStatus() {
616
                return status;
617
        }
618
        
619
        public List<ITopologyRule> getAllRules(){
620
                List<ITopologyRule> solution = new ArrayList<ITopologyRule>();
621
                solution.addAll(this.rules);
622
                solution.addAll(this.clusterToleranceRules);
623
                return solution;
624
        }
625

    
626
        /**
627
         * Adds a dirty zone to the topology (usually when a feature of a layer of
628
         * the topology has been edited)
629
         */
630
        public void addDirtyZone(Rectangle2D newDirtyZone) {
631
                if(status == NOT_VALIDATED)
632
                        return;
633
                Iterator<Rectangle2D> zonesIt = dirtyZones.iterator();
634
                while (zonesIt.hasNext()) {
635
                        Rectangle2D dirtyZone = zonesIt.next();
636
                        if (dirtyZone.contains(newDirtyZone)) {
637
                                return;// we dont add this dirty zone. Its redundant
638
                        }
639
                        if (newDirtyZone.contains(dirtyZone)) {
640
                                zonesIt.remove();
641
                                dirtyZones.add(newDirtyZone);
642
                                return;
643
                        }
644

    
645
                        if (dirtyZone.intersects(newDirtyZone)) {
646
                                dirtyZone.add(newDirtyZone);
647
                                return;
648
                        }
649
                }// while
650
                
651
                if(status == VALIDATED)
652
                        setStatus(VALIDATED_WITH_DIRTY_ZONES);
653
                
654
                // at this point, we add the new dirty zone
655
                dirtyZones.add(newDirtyZone);
656
        }
657
        
658
        
659
        public void removeDirtyZone(Rectangle2D newDirtyZone) {
660
                if(status == NOT_VALIDATED)
661
                        return;//maybe we must launch an inconsistent status exception
662
                Iterator<Rectangle2D> zonesIt = dirtyZones.iterator();
663
                while (zonesIt.hasNext()) {
664
                        Rectangle2D dirtyZone = zonesIt.next();
665
                        if (newDirtyZone.contains(dirtyZone)) {
666
                                zonesIt.remove();
667
                                continue;
668
                        }
669
                }// while
670

    
671
                // at this point, we add the new dirty zone
672
                dirtyZones.remove(newDirtyZone);
673
        }
674
        
675

    
676
        public Rectangle2D getDirtyZone(int i) {
677
                return dirtyZones.get(i);
678
        }
679
        
680
        public int getNumberOfDirtyZones(){
681
                return dirtyZones.size();
682
        }
683
        
684
        
685
        public void validate(){
686
                validate(null);
687
        }
688
        
689
        /**
690
         * Validates the topology: it validates each topology rule for the given
691
         * dirty zones. After the end of the process,
692
         */
693
        public void validate(CancellableProgressTask progressMonitor) {
694
                
695
                if(progressMonitor != null){
696
                        progressMonitor.setInitialStep(0);
697
                        int numOfSteps = rules.size() + clusterToleranceRules.size();
698
                        progressMonitor.setFinalStep(numOfSteps);
699
                        progressMonitor.setDeterminatedProcess(true);
700
                        progressMonitor.setNote(Messages.getText("Validating_a_topology"));
701
                        progressMonitor.setStatusMessage(Messages.getText(rules.get(0).getDescription()));
702
                }
703
                
704
                
705
                if (this.status == EMPTY) {
706
                        //TODO Maybe we must do progressMonitor.setFinished(true)
707
                        //or throw an exception
708
                        return;
709
                } else if (this.status == VALIDATED) {
710
                        return;
711
                } 
712
                
713
                else if (this.status == NOT_VALIDATED){
714
                        setStatus(VALIDATING);
715
                        
716
                        
717
                        //we make a local copy of dirty zones to avoid to use dirty zones created in 
718
                        //the current validation.
719
                        ArrayList<Rectangle2D> dirtyZonesCopy = new ArrayList<Rectangle2D>();
720
                        Collections.copy(dirtyZonesCopy, this.dirtyZones);
721
                        Iterator<MustBeLargerThanClusterTolerance> it = 
722
                                clusterToleranceRules.iterator();
723
                        while(it.hasNext()){
724
                                MustBeLargerThanClusterTolerance rule = it.next();
725
                                if(progressMonitor != null){
726
                                        if(progressMonitor.isCanceled()/*|| progressMonitor.isFinished()*/){
727
                                                resetStatus();
728
                                                return;
729
                                        }
730
                                        progressMonitor.setNote(Messages.getText("VALIDANDO_REGLA")+" "+
731
                                                   Messages.getText(rule.getName()));
732
                                        progressMonitor.reportStep();
733
                                   }
734
                                
735
                                if(getNumberOfErrors() >= this.maxNumberOfErrors){
736
                                        if(progressMonitor != null)
737
                                                progressMonitor.setCanceled(true);
738
                                        return;
739
                                }
740
                                
741
                                rule.checkRule(progressMonitor);
742
                        }
743
                        
744
                        Iterator<ITopologyRule> rulesIt = this.rules.iterator();
745
                        while (rulesIt.hasNext()) {
746
                                ITopologyRule rule = rulesIt.next();
747
                                
748
                                if(progressMonitor != null){
749
                                        if(progressMonitor.isCanceled()/*|| progressMonitor.isFinished()*/){
750
                                                resetStatus();
751
                                                return;
752
                                        }
753
                                        progressMonitor.setNote(Messages.getText("VALIDANDO_REGLA")+" "+
754
                                                   Messages.getText(rule.getName()));
755
                                        progressMonitor.reportStep();
756
                                   }
757
                                
758
                                if(getNumberOfErrors() >= this.maxNumberOfErrors){
759
                                        if(progressMonitor != null)
760
                                                progressMonitor.setCanceled(true);
761
                                        return;
762
                                }
763
                                
764
                                if(dirtyZonesCopy.size() == 0){
765
                                        rule.checkRule(progressMonitor);
766
                                }else{
767
                                        //A topology is NON_VALIDATED with dirty zones when
768
                                        //it has VALIDATED status and we add a new rule.
769
                                        
770
                                        //TODO Check to add a new rule to a topology
771
                                        //with VALIDATED_WITH_ERROR status
772
                                        Iterator<Rectangle2D> dirtyZonesIt = dirtyZonesCopy.iterator();
773
                                        while(dirtyZonesIt.hasNext()){
774
                                                Rectangle2D rect = dirtyZonesIt.next();
775
                                                rule.checkRule(progressMonitor,rect);
776
                                        }//while
777
                                }//else
778
                        }//while
779
                        if(this.errorContainer.getNumberOfErrors() > 0)
780
                                setStatus(VALIDATED_WITH_ERRORS);
781
                        else
782
                                setStatus(VALIDATED);        
783
                }
784
                else if (this.status == VALIDATED_WITH_ERRORS || 
785
                                this.status == ITopologyStatus.VALIDATED_WITH_DIRTY_ZONES) {
786
                        setStatus(VALIDATING);
787
                        // this topology must have at least one dirty zone
788
                        if (this.dirtyZones.size() < 1){
789
//                                 FIXME Deberiamos lanzar una
790
                                // InconsistentStatusException ??
791
                                return;
792
                        }
793

    
794
                        Iterator<Rectangle2D> dirtyZonesIt = this.dirtyZones.iterator();
795
                        while (dirtyZonesIt.hasNext()) {
796
                                Rectangle2D dirtyZone = dirtyZonesIt.next();
797
                                
798
                                Iterator<MustBeLargerThanClusterTolerance> it = this.clusterToleranceRules.iterator();
799
                                while(it.hasNext()){
800
                                        MustBeLargerThanClusterTolerance rule = it.next();
801
                                        if(progressMonitor != null){
802
                                                if(progressMonitor.isCanceled()/*|| progressMonitor.isFinished()*/){
803
                                                        resetStatus();
804
                                                        return;
805
                                                }
806
                                                progressMonitor.setNote(Messages.getText("VALIDANDO_REGLA")+" "+
807
                                                           Messages.getText(rule.getName()));
808
                                                progressMonitor.reportStep();
809
                                           }
810
                                        
811
                                        if(getNumberOfErrors() >= this.maxNumberOfErrors){
812
                                                if(progressMonitor != null)
813
                                                        progressMonitor.setCanceled(true);
814
                                                return;
815
                                        }
816
                                        
817
                                        rule.checkRule(dirtyZone);
818
                                }
819
                                
820
                                Iterator<ITopologyRule> rulesIt = this.rules.iterator();
821
                                while (rulesIt.hasNext()) {
822
                                        ITopologyRule rule = rulesIt.next();
823
                                        if(progressMonitor != null){
824
                                                if(progressMonitor.isCanceled()/*|| progressMonitor.isFinished()*/){
825
                                                        resetStatus();
826
                                                        return;
827
                                                }
828
                                                progressMonitor.setNote(Messages.getText("VALIDANDO_REGLA")+" "+
829
                                                           Messages.getText(rule.getName()));
830
                                                progressMonitor.reportStep();
831
                                           }
832
                                        
833
                                        if(getNumberOfErrors() >= this.maxNumberOfErrors){
834
                                                if(progressMonitor != null)
835
                                                        progressMonitor.setCanceled(true);
836
                                                return;
837
                                        }
838
                                        rule.checkRule(dirtyZone);
839
                                }//while
840
                                
841
                                
842
                        }//while
843
                        if(this.errorContainer.getNumberOfErrors() > 0)
844
                                setStatus(VALIDATED_WITH_ERRORS);
845
                        else
846
                                setStatus(VALIDATED);
847
                }//if
848
        }
849

    
850
        public int getLayerCount() {
851
                return super.getLayersCount();
852
        }
853
        
854
        public List getLayers(){
855
                return this.layers;
856
        }
857

    
858
        public int getRuleCount() {
859
                return rules.size();
860
        }
861

    
862
        public FLyrVect getLyr(int lyrIndex) {
863
                return (FLyrVect) super.getLayer(lyrIndex);
864
        }
865

    
866
        public ITopologyRule getRule(int ruleIndex) {
867
                return rules.get(ruleIndex);
868
        }
869
        
870
        public ITopologyRule getRuleById(int ruleId){
871
                for(int i = 0; i < rules.size(); i++){
872
                        ITopologyRule rule = rules.get(i);
873
                        if(rule.getId() == ruleId){
874
                                return rule;
875
                        }//if
876
                }//for
877
                
878
                for(int i = 0; i < clusterToleranceRules.size(); i++){
879
                        MustBeLargerThanClusterTolerance rule = clusterToleranceRules.get(i);
880
                        if(rule.getId() == ruleId){
881
                                return rule;
882
                        }//if
883
                }
884
                return null;
885
        }
886
        
887
        /**
888
         * Looks for all rules which has at least one reference to the given layer
889
         * (as origin or destination layer in the rule)
890
         * 
891
         * @param lyr
892
         * @return
893
         */
894
        public List<ITopologyRule> getRulesByLyr(FLyrVect lyr){
895
                List<ITopologyRule> solution = new ArrayList<ITopologyRule>();
896
                Iterator<ITopologyRule> ruleIt = this.rules.iterator();
897
                while(ruleIt.hasNext()){
898
                        ITopologyRule rule = ruleIt.next();
899
                        if(rule instanceof IOneLyrRule){
900
                                IOneLyrRule oneLyrRule = (IOneLyrRule) rule;
901
                                FLyrVect originLyr = oneLyrRule.getOriginLyr();
902
                                if(originLyr.equals(lyr))
903
                                {
904
                                        solution.add(rule);
905
                                        continue;//dont need to check for destination layer
906
                                }
907
                        }//if
908
                        
909
                        if(rule instanceof ITwoLyrRule){
910
                                ITwoLyrRule twoLyrRule = (ITwoLyrRule) rule;
911
                                FLyrVect destinationLyr = twoLyrRule.getDestinationLyr();
912
                                if(destinationLyr.equals(lyr)){
913
                                        solution.add(rule);
914
                                }
915
                        }
916
                        
917
                }//while
918
                
919
                Iterator<MustBeLargerThanClusterTolerance> clusterIt = clusterToleranceRules.iterator();
920
                while(clusterIt.hasNext()){
921
                        MustBeLargerThanClusterTolerance rule = clusterIt.next();
922
                        FLyrVect originLyr = rule.getOriginLyr();
923
                        if(originLyr.equals(lyr))
924
                        {
925
                                solution.add(rule);
926
                                continue;//dont need to check for destination layer
927
                        }
928
                }
929
                return solution;
930
        }
931
        
932
        /**
933
         * Returns if a specified rectangle touch one of the existing dirty zones.
934
         * If not, probably is needed to add to the dirty zones collection. If true,
935
         * maybe it could modify the dirty zone.
936
         */
937
        public boolean isInDirtyZone(Rectangle2D envelope) {
938
                Iterator<Rectangle2D> zonesIt = dirtyZones.iterator();
939
                while (zonesIt.hasNext()) {
940
                        Rectangle2D rect = zonesIt.next();
941
                        if (rect.contains(envelope))
942
                                return true;
943
                }
944
                return false;
945
        }
946

    
947
        /**
948
         * Modify the dirty zone of the specified index
949
         * 
950
         * TODO Make thread safe ?
951
         */
952
        public void updateDirtyZone(int dirtyZoneIndex, Rectangle2D dirtyZone) {
953
                dirtyZones.remove(dirtyZoneIndex);
954
                dirtyZones.add(dirtyZoneIndex, dirtyZone);
955
        }
956

    
957
        public void setMaxNumberOfErrors(int maxNumberOfErrors) {
958
                this.maxNumberOfErrors = maxNumberOfErrors;
959
        }
960

    
961
        public int getMaxNumberOfErrors() {
962
                return maxNumberOfErrors;
963
        }
964

    
965
        /*
966
         * @see org.gvsig.topology.ITopologyErrorContainer#addTopologyError(org.gvsig.topology.TopologyError)
967
         */
968

    
969
        public void addTopologyError(TopologyError topologyError) {
970
                errorContainer.addTopologyError(topologyError);
971
                Rectangle2D rect = topologyError.getGeometry().getBounds2D();
972
                addDirtyZone(rect);
973
        }
974

    
975
        /**
976
         * marks topologyErrors as an exception (and removes its bounds of the 
977
         * dirty zones list)
978
         * @param topologyError error to mark as exceptions
979
         */
980
        public void markAsTopologyException(TopologyError topologyError) {
981
                errorContainer.markAsTopologyException(topologyError);
982
                Rectangle2D rect = topologyError.getGeometry().getBounds2D();
983
                removeDirtyZone(rect);
984
                
985
                if(status == VALIDATED_WITH_DIRTY_ZONES){
986
                        if(dirtyZones.size() == 0)
987
                                setStatus(VALIDATED);
988
                }else if(status == VALIDATED_WITH_ERRORS){
989
                        if(getNumberOfErrors() == getNumberOfExceptions())
990
                                setStatus(VALIDATED);
991
                }
992
        }
993
        
994
        public void demoteToError(TopologyError topologyError) {
995
                errorContainer.demoteToError(topologyError);
996
                Rectangle2D rect = topologyError.getGeometry().getBounds2D();
997
                addDirtyZone(rect);
998
                if(getNumberOfErrors() > getNumberOfExceptions() )
999
                        setStatus(VALIDATED_WITH_ERRORS);
1000
        }
1001

    
1002
        
1003
        
1004
        public List<TopologyError> getTopologyErrorsByLyr(FLyrVect layer,
1005
                                                                                                        IProjection desiredProjection, 
1006
                                                                                                        boolean includeExceptions) {
1007
                
1008
                return errorContainer.getTopologyErrorsByLyr(layer, 
1009
                                                                                 desiredProjection,  
1010
                                                                                 includeExceptions);
1011
        }
1012

    
1013
        public List<TopologyError> getTopologyErrorsByRule(String ruleName,
1014
                        IProjection desiredProjection, boolean includeExceptions) {
1015
                
1016
                return errorContainer.getTopologyErrorsByRule(ruleName, 
1017
                                                                                                desiredProjection, 
1018
                                                                                                includeExceptions);
1019
        }
1020
        
1021

    
1022
        public List<TopologyError> getTopologyErrorsByShapeType(int shapeType,
1023
                        IProjection desiredProjection, boolean includeExceptions) {
1024
                return errorContainer.getTopologyErrorsByShapeType(shapeType, 
1025
                                                                                                        desiredProjection, 
1026
                                                                                                        includeExceptions);        
1027
        }
1028

    
1029
        
1030
        /**
1031
         * Get TopologyError filtered byte shapeType, sourte layer of the rule which
1032
         * was violtated by this error.
1033
         */
1034
        public List<TopologyError> getTopologyErrors(String ruleName,
1035
                        int shapeType, FLyrVect sourceLayer, IProjection desiredProjection,
1036
                        boolean includeExceptions) {
1037

    
1038
                return errorContainer.getTopologyErrors(ruleName,
1039
                                                                                                shapeType, 
1040
                                                                                                sourceLayer, 
1041
                                                                                                desiredProjection, 
1042
                                                                                                includeExceptions);
1043
        }
1044

    
1045
        /**
1046
         * Return an unique identifier for the error saved in this
1047
         * TopologyErrorContainer.
1048
         * 
1049
         * Until a new error would be added to this error container, it will
1050
         * return always the same error fid.
1051
         */
1052
        public synchronized String getErrorFid() {
1053
                return errorContainer.getErrorFid();
1054
        }
1055

    
1056
        public int getNumberOfErrors() {
1057
                return errorContainer.getNumberOfErrors();
1058
        }
1059

    
1060
        public TopologyError getTopologyError(int index) {
1061
                return errorContainer.getTopologyError(index);
1062
        }
1063
        
1064
        public void clear() {
1065
                errorContainer.clear();
1066
        }
1067

    
1068
        public XMLEntity getXMLEntity() throws XMLException{
1069
                
1070
                //Topology is a subclass of FLayers, so the call to super
1071
                //allows to persist layer status, toc information and layers
1072
                XMLEntity xml = super.getXMLEntity();
1073
                
1074
                //Si no ponemos esto className ser? FLayerDefault, no??
1075
                xml.putProperty("className", this.getClass().getName());
1076
                xml.putProperty("name", name);
1077
                xml.putProperty("clusterTolerance", clusterTolerance);
1078
                xml.putProperty("status", status);
1079
                xml.putProperty("maxNumberOfErrors", maxNumberOfErrors);
1080
                
1081
                int numberOfTopologyRules = rules.size();
1082
                xml.putProperty("numberOfTopologyRules", numberOfTopologyRules);
1083
                for(int i = 0; i < numberOfTopologyRules; i++){
1084
                        ITopologyRule rule = rules.get(i);
1085
                        xml.addChild(rule.getXMLEntity());
1086
                }
1087
                
1088
                
1089
                int numberOfClusterRules = clusterToleranceRules.size();
1090
                xml.putProperty("numberOfClusterRules", numberOfClusterRules);
1091
                for(int i = 0; i < numberOfClusterRules; i++){
1092
                        MustBeLargerThanClusterTolerance rule = clusterToleranceRules.get(i);
1093
                        xml.addChild(rule.getXMLEntity());
1094
                }
1095
                
1096
                int numberOfDirtyZones = dirtyZones.size();
1097
                xml.putProperty("numberOfDirtyZones", numberOfDirtyZones);
1098
                for(int i = 0; i < numberOfDirtyZones; i++){
1099
                        Rectangle2D rect = dirtyZones.get(i);
1100
                        if(rect != null){
1101
                                XMLEntity dirtyZoneXML = new XMLEntity();
1102
                                dirtyZoneXML.putProperty("extent" + i + "X", rect.getX());
1103
                                dirtyZoneXML.putProperty("extent" + i + "Y", rect.getY());
1104
                                dirtyZoneXML.putProperty("extent" + i + "W", rect.getWidth());
1105
                                dirtyZoneXML.putProperty("extent" + i + "H", rect.getHeight());        
1106
                                
1107
                                xml.addChild(dirtyZoneXML);
1108
                        }//if
1109
                        
1110
                }//for
1111
                
1112
                XMLEntity errorContainerXML = errorContainer.getXMLEntity();
1113
                xml.addChild(errorContainerXML);
1114
                
1115
                Collection<XYZLayerRank> ranksVal = layerRanks.values();
1116
                int numberOfRanks = ranksVal.size();
1117
                xml.putProperty("numberOfRanks", numberOfRanks);
1118
                Iterator<XYZLayerRank> xyzRankIterator = layerRanks.values().iterator();
1119
                while(xyzRankIterator.hasNext()){
1120
                        XYZLayerRank layerRank = xyzRankIterator.next();
1121
                        XMLEntity entity = layerRank.getXMLEntity();
1122
                        xml.addChild(entity);
1123
                }
1124
                return xml;
1125
        }
1126
        
1127

    
1128
        public void setXMLEntity(XMLEntity xml) throws XMLException {
1129
                super.setXMLEntity(xml);
1130
                
1131
                //FIXME Cambiar el uso de childrenCount por el empleo de propiedades
1132
                int numLayers = this.getLayersCount();
1133
                int numProperties = this.getExtendedProperties().size();
1134
                
1135
                int childrenCount = numLayers + numProperties;
1136
                
1137
                if(xml.contains("clusterTolerance"))
1138
                {
1139
                        this.clusterTolerance = xml.getDoubleProperty("clusterTolerance");
1140
                }
1141
                
1142
                if(xml.contains("name"))
1143
                {
1144
                        this.name = xml.getStringProperty("name");
1145
                }
1146
                
1147
                if(xml.contains("status"))
1148
                {
1149
                        this.status = (byte) xml.getIntProperty("status");
1150
                }
1151
                
1152
                if(xml.contains("maxNumberOfErrors"))
1153
                {
1154
                        this.maxNumberOfErrors =  xml.getIntProperty("maxNumberOfErrors");
1155
                }
1156
                
1157
                if(xml.contains("numberOfTopologyRules"))
1158
                {
1159
                        int numberOfTopologyRules =   xml.getIntProperty("numberOfTopologyRules");
1160
                        for(int i = 0; i < numberOfTopologyRules; i++){
1161
                                XMLEntity ruleXML = xml.getChild(childrenCount++);
1162
                                ITopologyRule rule = TopologyRuleFactory.createFromXML(this, ruleXML);
1163
                                this.rules.add(rule);
1164
                        }
1165
                }
1166
                
1167
                
1168
                if(xml.contains("numberOfClusterRules")){
1169
                        int numberOfClusterRules = xml.getIntProperty("numberOfClusterRules");
1170
                        for(int i = 0; i < numberOfClusterRules; i++){
1171
                                XMLEntity ruleXML = xml.getChild(childrenCount++);
1172
                                MustBeLargerThanClusterTolerance rule = (MustBeLargerThanClusterTolerance)TopologyRuleFactory.createFromXML(this, ruleXML);
1173
                                this.clusterToleranceRules.add(rule);
1174
                        }
1175
                }
1176
                
1177
                if(xml.contains("numberOfDirtyZones")){
1178
                        int numberOfDirtyZones = xml.getIntProperty("numberOfDirtyZones");
1179
                        for(int i = 0; i < numberOfDirtyZones; i++){
1180
                                XMLEntity dirtyZoneXml = xml.getChild(childrenCount++);
1181
                                double x = dirtyZoneXml.getDoubleProperty("extent"+i+"X");
1182
                                double y = dirtyZoneXml.getDoubleProperty("extent"+i+"Y");
1183
                                double w = dirtyZoneXml.getDoubleProperty("extent"+i+"W");
1184
                                double h = dirtyZoneXml.getDoubleProperty("extent"+i+"H");
1185
                                
1186
                                Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h);
1187
                                dirtyZones.add(rect);
1188
                        }
1189
                }
1190
                
1191
                
1192
                
1193
                XMLEntity errorContainerXML = xml.getChild(childrenCount++);
1194
                if(errorContainerXML != null)
1195
                {
1196
                        this.errorContainer = TopologyPersister.createErrorContainerFromXML(errorContainerXML);
1197
                }
1198
                
1199
                if(xml.contains("numberOfRanks")){
1200
                        int numberOfRanks = xml.getIntProperty("numberOfRanks");
1201
                        for(int i = 0; i < numberOfRanks; i++){
1202
                                XMLEntity xmlRank = xml.getChild(childrenCount++);
1203
                                XYZLayerRank rank = new XYZLayerRank();
1204
                                rank.setXMLEntity(xmlRank);
1205
                        }
1206
                }                
1207
        }
1208

    
1209
        public int getNumberOfExceptions() {
1210
                return this.errorContainer.getNumberOfExceptions();
1211
        }
1212

    
1213

    
1214

    
1215
        public String getName() {
1216
                return name;
1217
        }
1218

    
1219

    
1220

    
1221
        public void setName(String name) {
1222
                this.name = name;
1223
        }
1224
        
1225
        public ITopologyErrorContainer getErrorContainer(){
1226
                return this.errorContainer;
1227
        }
1228
        
1229
        public void setErrorContainer(ITopologyErrorContainer errorContainer){
1230
                this.errorContainer = errorContainer;
1231
        }
1232
        
1233
        private void fireStatusChange(TopologyStatusEvent event){
1234
                Iterator<ITopologyStatusListener> it = 
1235
                        this.statusListeners.iterator();
1236
                while(it.hasNext()){
1237
                        ITopologyStatusListener listener = it.next();
1238
                        listener.statusChange(event);
1239
                }
1240
        }
1241

    
1242
        public void removeErrorsByLayer(FLyrVect layer) {
1243
                this.errorContainer.removeErrorsByLayer(layer);
1244
        }
1245

    
1246
        public void removeErrorsByRule(String ruleName) {
1247
                this.errorContainer.removeErrorsByRule(ruleName);
1248
        }
1249
        
1250
        public static void copyProperties(Topology from, Topology to){
1251
                to.name = from.name;
1252
                to.ruleId = from.ruleId;
1253
                to.status = from.status;
1254
                to.maxNumberOfErrors = from.maxNumberOfErrors;
1255
                to.rules = from.rules;        
1256
                to.dirtyZones = from.dirtyZones;
1257
                to.errorContainer = from.errorContainer;
1258
                to.statusListeners = from.statusListeners;
1259
                to.layerRanks = from.layerRanks;
1260
                
1261
                List layers = from.layers;
1262
                to.layers = new ArrayList();
1263
                for(int i = 0; i < layers.size(); i++){
1264
                        to.addLayer((FLayer) layers.get(i));
1265
                }
1266
                
1267
                to.layers = from.layers;
1268
        }
1269

    
1270
}