Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.compat.cdc / org.gvsig.fmap.mapcontext / org.gvsig.fmap.mapcontext.api / src / main / java / org / gvsig / fmap / mapcontext / layers / vectorial / FLyrVect.java @ 42811

History | View | Annotate | Download (40.6 KB)

1
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright (C) 2007-2013 gvSIG Association.
5
 *
6
 * This program is free software; you can redistribute it and/or modify it under
7
 * the terms of the GNU General Public License as published by the Free Software
8
 * Foundation; either version 3 of the License, or (at your option) any later
9
 * version.
10
 *
11
 * This program is distributed in the hope that it will be useful, but WITHOUT
12
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14
 * details.
15
 *
16
 * You should have received a copy of the GNU General Public License along with
17
 * this program; if not, write to the Free Software Foundation, Inc., 51
18
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
 *
20
 * For any additional information, do not hesitate to contact us at info AT
21
 * gvsig.com, or visit our website www.gvsig.com.
22
 */
23
package org.gvsig.fmap.mapcontext.layers.vectorial;
24

    
25
import java.awt.Graphics2D;
26
import java.awt.Point;
27
import java.awt.geom.AffineTransform;
28
import java.awt.geom.Point2D;
29
import java.awt.image.BufferedImage;
30
import java.util.Iterator;
31
import java.util.Set;
32
import java.util.TreeSet;
33

    
34
import org.cresques.cts.ICoordTrans;
35
import org.cresques.cts.IProjection;
36
import org.slf4j.LoggerFactory;
37

    
38
import org.gvsig.compat.print.PrintAttributes;
39
import org.gvsig.fmap.dal.DataStore;
40
import org.gvsig.fmap.dal.exception.DataException;
41
import org.gvsig.fmap.dal.exception.ReadException;
42
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
43
import org.gvsig.fmap.dal.feature.FeatureQuery;
44
import org.gvsig.fmap.dal.feature.FeatureSet;
45
import org.gvsig.fmap.dal.feature.FeatureStore;
46
import org.gvsig.fmap.dal.feature.FeatureStoreNotification;
47
import org.gvsig.fmap.dal.feature.FeatureType;
48
import org.gvsig.fmap.dal.feature.exception.CreateGeometryException;
49
import org.gvsig.fmap.geom.Geometry;
50
import org.gvsig.fmap.geom.Geometry.SUBTYPES;
51
import org.gvsig.fmap.geom.Geometry.TYPES;
52
import org.gvsig.fmap.geom.GeometryLocator;
53
import org.gvsig.fmap.geom.GeometryManager;
54
import org.gvsig.fmap.geom.exception.CreateEnvelopeException;
55
import org.gvsig.fmap.geom.primitive.Circle;
56
import org.gvsig.fmap.geom.primitive.Envelope;
57
import org.gvsig.fmap.geom.type.GeometryType;
58
import org.gvsig.fmap.geom.type.GeometryTypeNotSupportedException;
59
import org.gvsig.fmap.geom.type.GeometryTypeNotValidException;
60
import org.gvsig.fmap.mapcontext.MapContextLocator;
61
import org.gvsig.fmap.mapcontext.MapContextManager;
62
import org.gvsig.fmap.mapcontext.ViewPort;
63
import org.gvsig.fmap.mapcontext.exceptions.LegendLayerException;
64
import org.gvsig.fmap.mapcontext.exceptions.LoadLayerException;
65
import org.gvsig.fmap.mapcontext.exceptions.ReloadLayerException;
66
import org.gvsig.fmap.mapcontext.exceptions.StartEditionLayerException;
67
import org.gvsig.fmap.mapcontext.layers.FLayer;
68
import org.gvsig.fmap.mapcontext.layers.FLyrDefault;
69
import org.gvsig.fmap.mapcontext.layers.LayerEvent;
70
import org.gvsig.fmap.mapcontext.layers.SpatialCache;
71
import org.gvsig.fmap.mapcontext.rendering.legend.ILegend;
72
import org.gvsig.fmap.mapcontext.rendering.legend.IVectorLegend;
73
import org.gvsig.fmap.mapcontext.rendering.legend.LegendException;
74
import org.gvsig.fmap.mapcontext.rendering.legend.events.FeatureDrawnNotification;
75
import org.gvsig.fmap.mapcontext.rendering.legend.events.LegendChangedEvent;
76
import org.gvsig.fmap.mapcontext.rendering.legend.events.LegendClearEvent;
77
import org.gvsig.fmap.mapcontext.rendering.legend.events.LegendContentsChangedListener;
78
import org.gvsig.fmap.mapcontext.rendering.legend.events.LegendEvent;
79
import org.gvsig.fmap.mapcontext.rendering.legend.events.SymbolLegendEvent;
80
import org.gvsig.fmap.mapcontext.rendering.legend.styling.ILabelingStrategy;
81
import org.gvsig.fmap.mapcontext.rendering.symbols.CartographicSupport;
82
import org.gvsig.metadata.exceptions.MetadataException;
83
import org.gvsig.tools.ToolsLocator;
84
import org.gvsig.tools.dynobject.DynObjectSet;
85
import org.gvsig.tools.dynobject.DynStruct;
86
import org.gvsig.tools.exception.BaseException;
87
import org.gvsig.tools.locator.LocatorException;
88
import org.gvsig.tools.observer.Observable;
89
import org.gvsig.tools.observer.Observer;
90
import org.gvsig.tools.persistence.PersistenceManager;
91
import org.gvsig.tools.persistence.PersistentState;
92
import org.gvsig.tools.persistence.exception.PersistenceException;
93
import org.gvsig.tools.task.Cancellable;
94
import org.gvsig.tools.util.Callable;
95

    
96
/**
97
 * Capa b?sica Vectorial.
98
 *
99
 */
100
public class FLyrVect extends FLyrDefault implements VectorLayer,
101
        LegendContentsChangedListener, Observer {
102

    
103
    final static private org.slf4j.Logger logger
104
            = LoggerFactory.getLogger(FLyrVect.class);
105
    private final GeometryManager geomManager
106
            = GeometryLocator.getGeometryManager();
107

    
108
    /**
109
     * Leyenda de la capa vectorial
110
     */
111
    private IVectorLegend legend;
112
    private int typeShape = -1;
113
    private FeatureStore featureStore = null;
114
    private SpatialCache spatialCache = new SpatialCache();
115

    
116
    /**
117
     * An implementation of gvSIG spatial index
118
     */
119
    // protected ISpatialIndex spatialIndex = null;
120
    private IVectorLegend loadLegend = null;
121

    
122
    private boolean isLabeled;
123
    protected ILabelingStrategy strategy;
124
//        private ReprojectDefaultGeometry reprojectTransform;
125
    private FeatureQuery baseQuery = null;
126

    
127
    public FLyrVect() {
128
        super();
129
    }
130

    
131
    public String getTocImageIcon() {
132
        if (this.isAvailable()) {
133
            return MapContextLocator.getMapContextManager().getIconLayer(this.getDataStore());
134
        } else {
135
            /*
136
             * data store can be be null,
137
             * for example, a layer not loaded from persistence
138
             */
139
            return "layer-icon-unavailable";
140
        }
141

    
142
    }
143

    
144
    /**
145
     * Devuelve el VectorialAdapater de la capa.
146
     *
147
     * @return VectorialAdapter.
148
     */
149
    public DataStore getDataStore() {
150
//        if (!this.isAvailable()) {
151
//            return null;
152
//        }
153
        return featureStore;
154
    }
155

    
156
    /**
157
     * Asigna el data-store a la capa. Esta operacion no se deneria poder hacer
158
     * desde fuera de la clase.
159
     *
160
     * @param dataStore
161
     * @throws LoadLayerException
162
     * @deprecated use {@link #bindToDataStore(DataStore)}
163
     */
164
    public void setDataStore(DataStore dataStore) throws LoadLayerException {
165
        bindToDataStore(dataStore);
166
    }
167

    
168
    /**
169
     * Enlaza la capa con el DataStore indicado.
170
     *
171
     * @param dataStore
172
     * @throws LoadLayerException
173
     */
174
    protected void bindToDataStore(DataStore dataStore) throws LoadLayerException {
175
        if (this.featureStore != null && this.featureStore != dataStore) {
176
            this.featureStore.deleteObserver(this);
177
        }
178

    
179
        featureStore = (FeatureStore) dataStore;
180

    
181
        MapContextManager mapContextManager
182
                = MapContextLocator.getMapContextManager();
183

    
184
        //Set the legend
185
        IVectorLegend legend
186
                = (IVectorLegend) mapContextManager.getLegend(dataStore);
187

    
188
        if (legend == null) {
189
            throw new LegendLayerException(this.getName());
190
        }
191

    
192
        this.setLegend(legend);
193

    
194
        //Set the labeling strategy
195
        ILabelingStrategy labeler
196
                = (ILabelingStrategy) mapContextManager.getLabelingStrategy(dataStore);
197

    
198
        if (labeler != null) {
199
            labeler.setLayer(this);
200
            this.setLabelingStrategy(labeler);
201
            this.setIsLabeled(true); // TODO: ac? no s'hauria de detectar si t?
202
            // etiquetes?????
203
        }
204

    
205
        this.delegate(dataStore);
206

    
207
        dataStore.addObserver(this);
208

    
209
        ToolsLocator.getDisposableManager().bind(dataStore);
210
    }
211

    
212
    public Envelope getFullEnvelope() throws ReadException {
213
        Envelope rAux;
214
        try {
215
            rAux = getFeatureStore().getEnvelope();
216
        } catch (BaseException e) {
217
            throw new ReadException(getName(), e);
218
        }
219

    
220
        // Esto es para cuando se crea una capa nueva con el fullExtent de ancho
221
        // y alto 0.
222
        if (rAux == null || rAux.isEmpty() || rAux.getMaximum(0) - rAux.getMinimum(0) == 0
223
                && rAux.getMaximum(1) - rAux.getMinimum(1) == 0) {
224
            try {
225
                rAux
226
                        = geomManager.createEnvelope(0, 0, 90, 90, SUBTYPES.GEOM2D);
227
            } catch (CreateEnvelopeException e) {
228
                logger.error("Error creating the envelope", e);
229
                e.printStackTrace();
230
            }
231
        }
232
        // Si existe reproyecci?n, reproyectar el extent
233
        ICoordTrans ct = getCoordTrans();
234
        if (ct != null) {
235
            rAux = rAux.convert(ct);
236
        }
237
        return rAux;
238

    
239
    }
240
    
241
    public void setBaseQuery(FeatureQuery baseQuery) {
242
        this.baseQuery = baseQuery;
243
    }
244

    
245
    @Override
246
    public FeatureQuery getBaseQuery() {
247
        return this.baseQuery;
248
    }
249

    
250
    
251
    /**
252
     * Draws using IFeatureIterator. This method will replace the old draw(...)
253
     * one.
254
     *
255
     * @autor jaume dominguez faus - jaume.dominguez@iver.es
256
     * @param image
257
     * @param g
258
     * @param viewPort
259
     * @param cancel
260
     * @param scale
261
     * @throws ReadDriverException
262
     */
263
    public void draw(BufferedImage image,
264
            Graphics2D g,
265
            ViewPort viewPort,
266
            Cancellable cancel,
267
            double scale) throws ReadException {
268

    
269
        if (legend == null) {
270
            return;
271
        }
272

    
273
        if (!this.isWithinScale(scale)) {
274
            return;
275
        }
276
        if (cancel.isCanceled()) {
277
            return;
278
        }
279

    
280
        if (spatialCache.isEnabled()) {
281
            spatialCache.clearAll();
282
            legend.addDrawingObserver(this);
283
        }
284

    
285
        FeatureQuery featureQuery = createFeatureQuery();
286

    
287
        try {
288
            FeatureAttributeDescriptor featureAttributeDescriptor
289
                    = getFeatureStore().getDefaultFeatureType().getDefaultTimeAttribute();
290

    
291
            if ((viewPort.getTime() != null) && (featureAttributeDescriptor != null)) {
292
                IntersectsTimeEvaluator intersectsTimeEvaluator
293
                        = new IntersectsTimeEvaluator(viewPort.getTime(), featureAttributeDescriptor.getName());
294
                featureQuery.addFilter(intersectsTimeEvaluator);
295
            }
296
        } catch (DataException e1) {
297
            logger.error("Impossible to get the temporal filter", e1);
298
        }
299

    
300
        try {
301

    
302
            long tini = System.currentTimeMillis();
303

    
304
            legend.draw(image,
305
                    g,
306
                    viewPort,
307
                    cancel,
308
                    scale,
309
                    null,
310
                    getCoordTrans(),
311
                    getFeatureStore(),
312
                    featureQuery);
313

    
314
            logger.debug("Layer " + this.getName() + " drawn in "
315
                    + (System.currentTimeMillis() - tini) + " milliseconds.");
316

    
317
        } catch (LegendException e) {
318
            this.setAvailable(false);
319
            this.setError(e);
320
            throw new ReadException(getName(), e);
321
        } finally {
322
            if (spatialCache.isEnabled()) {
323
                legend.deleteDrawingObserver(this);
324
            }
325
        }
326
    }
327

    
328
    public void print(Graphics2D g,
329
            ViewPort viewPort,
330
            Cancellable cancel,
331
            double scale,
332
            PrintAttributes properties) throws ReadException {
333
        if (!this.isWithinScale(scale)) {
334
            return;
335
        }
336
        if (cancel.isCanceled()) {
337
            return;
338
        }
339
        FeatureQuery featureQuery = createFeatureQuery();
340

    
341
        try {
342
            legend.print(g,
343
                    viewPort,
344
                    cancel,
345
                    scale,
346
                    null,
347
                    getCoordTrans(),
348
                    getFeatureStore(),
349
                    featureQuery,
350
                    properties);
351

    
352
        } catch (LegendException e) {
353
            this.setVisible(false);
354
            this.setActive(false);
355
            throw new ReadException(getName(), e);
356
        }
357
    }
358

    
359
    public void setLegend(IVectorLegend legend) throws LegendLayerException {
360
        if (this.legend == legend) {
361
            return;
362
        }
363
        if (this.legend != null && this.legend.equals(legend)) {
364
            return;
365
        }
366
        IVectorLegend oldLegend = this.legend;
367
        this.legend = legend;
368
        if (oldLegend != null) {
369
            oldLegend.removeLegendListener(this);
370
            oldLegend.deleteDrawingObserver(this);
371
        }
372
        if (legend != null) {
373
            this.legend.addDrawingObserver(this);
374
            this.legend.addLegendListener(this);
375
        }
376
        LegendChangedEvent e = LegendChangedEvent.createLegendChangedEvent(oldLegend, this.legend);
377
        e.setLayer(this);
378
        updateDrawVersion();
379
        callLegendChanged(e);
380
    }
381

    
382
    /**
383
     * Devuelve la Leyenda de la capa.
384
     *
385
     * @return Leyenda.
386
     */
387
    public ILegend getLegend() {
388
        return legend;
389
    }
390

    
391
    public int getShapeType() throws ReadException {
392
        if (typeShape == -1) {
393
            FeatureType featureType = null;
394
            try {
395
                if (getDataStore() != null) {
396
                    featureType
397
                            = (((FeatureStore) getDataStore()).getDefaultFeatureType());
398
                }
399
            } catch (DataException e) {
400
                throw new ReadException(getName(), e);
401
            }
402
            if (featureType != null) {
403
                int indexGeom = featureType.getDefaultGeometryAttributeIndex();
404
                typeShape
405
                        = featureType.getAttributeDescriptor(indexGeom).getGeometryType();
406
            }
407
        }
408
        return typeShape;
409
    }
410

    
411
    /**
412
     * Returns the layer's geometry type
413
     *
414
     * @return the geometry type
415
     *
416
     * @throws ReadException if there is an error getting the geometry type
417
     */
418
    public GeometryType getGeometryType() throws ReadException {
419
        FeatureType featureType = null;
420
        try {
421
            if (getDataStore() != null) {
422
                featureType
423
                        = (((FeatureStore) getDataStore()).getDefaultFeatureType());
424
            }
425
        } catch (DataException e) {
426
            throw new ReadException(getName(), e);
427
        }
428
        return featureType == null ? null : featureType
429
                .getDefaultGeometryAttribute().getGeomType();
430
    }
431

    
432
    public void saveToState(PersistentState state) throws PersistenceException {
433

    
434
        FeatureStore featureStore = null;
435

    
436
        if (!this.isAvailable()) {
437
            logger.info("The '" + this.getName() + "' layer is not available, it will persist not.");
438
            return;
439
        }
440

    
441
        try {
442
            super.saveToState(state);
443

    
444
            if (getLegend() != null) {
445
                state.set("legend", getLegend());
446
            }
447

    
448
            featureStore = getFeatureStore();
449

    
450
            if (featureStore != null) {
451
                state.set("featureStore", featureStore);
452
            }
453

    
454
            state.set("isLabeled", isLabeled);
455

    
456
            if (strategy != null) {
457
                state.set("labelingStrategy", strategy);
458
            }
459

    
460
            if (getLinkProperties() != null) {
461
                state.set("linkProperties", getLinkProperties());
462
            }
463

    
464
            state.set("typeShape", typeShape);
465
        } catch (PersistenceException ex) {
466
            logger.warn("Can't persist to state the layer '" + this.getName() + "'.", ex);
467
            throw ex;
468
        } catch (RuntimeException ex) {
469
            logger.warn("Can't persist to state the layer '" + this.getName() + "'.", ex);
470
            throw ex;
471
        }
472

    
473
    }
474

    
475
    public void loadFromState(PersistentState state) throws PersistenceException {
476

    
477
        DataStore store = null;
478
        IVectorLegend vectorLegend = null;
479
        ILabelingStrategy labelingStrategy = null;
480
        Boolean isLabeled = Boolean.FALSE;
481

    
482
        try {
483
            super.loadFromState(state);
484
            store = (DataStore) state.get("featureStore");
485

    
486
            try {
487
                this.bindToDataStore(store);
488
            } catch (LoadLayerException e) {
489
                throw new PersistenceException("Can't bind layer '" + this.getName() + "' to store '" + store.getFullName() + "'.", e);
490
            }
491

    
492
            vectorLegend = (IVectorLegend) state.get("legend");
493

    
494
            try {
495
                this.setLegend(vectorLegend);
496
            } catch (LegendLayerException e) {
497
                throw new PersistenceException("Can't set vector legend to the layer.", e);
498
            }
499

    
500
            try {
501
                isLabeled = (Boolean) state.get("isLabeled");
502
                if (isLabeled.booleanValue()) {
503
                    labelingStrategy = (ILabelingStrategy) state.get("labelingStrategy");
504
                }
505
            } catch (Exception ex) {
506
                throw new PersistenceException("Can't load labeling strategi from persistent state.",
507
                        ex);
508
            }
509

    
510
            if (isLabeled.booleanValue()) {
511
                this.setIsLabeled(true);
512
                this.setLabelingStrategy(labelingStrategy);
513
            } else {
514
                this.setIsLabeled(false);
515
                this.setLabelingStrategy(null);
516
            }
517

    
518
            typeShape = state.getInt("typeShape");
519

    
520
        } catch (Throwable e) {
521
            String storeName = (store == null) ? "unknow" : store.getFullName();
522
            logger.warn("can't load layer '" + this.getName() + "' (store=" + storeName + ") from persisted state.", e);
523
            this.setAvailable(false);
524
            return;
525
        }
526

    
527
    }
528

    
529
    /**
530
     * Sobreimplementaci?n del m?todo toString para que las bases de datos
531
     * identifiquen la capa.
532
     *
533
     * @return DOCUMENT ME!
534
     */
535
    public String toString() {
536
        /*
537
         * Se usa internamente para que la parte de datos identifique de forma
538
         * un?voca las tablas
539
         */
540
        String ret = super.toString();
541

    
542
        return ret ; //"layer" + ret.substring(ret.indexOf('@') + 1);
543
    }
544

    
545
    public boolean isEditing() {
546
        FeatureStore fs = getFeatureStore();
547
        if (fs == null) {
548
            /*
549
             * This happens when layer is not available, for example,
550
             * it was not possible to load from persistence
551
             */
552
            return false;
553
        } else {
554
            return fs.isEditing();
555
        }
556
    }
557

    
558
    public void setEditing(boolean b) throws StartEditionLayerException {
559

    
560
        try {
561
            throw new RuntimeException();
562
        } catch (Throwable th) {
563
            logger.info("This method is deprecated. ", th);
564
        }
565

    
566
        if (b == super.isEditing()) {
567
            return;
568
        }
569

    
570
        super.setEditing(b);
571
        FeatureStore fs = getFeatureStore();
572
        if (b) {
573
            try {
574
                fs.edit();
575
            } catch (DataException e) {
576
                throw new StartEditionLayerException(getName(), e);
577
            }
578
        }
579
        setSpatialCacheEnabled(b);
580
        callEditionChanged(LayerEvent.createEditionChangedEvent(this, "edition"));
581
    }
582

    
583
    /**
584
     * @deprecated Use {@link #getSpatialCache()}
585
     */
586
    public void clearSpatialCache() {
587
        spatialCache.clearAll();
588
    }
589

    
590
    /**
591
     * @deprecated Use {@link #getSpatialCache()}
592
     */
593
    public boolean isSpatialCacheEnabled() {
594
        return spatialCache.isEnabled();
595
    }
596

    
597
    /**
598
     * @deprecated Use {@link #getSpatialCache()}
599
     */
600
    public void setSpatialCacheEnabled(boolean spatialCacheEnabled) {
601
        spatialCache.setEnabled(spatialCacheEnabled);
602
    }
603

    
604
    public SpatialCache getSpatialCache() {
605
        return spatialCache;
606
    }
607

    
608
    /**
609
     * Siempre es un numero mayor de 1000
610
     *
611
     * @param maxFeatures
612
     */
613
    public void setMaxFeaturesInEditionCache(int maxFeatures) {
614
        if (maxFeatures > spatialCache.getMaxFeatures()) {
615
            spatialCache.setMaxFeatures(maxFeatures);
616
        }
617

    
618
    }
619

    
620
    /**
621
     * This method returns a boolean that is used by the FPopMenu to make
622
     * visible the properties menu or not. It is visible by default, and if a
623
     * later don't have to show this menu only has to override this method.
624
     *
625
     * @return If the properties menu is visible (or not)
626
     */
627
    public boolean isPropertiesMenuVisible() {
628
        return true;
629
    }
630

    
631
    public void reload() throws ReloadLayerException {
632
        super.reload();
633
        try {
634
            getFeatureStore().refresh();
635
        } catch (Exception e) {
636
            throw new ReloadLayerException(getName(), e);
637
        }
638
    }
639

    
640
    protected void setLoadSelection(Object xml) {
641
        // this.loadSelection = xml;
642
    }
643

    
644
    protected void setLoadLegend(IVectorLegend legend) {
645
        this.loadLegend = legend;
646
    }
647

    
648
    protected void putLoadSelection() {
649
        // if (this.loadSelection == null) return;
650
        // try {
651
        // this.getRecordset().getSelectionSupport().setXMLEntity(this.loadSelection);
652
        // } catch (ReadDriverException e) {
653
        // throw new XMLException(e);
654
        // }
655
        // this.loadSelection = null;
656

    
657
    }
658

    
659
    protected void putLoadLegend() throws LegendLayerException {
660
        if (this.loadLegend == null) {
661
            return;
662
        }
663
        this.setLegend(this.loadLegend);
664
        this.loadLegend = null;
665
    }
666

    
667
    protected void cleanLoadOptions() {
668
        this.loadLegend = null;
669
    }
670

    
671
    public boolean isWritable() {
672
        return getFeatureStore().allowWrite();
673
    }
674

    
675
    public FLayer cloneLayer() throws Exception {
676
        FLyrVect clonedLayer = new FLyrVect();
677
        clonedLayer.bindToDataStore(getDataStore());
678
        // if (isJoined()) {
679
        // clonedLayer.setIsJoined(true);
680
        // }
681
        clonedLayer.setVisible(isVisible());
682
        // clonedLayer.setISpatialIndex(getISpatialIndex());
683
        clonedLayer.setName(getName());
684
        clonedLayer.setCoordTrans(getCoordTrans());
685

    
686
        clonedLayer.setLegend((IVectorLegend) getLegend().cloneLegend());
687

    
688
        clonedLayer.setIsLabeled(isLabeled());
689
        ILabelingStrategy labelingStrategy = getLabelingStrategy();
690
        if (labelingStrategy != null) {
691
            clonedLayer.setLabelingStrategy(labelingStrategy);
692
        }
693

    
694
        return clonedLayer;
695
    }
696

    
697
    protected boolean isOnePoint(AffineTransform graphicsTransform,
698
            ViewPort viewPort,
699
            double dpi,
700
            CartographicSupport csSym,
701
            Geometry geom,
702
            int[] xyCoords) {
703
        return isOnePoint(graphicsTransform, viewPort, geom, xyCoords)
704
                && csSym.getCartographicSize(viewPort, dpi, geom) <= 1;
705
    }
706

    
707
    private boolean isOnePoint(AffineTransform graphicsTransform,
708
            ViewPort viewPort,
709
            Geometry geom,
710
            int[] xyCoords) {
711
        boolean onePoint = false;
712
        int type = geom.getType();
713
        if (type == Geometry.TYPES.NULL) {
714
            return false;
715
        }
716
        if (type != Geometry.TYPES.POINT && type != Geometry.TYPES.MULTIPOINT) {
717

    
718
            Envelope geomBounds = geom.getEnvelope();
719

    
720
            // ICoordTrans ct = getCoordTrans();
721
            // Se supone que la geometria ya esta reproyectada
722
            // if (ct!=null) {
723
            // // geomBounds = ct.getInverted().convert(geomBounds);
724
            // geomBounds = geomBounds.convert(ct);
725
            // }
726
            double dist1Pixel = viewPort.getDist1pixel();
727

    
728
            onePoint
729
                    = (geomBounds.getLength(0) <= dist1Pixel && geomBounds.getLength(1) <= dist1Pixel);
730

    
731
            if (onePoint) {
732
                // avoid out of range exceptions
733
                org.gvsig.fmap.geom.primitive.Point p;
734
                try {
735
                    p
736
                            = geomManager.createPoint(geomBounds.getMinimum(0),
737
                                    geomBounds.getMinimum(1),
738
                                    SUBTYPES.GEOM2D);
739
                    p.transform(viewPort.getAffineTransform());
740
                    p.transform(graphicsTransform);
741
                    xyCoords[0] = (int) p.getX();
742
                    xyCoords[1] = (int) p.getY();
743
                } catch (org.gvsig.fmap.geom.exception.CreateGeometryException e) {
744
                    logger.error("Error creating a point", e);
745
                }
746

    
747
            }
748

    
749
        }
750
        return onePoint;
751
    }
752

    
753
    public boolean isLabeled() {
754
        return isLabeled;
755
    }
756

    
757
    public void setIsLabeled(boolean isLabeled) {
758
        this.isLabeled = isLabeled;
759
    }
760

    
761
    public ILabelingStrategy getLabelingStrategy() {
762
        return strategy;
763
    }
764

    
765
    public void setLabelingStrategy(ILabelingStrategy strategy) {
766
        this.strategy = strategy;
767
        if (strategy == null) {
768
            return;
769
        }
770
        strategy.setLayer(this);
771
        updateDrawVersion();
772
    }
773

    
774
    public void drawLabels(BufferedImage image,
775
            Graphics2D g,
776
            ViewPort viewPort,
777
            Cancellable cancel,
778
            double scale,
779
            double dpi) throws ReadException {
780
        if (strategy != null && isWithinScale(scale)) {
781
            strategy.draw(image, g, scale, viewPort, cancel, dpi);
782
        }
783
    }
784

    
785
    public void printLabels(Graphics2D g,
786
            ViewPort viewPort,
787
            Cancellable cancel,
788
            double scale,
789
            PrintAttributes properties) throws ReadException {
790
        if (strategy != null) {
791
            strategy.print(g, scale, viewPort, cancel, properties);
792
        }
793
    }
794

    
795
    /**
796
     * Return true, because a Vectorial Layer supports HyperLink
797
     *
798
     * @deprecated the hiperlink functionaliti is out the layer now
799
     */
800
    public boolean allowLinks() {
801
        return false;
802
    }
803

    
804
    public void load() throws LoadLayerException {
805
        super.load();
806
    }
807

    
808
    public FeatureStore getFeatureStore() {
809
        return (FeatureStore) getDataStore();
810
    }
811

    
812
    public FeatureQuery createFeatureQuery() {
813
        if( this.baseQuery==null ) {
814
            return this.getFeatureStore().createFeatureQuery();
815
        }
816
        try {
817
            return (FeatureQuery) baseQuery.clone();
818
        } catch (CloneNotSupportedException ex) {
819
            throw new RuntimeException(ex);
820
        }
821
    }
822
    
823
    /**
824
     * @deprecated use instead
825
     * {@link #queryByPoint(org.gvsig.fmap.geom.primitive.Point, double, FeatureType)}
826
     */
827
    public FeatureSet queryByPoint(Point2D mapPoint,
828
            double tol,
829
            FeatureType featureType) throws DataException {
830
        logger.warn("Deprecated use of queryByPoint.");
831
        GeometryManager manager = GeometryLocator.getGeometryManager();
832
        org.gvsig.fmap.geom.primitive.Point center;
833
        try {
834
            center
835
                    = (org.gvsig.fmap.geom.primitive.Point) manager.create(TYPES.POINT,
836
                            SUBTYPES.GEOM2D);
837
            center.setX(mapPoint.getX());
838
            center.setY(mapPoint.getY());
839
            Circle circle
840
                    = (Circle) manager.create(TYPES.CIRCLE, SUBTYPES.GEOM2D);
841
            circle.setPoints(center, tol);
842
            return queryByGeometry(circle, featureType);
843
        } catch (org.gvsig.fmap.geom.exception.CreateGeometryException e) {
844
            throw new CreateGeometryException(TYPES.CIRCLE, SUBTYPES.GEOM2D, e);
845
        }
846
    }
847

    
848
    public FeatureSet queryByPoint(org.gvsig.fmap.geom.primitive.Point point,
849
            double tol,
850
            FeatureType featureType) throws DataException {
851
        GeometryManager manager = GeometryLocator.getGeometryManager();
852
        try {
853
            Circle circle
854
                    = (Circle) manager.create(TYPES.CIRCLE, SUBTYPES.GEOM2D);
855
            circle.setPoints(point, tol);
856
            return queryByGeometry(circle, featureType);
857
        } catch (org.gvsig.fmap.geom.exception.CreateGeometryException e) {
858
            throw new CreateGeometryException(TYPES.CIRCLE, SUBTYPES.GEOM2D, e);
859
        }
860
    }
861

    
862
    /**
863
     * Input geom must be in the CRS of the view.
864
     *
865
     * @param geom
866
     * @param featureType
867
     * @return
868
     * @throws DataException
869
     */
870
    public FeatureSet queryByGeometry(Geometry geom, FeatureType featureType) throws DataException {
871
        FeatureQuery featureQuery = createFeatureQuery();
872
        String geomName
873
                = featureStore.getDefaultFeatureType()
874
                .getDefaultGeometryAttributeName();
875
        featureQuery.setFeatureType(featureType);
876

    
877
        Geometry query_geo = this.transformToSourceCRS(geom, true);
878
        IProjection query_proj = getMapContext().getProjection();
879
        if (this.getCoordTrans() != null) {
880
            query_proj = this.getCoordTrans().getPOrig();
881
        }
882

    
883
        IntersectsGeometryEvaluator iee
884
                = new IntersectsGeometryEvaluator(
885
                        query_geo,
886
                        query_proj,
887
                        featureStore.getDefaultFeatureType(),
888
                        geomName);
889
        featureQuery.setFilter(iee);
890
        featureQuery.setAttributeNames(null);
891
        return getFeatureStore().getFeatureSet(featureQuery);
892

    
893
    }
894

    
895
    /**
896
     * It return the {@link FeatureSet} that intersects with the envelope.
897
     *
898
     * @param envelope envelope that defines the area for the query.
899
     * @param featureType only the features with this feature type are used in
900
     * the query.
901
     * @return the set of features that intersect with the envelope.
902
     * @throws DataException
903
     */
904
    public FeatureSet queryByEnvelope(Envelope envelope, FeatureType featureType) throws DataException {
905
        return queryByEnvelope(envelope, featureType, null);
906
    }
907

    
908
    /**
909
     * It return the {@link FeatureSet} that intersects with the envelope.
910
     *
911
     * @param envelope envelope that defines the area for the query in viewport
912
     * CRS
913
     * @param featureType only the features with this feature type are used in
914
     * the query.
915
     * @param names the feature attributes that have to be checked.
916
     * @return the set of features that intersect with the envelope.
917
     * @throws DataException
918
     */
919
    public FeatureSet queryByEnvelope(Envelope envelope,
920
            FeatureType featureType,
921
            String[] names) throws DataException {
922
        FeatureQuery featureQuery = createFeatureQuery();
923
        if (names == null) {
924
            featureQuery.setFeatureType(featureType);
925
        } else {
926
            featureQuery.setAttributeNames(names);
927
            featureQuery.setFeatureTypeId(featureType.getId());
928
        }
929
        String geomName = featureStore.getDefaultFeatureType()
930
                .getDefaultGeometryAttributeName();
931

    
932
        Envelope query_env = fromViewPortCRSToSourceCRS(this, envelope);
933
        IProjection query_proj = getMapContext().getProjection();
934
        if (this.getCoordTrans() != null) {
935
            query_proj = this.getCoordTrans().getPOrig();
936
        }
937

    
938
        IntersectsGeometryEvaluator iee
939
                = new IntersectsGeometryEvaluator(
940
                        query_env.getGeometry(), query_proj,
941
                        featureStore.getDefaultFeatureType(),
942
                        geomName);
943
        featureQuery.setFilter(iee);
944
        return getFeatureStore().getFeatureSet(featureQuery);
945

    
946
    }
947

    
948
    public DynObjectSet getInfo(Point p, double tolerance, Cancellable cancel) throws LoadLayerException,
949
            DataException {
950

    
951
        return getInfo(p, tolerance, cancel, true);
952
    }
953

    
954
    public DynObjectSet getInfo(Point p,
955
            double tolerance,
956
            Cancellable cancel,
957
            boolean fast) throws LoadLayerException, DataException {
958
        Point2D infop = new Point2D.Double(p.x, p.y);
959
        Point2D pReal = this.getMapContext().getViewPort().toMapPoint(infop);
960
        return queryByPoint(pReal,
961
                tolerance,
962
                getFeatureStore().getDefaultFeatureType()).getDynObjectSet(fast);
963
    }
964

    
965
    public DynObjectSet getInfo(org.gvsig.fmap.geom.primitive.Point p,
966
            double tolerance) throws LoadLayerException, DataException {
967
        return queryByPoint(p, tolerance, getFeatureStore().getDefaultFeatureType()).getDynObjectSet(false);
968
    }
969

    
970
    @Override
971
    public void legendCleared(LegendClearEvent event) {
972
        this.updateDrawVersion();
973
        LegendChangedEvent e = LegendChangedEvent.createLegendChangedEvent(legend,event);
974
        this.callLegendChanged(e);
975
    }
976

    
977
    @Override
978
    public boolean symbolChanged(SymbolLegendEvent e) {
979
        this.updateDrawVersion();
980
        LegendChangedEvent ev = LegendChangedEvent.createLegendChangedEvent(legend, e);
981
        this.callLegendChanged(ev);
982
        return true;
983
    }
984

    
985
    public void update(Observable observable, Object notification) {
986
        if (observable.equals(this.featureStore)) {
987
            if (notification instanceof FeatureStoreNotification) {
988
                FeatureStoreNotification event
989
                        = (FeatureStoreNotification) notification;
990
                if (event.getType() == FeatureStoreNotification.AFTER_DELETE
991
                        || event.getType() == FeatureStoreNotification.AFTER_UNDO
992
                        || event.getType() == FeatureStoreNotification.AFTER_REDO
993
                        || event.getType() == FeatureStoreNotification.AFTER_REFRESH
994
                        || event.getType() == FeatureStoreNotification.AFTER_UPDATE
995
                        || event.getType() == FeatureStoreNotification.AFTER_UPDATE_TYPE
996
                        || event.getType() == FeatureStoreNotification.SELECTION_CHANGE
997
                        || event.getType() == FeatureStoreNotification.AFTER_INSERT) {
998
                    this.updateDrawVersion();
999

    
1000
                } else if (event.getType() == FeatureStoreNotification.AFTER_CANCELEDITING) {
1001

    
1002
                    setSpatialCacheEnabled(false);
1003
                    callEditionChanged(LayerEvent.createEditionChangedEvent(this, "edition"));
1004
                    this.updateDrawVersion();
1005

    
1006
                } else if (event.getType() == FeatureStoreNotification.AFTER_STARTEDITING) {
1007

    
1008
                    setSpatialCacheEnabled(true);
1009
                    callEditionChanged(LayerEvent.createEditionChangedEvent(this, "edition"));
1010

    
1011
                } else if (event.getType() == FeatureStoreNotification.TRANSFORM_CHANGE) {
1012
                    //If a transform has to be applied, try to reload the layer.
1013
                    try {
1014
                        reload();
1015
                    } catch (ReloadLayerException e) {
1016
                        logger.info("While reloading layer.", e);
1017
                        this.setAvailable(false);
1018
                    }
1019
                } else if (event.getType() == FeatureStoreNotification.RESOURCE_CHANGED) {
1020
                    this.setAvailable(false);
1021
                } else if (event.getType() == FeatureStoreNotification.AFTER_FINISHEDITING) {
1022
                    this.setAvailable(true);
1023
                    setSpatialCacheEnabled(false);
1024
                    callEditionChanged(LayerEvent.createEditionChangedEvent(this, "edition"));
1025
                    this.updateDrawVersion();
1026
                }
1027
            }
1028
        } else if (notification instanceof FeatureDrawnNotification
1029
                && (isEditing() || isLayerToSnap())) {
1030
            // This code is needed in editing mode
1031
            // for all layers involved in snapping
1032
            // (including the layer being edited)
1033
            Geometry geometry
1034
                    = ((FeatureDrawnNotification) notification).getDrawnGeometry();
1035
            spatialCache.insert(geometry.getEnvelope(), geometry);
1036
        }
1037
    }
1038

    
1039
    private boolean isLayerToSnap() {
1040

    
1041
        if (this.getMapContext() == null) {
1042
            /*
1043
             * This happens with the graphics layer because it has no parent
1044
             */
1045
            return false;
1046
        } else {
1047
            return this.getMapContext().getLayersToSnap().contains(this);
1048
        }
1049

    
1050
        /*
1051
         Iterator itersnap = this.getMapContext().getLayersToSnap().iterator();
1052
         Object item = null;
1053
         while (itersnap.hasNext()) {
1054
         item = itersnap.next();
1055
         if (item == this) {
1056
         return true;
1057
         }
1058
         }
1059
         return false;
1060
         */
1061
    }
1062

    
1063
    /*
1064
     * (non-Javadoc)
1065
     * 
1066
     * @see org.gvsig.metadata.Metadata#getMetadataChildren()
1067
     */
1068
    public Set getMetadataChildren() {
1069
        Set ret = new TreeSet();
1070
        ret.add(this.featureStore);
1071
        return ret;
1072
    }
1073

    
1074
    /*
1075
     * (non-Javadoc)
1076
     * 
1077
     * @see org.gvsig.metadata.Metadata#getMetadataID()
1078
     */
1079
    public Object getMetadataID() throws MetadataException {
1080
        return "Layer(" + this.getName() + "):"
1081
                + this.featureStore.getMetadataID();
1082
    }
1083

    
1084
    public GeometryType getTypeVectorLayer() throws DataException,
1085
            LocatorException,
1086
            GeometryTypeNotSupportedException,
1087
            GeometryTypeNotValidException {
1088
        // FIXME Esto deberia de pedirse a FType!!!!
1089
        FeatureStore fs = this.getFeatureStore();
1090
        FeatureType fType = fs.getDefaultFeatureType();
1091
        FeatureAttributeDescriptor attr
1092
                = fType.getAttributeDescriptor(fType.getDefaultGeometryAttributeIndex());
1093
        GeometryType geomType
1094
                = GeometryLocator.getGeometryManager()
1095
                .getGeometryType(attr.getGeometryType(),
1096
                        attr.getGeometrySubType());
1097
        return geomType;
1098
    }
1099

    
1100
    public static class RegisterPersistence implements Callable {
1101

    
1102
        public Object call() {
1103
            PersistenceManager manager = ToolsLocator.getPersistenceManager();
1104

    
1105
            DynStruct definition
1106
                    = manager.addDefinition(FLyrVect.class,
1107
                            "FLyrVect",
1108
                            "FLyrVect Persistence definition",
1109
                            null,
1110
                            null);
1111
            definition.extend(PersistenceManager.PERSISTENCE_NAMESPACE,
1112
                    "FLyrDefault");
1113

    
1114
            definition.addDynFieldObject("legend")
1115
                    .setClassOfValue(IVectorLegend.class)
1116
                    .setMandatory(true);
1117
            definition.addDynFieldObject("featureStore")
1118
                    .setClassOfValue(FeatureStore.class)
1119
                    .setMandatory(true);
1120
            definition.addDynFieldBoolean("isLabeled").setMandatory(true);
1121
            definition.addDynFieldInt("typeShape").setMandatory(true);
1122
            definition.addDynFieldObject("labelingStrategy")
1123
                    .setClassOfValue(ILabelingStrategy.class)
1124
                    .setMandatory(false);
1125

    
1126
            return Boolean.TRUE;
1127
        }
1128
    }
1129

    
1130
    protected void doDispose() throws BaseException {
1131
        dispose(featureStore);
1132
        spatialCache.clearAll();
1133
    }
1134

    
1135
    /**
1136
     * Returns envelope in layer's data source CRS from envelope provided in
1137
     * viewport CRS
1138
     *
1139
     * @param lyr
1140
     * @param env
1141
     * @return
1142
     */
1143
    public static Envelope fromViewPortCRSToSourceCRS(FLayer lyr, Envelope env) {
1144

    
1145
        if (lyr == null || env == null) {
1146
            return null;
1147
        }
1148

    
1149
        ICoordTrans ct = lyr.getCoordTrans();
1150
        if (ct == null) {
1151
            return env;
1152
        } else {
1153
            return env.convert(ct.getInverted());
1154
        }
1155
    }
1156

    
1157
    public Geometry transformToSourceCRS(Geometry geom, boolean clone) {
1158
        return fromViewPortCRSToSourceCRS(this, geom, clone);
1159
    }
1160

    
1161
    /**
1162
     * Returns geometry in layer's data source CRS from geometry provided in
1163
     * viewport CRS
1164
     *
1165
     * @param lyr
1166
     * @param geo
1167
     * @param clone
1168
     * @return
1169
     * @deprecated use the transformToSourceCRS method of layer.
1170
     */
1171
    public static Geometry fromViewPortCRSToSourceCRS(
1172
            FLayer lyr,
1173
            Geometry geo,
1174
            boolean clone) {
1175

    
1176
        if (lyr == null || geo == null) {
1177
            return null;
1178
        }
1179
        ICoordTrans ct = lyr.getCoordTrans();
1180
        Geometry resp = geo;
1181
        if (clone) {
1182
            resp = resp.cloneGeometry();
1183
        }
1184
        if (ct != null) {
1185
            resp.reProject(ct.getInverted());
1186
        }
1187
        return resp;
1188
    }
1189

    
1190
    public Iterator iterator() {
1191
        return this.getFeatureStore().iterator();
1192
    }
1193
}