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 @ 42877

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

    
99
/**
100
 * Capa b?sica Vectorial.
101
 *
102
 */
103
public class FLyrVect extends FLyrDefault implements VectorLayer,
104
        LegendContentsChangedListener, Observer {
105

    
106
    final static private org.slf4j.Logger logger
107
            = LoggerFactory.getLogger(FLyrVect.class);
108
    private final GeometryManager geomManager
109
            = GeometryLocator.getGeometryManager();
110

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

    
119
    /**
120
     * An implementation of gvSIG spatial index
121
     */
122
    // protected ISpatialIndex spatialIndex = null;
123
    private IVectorLegend loadLegend = null;
124

    
125
    private boolean isLabeled;
126
    protected ILabelingStrategy strategy;
127
//        private ReprojectDefaultGeometry reprojectTransform;
128
    private FeatureQuery baseQuery = null;
129

    
130
    public FLyrVect() {
131
        super();
132
    }
133

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

    
145
    }
146

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

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

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

    
182
        featureStore = (FeatureStore) dataStore;
183

    
184
        MapContextManager mapContextManager
185
                = MapContextLocator.getMapContextManager();
186

    
187
        //Set the legend
188
        IVectorLegend legend
189
                = (IVectorLegend) mapContextManager.getLegend(dataStore);
190

    
191
        if (legend == null) {
192
            throw new LegendLayerException(this.getName());
193
        }
194

    
195
        this.setLegend(legend);
196

    
197
        //Set the labeling strategy
198
        ILabelingStrategy labeler
199
                = (ILabelingStrategy) mapContextManager.getLabelingStrategy(dataStore);
200

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

    
208
        this.delegate(dataStore);
209

    
210
        dataStore.addObserver(this);
211

    
212
        ToolsLocator.getDisposableManager().bind(dataStore);
213
    }
214

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

    
223
        // Esto es para cuando se crea una capa nueva con el fullExtent de ancho
224
        // y alto 0.
225
        if (rAux == null || rAux.isEmpty() || rAux.getMaximum(0) - rAux.getMinimum(0) == 0
226
                && rAux.getMaximum(1) - rAux.getMinimum(1) == 0) {
227
            try {
228
                rAux
229
                        = geomManager.createEnvelope(0, 0, 90, 90, SUBTYPES.GEOM2D);
230
            } catch (CreateEnvelopeException e) {
231
                logger.error("Error creating the envelope", e);
232
                e.printStackTrace();
233
            }
234
        }
235
        // Si existe reproyecci?n, reproyectar el extent
236
        ICoordTrans ct = getCoordTrans();
237
        if (ct != null) {
238
            boolean originalEnvelopeIsEmpty = rAux.isEmpty();
239
            rAux = rAux.convert(ct);
240
            if(!originalEnvelopeIsEmpty && rAux.isEmpty()){
241
                try {
242
                    this.setAvailable(false);
243
                    throw new EnvelopeCantBeInitializedException();
244
                } catch(EnvelopeCantBeInitializedException e){
245
                    this.addError(e);
246
                }
247
            }
248
        }
249
        return rAux;
250

    
251
    }
252

    
253
    public void setBaseQuery(FeatureQuery baseQuery) {
254
        this.baseQuery = baseQuery;
255
    }
256

    
257
    @Override
258
    public FeatureQuery getBaseQuery() {
259
        return this.baseQuery;
260
    }
261

    
262
    public void addBaseFilter(Evaluator filter) {
263
        if( this.baseQuery == null ) {
264
            this.baseQuery = this.getFeatureStore().createFeatureQuery();
265
        }
266
        this.baseQuery.addFilter(filter);
267
    }
268

    
269
    /**
270
     * Draws using IFeatureIterator. This method will replace the old draw(...)
271
     * one.
272
     *
273
     * @autor jaume dominguez faus - jaume.dominguez@iver.es
274
     * @param image
275
     * @param g
276
     * @param viewPort
277
     * @param cancel
278
     * @param scale
279
     * @throws ReadDriverException
280
     */
281
    public void draw(BufferedImage image,
282
            Graphics2D g,
283
            ViewPort viewPort,
284
            Cancellable cancel,
285
            double scale) throws ReadException {
286

    
287
        if (legend == null) {
288
            return;
289
        }
290

    
291
        if (!this.isWithinScale(scale)) {
292
            return;
293
        }
294
        if (cancel.isCanceled()) {
295
            return;
296
        }
297

    
298
        if (spatialCache.isEnabled()) {
299
            spatialCache.clearAll();
300
            legend.addDrawingObserver(this);
301
        }
302

    
303
        FeatureQuery featureQuery = createFeatureQuery();
304

    
305
        try {
306
            FeatureAttributeDescriptor featureAttributeDescriptor
307
                    = getFeatureStore().getDefaultFeatureType().getDefaultTimeAttribute();
308

    
309
            if ((viewPort.getTime() != null) && (featureAttributeDescriptor != null)) {
310
                IntersectsTimeEvaluator intersectsTimeEvaluator
311
                        = new IntersectsTimeEvaluator(viewPort.getTime(), featureAttributeDescriptor.getName());
312
                featureQuery.addFilter(intersectsTimeEvaluator);
313
            }
314
        } catch (DataException e1) {
315
            logger.error("Impossible to get the temporal filter", e1);
316
        }
317

    
318
        try {
319

    
320
            long tini = System.currentTimeMillis();
321

    
322
            legend.draw(image,
323
                    g,
324
                    viewPort,
325
                    cancel,
326
                    scale,
327
                    null,
328
                    getCoordTrans(),
329
                    getFeatureStore(),
330
                    featureQuery);
331

    
332
            logger.debug("Layer " + this.getName() + " drawn in "
333
                    + (System.currentTimeMillis() - tini) + " milliseconds.");
334

    
335
        } catch (LegendException e) {
336
            this.setAvailable(false);
337
            this.setError(e);
338
            throw new ReadException(getName(), e);
339
        } finally {
340
            if (spatialCache.isEnabled()) {
341
                legend.deleteDrawingObserver(this);
342
            }
343
        }
344
    }
345

    
346
    public void print(Graphics2D g,
347
            ViewPort viewPort,
348
            Cancellable cancel,
349
            double scale,
350
            PrintAttributes properties) throws ReadException {
351
        if (!this.isWithinScale(scale)) {
352
            return;
353
        }
354
        if (cancel.isCanceled()) {
355
            return;
356
        }
357
        FeatureQuery featureQuery = createFeatureQuery();
358

    
359
        try {
360
            legend.print(g,
361
                    viewPort,
362
                    cancel,
363
                    scale,
364
                    null,
365
                    getCoordTrans(),
366
                    getFeatureStore(),
367
                    featureQuery,
368
                    properties);
369

    
370
        } catch (LegendException e) {
371
            this.setVisible(false);
372
            this.setActive(false);
373
            throw new ReadException(getName(), e);
374
        }
375
    }
376

    
377
    public void setLegend(IVectorLegend legend) throws LegendLayerException {
378
        if (this.legend == legend) {
379
            return;
380
        }
381
        if (this.legend != null && this.legend.equals(legend)) {
382
            return;
383
        }
384
        IVectorLegend oldLegend = this.legend;
385
        this.legend = legend;
386
        if (oldLegend != null) {
387
            oldLegend.removeLegendListener(this);
388
            oldLegend.deleteDrawingObserver(this);
389
        }
390
        if (legend != null) {
391
            this.legend.addDrawingObserver(this);
392
            this.legend.addLegendListener(this);
393
        }
394
        LegendChangedEvent e = LegendChangedEvent.createLegendChangedEvent(oldLegend, this.legend);
395
        e.setLayer(this);
396
        updateDrawVersion();
397
        callLegendChanged(e);
398
    }
399

    
400
    /**
401
     * Devuelve la Leyenda de la capa.
402
     *
403
     * @return Leyenda.
404
     */
405
    public ILegend getLegend() {
406
        return legend;
407
    }
408

    
409
    public int getShapeType() throws ReadException {
410
        if (typeShape == -1) {
411
            FeatureType featureType = null;
412
            try {
413
                if (getDataStore() != null) {
414
                    featureType
415
                            = (((FeatureStore) getDataStore()).getDefaultFeatureType());
416
                }
417
            } catch (DataException e) {
418
                throw new ReadException(getName(), e);
419
            }
420
            if (featureType != null) {
421
                int indexGeom = featureType.getDefaultGeometryAttributeIndex();
422
                typeShape
423
                        = featureType.getAttributeDescriptor(indexGeom).getGeometryType();
424
            }
425
        }
426
        return typeShape;
427
    }
428

    
429
    /**
430
     * Returns the layer's geometry type
431
     *
432
     * @return the geometry type
433
     *
434
     * @throws ReadException if there is an error getting the geometry type
435
     */
436
    public GeometryType getGeometryType() throws ReadException {
437
        FeatureType featureType = null;
438
        try {
439
            if (getDataStore() != null) {
440
                featureType
441
                        = (((FeatureStore) getDataStore()).getDefaultFeatureType());
442
            }
443
        } catch (DataException e) {
444
            throw new ReadException(getName(), e);
445
        }
446
        return featureType == null ? null : featureType
447
                .getDefaultGeometryAttribute().getGeomType();
448
    }
449

    
450
    public void saveToState(PersistentState state) throws PersistenceException {
451

    
452
        FeatureStore featureStore = null;
453

    
454
        if (!this.isAvailable()) {
455
            logger.info("The '" + this.getName() + "' layer is not available, it will persist not.");
456
            return;
457
        }
458

    
459
        try {
460
            super.saveToState(state);
461

    
462
            if (getLegend() != null) {
463
                state.set("legend", getLegend());
464
            }
465

    
466
            featureStore = getFeatureStore();
467

    
468
            if (featureStore != null) {
469
                state.set("featureStore", featureStore);
470
            }
471

    
472
            state.set("isLabeled", isLabeled);
473

    
474
            if (strategy != null) {
475
                state.set("labelingStrategy", strategy);
476
            }
477

    
478
            if (getLinkProperties() != null) {
479
                state.set("linkProperties", getLinkProperties());
480
            }
481

    
482
            state.set("typeShape", typeShape);
483
        } catch (PersistenceException ex) {
484
            logger.warn("Can't persist to state the layer '" + this.getName() + "'.", ex);
485
            throw ex;
486
        } catch (RuntimeException ex) {
487
            logger.warn("Can't persist to state the layer '" + this.getName() + "'.", ex);
488
            throw ex;
489
        }
490

    
491
    }
492

    
493
    public void loadFromState(PersistentState state) throws PersistenceException {
494

    
495
        DataStore store = null;
496
        IVectorLegend vectorLegend = null;
497
        ILabelingStrategy labelingStrategy = null;
498
        Boolean isLabeled = Boolean.FALSE;
499

    
500
        try {
501
            super.loadFromState(state);
502
            store = (DataStore) state.get("featureStore");
503

    
504
            try {
505
                this.bindToDataStore(store);
506
            } catch (LoadLayerException e) {
507
                throw new PersistenceException("Can't bind layer '" + this.getName() + "' to store '" + store.getFullName() + "'.", e);
508
            }
509

    
510
            vectorLegend = (IVectorLegend) state.get("legend");
511

    
512
            try {
513
                this.setLegend(vectorLegend);
514
            } catch (LegendLayerException e) {
515
                throw new PersistenceException("Can't set vector legend to the layer.", e);
516
            }
517

    
518
            try {
519
                isLabeled = (Boolean) state.get("isLabeled");
520
                if (isLabeled.booleanValue()) {
521
                    labelingStrategy = (ILabelingStrategy) state.get("labelingStrategy");
522
                }
523
            } catch (Exception ex) {
524
                throw new PersistenceException("Can't load labeling strategi from persistent state.",
525
                        ex);
526
            }
527

    
528
            if (isLabeled.booleanValue()) {
529
                this.setIsLabeled(true);
530
                this.setLabelingStrategy(labelingStrategy);
531
            } else {
532
                this.setIsLabeled(false);
533
                this.setLabelingStrategy(null);
534
            }
535

    
536
            typeShape = state.getInt("typeShape");
537

    
538
        } catch (Throwable e) {
539
            String storeName = (store == null) ? "unknow" : store.getFullName();
540
            logger.warn("can't load layer '" + this.getName() + "' (store=" + storeName + ") from persisted state.", e);
541
            this.setAvailable(false);
542
            return;
543
        }
544

    
545
    }
546

    
547
    /**
548
     * Sobreimplementaci?n del m?todo toString para que las bases de datos
549
     * identifiquen la capa.
550
     *
551
     * @return DOCUMENT ME!
552
     */
553
    public String toString() {
554
        /*
555
         * Se usa internamente para que la parte de datos identifique de forma
556
         * un?voca las tablas
557
         */
558
        String ret = super.toString();
559

    
560
        return ret ; //"layer" + ret.substring(ret.indexOf('@') + 1);
561
    }
562

    
563
    public boolean isEditing() {
564
        FeatureStore fs = getFeatureStore();
565
        if (fs == null) {
566
            /*
567
             * This happens when layer is not available, for example,
568
             * it was not possible to load from persistence
569
             */
570
            return false;
571
        } else {
572
            return fs.isEditing();
573
        }
574
    }
575

    
576
    public void setEditing(boolean b) throws StartEditionLayerException {
577

    
578
        try {
579
            throw new RuntimeException();
580
        } catch (Throwable th) {
581
            logger.info("This method is deprecated. ", th);
582
        }
583

    
584
        if (b == super.isEditing()) {
585
            return;
586
        }
587

    
588
        super.setEditing(b);
589
        FeatureStore fs = getFeatureStore();
590
        if (b) {
591
            try {
592
                fs.edit();
593
            } catch (DataException e) {
594
                throw new StartEditionLayerException(getName(), e);
595
            }
596
        }
597
        setSpatialCacheEnabled(b);
598
        callEditionChanged(LayerEvent.createEditionChangedEvent(this, "edition"));
599
    }
600

    
601
    /**
602
     * @deprecated Use {@link #getSpatialCache()}
603
     */
604
    public void clearSpatialCache() {
605
        spatialCache.clearAll();
606
    }
607

    
608
    /**
609
     * @deprecated Use {@link #getSpatialCache()}
610
     */
611
    public boolean isSpatialCacheEnabled() {
612
        return spatialCache.isEnabled();
613
    }
614

    
615
    /**
616
     * @deprecated Use {@link #getSpatialCache()}
617
     */
618
    public void setSpatialCacheEnabled(boolean spatialCacheEnabled) {
619
        spatialCache.setEnabled(spatialCacheEnabled);
620
    }
621

    
622
    public SpatialCache getSpatialCache() {
623
        return spatialCache;
624
    }
625

    
626
    /**
627
     * Siempre es un numero mayor de 1000
628
     *
629
     * @param maxFeatures
630
     */
631
    public void setMaxFeaturesInEditionCache(int maxFeatures) {
632
        if (maxFeatures > spatialCache.getMaxFeatures()) {
633
            spatialCache.setMaxFeatures(maxFeatures);
634
        }
635

    
636
    }
637

    
638
    /**
639
     * This method returns a boolean that is used by the FPopMenu to make
640
     * visible the properties menu or not. It is visible by default, and if a
641
     * later don't have to show this menu only has to override this method.
642
     *
643
     * @return If the properties menu is visible (or not)
644
     */
645
    public boolean isPropertiesMenuVisible() {
646
        return true;
647
    }
648

    
649
    public void reload() throws ReloadLayerException {
650
        super.reload();
651
        try {
652
            getFeatureStore().refresh();
653
        } catch (Exception e) {
654
            throw new ReloadLayerException(getName(), e);
655
        }
656
    }
657

    
658
    protected void setLoadSelection(Object xml) {
659
        // this.loadSelection = xml;
660
    }
661

    
662
    protected void setLoadLegend(IVectorLegend legend) {
663
        this.loadLegend = legend;
664
    }
665

    
666
    protected void putLoadSelection() {
667
        // if (this.loadSelection == null) return;
668
        // try {
669
        // this.getRecordset().getSelectionSupport().setXMLEntity(this.loadSelection);
670
        // } catch (ReadDriverException e) {
671
        // throw new XMLException(e);
672
        // }
673
        // this.loadSelection = null;
674

    
675
    }
676

    
677
    protected void putLoadLegend() throws LegendLayerException {
678
        if (this.loadLegend == null) {
679
            return;
680
        }
681
        this.setLegend(this.loadLegend);
682
        this.loadLegend = null;
683
    }
684

    
685
    protected void cleanLoadOptions() {
686
        this.loadLegend = null;
687
    }
688

    
689
    public boolean isWritable() {
690
        return getFeatureStore().allowWrite();
691
    }
692

    
693
    public FLayer cloneLayer() throws Exception {
694
        FLyrVect clonedLayer = new FLyrVect();
695
        clonedLayer.bindToDataStore(getDataStore());
696
        // if (isJoined()) {
697
        // clonedLayer.setIsJoined(true);
698
        // }
699
        clonedLayer.setVisible(isVisible());
700
        // clonedLayer.setISpatialIndex(getISpatialIndex());
701
        clonedLayer.setName(getName());
702
        clonedLayer.setCoordTrans(getCoordTrans());
703

    
704
        clonedLayer.setLegend((IVectorLegend) getLegend().cloneLegend());
705

    
706
        clonedLayer.setIsLabeled(isLabeled());
707
        ILabelingStrategy labelingStrategy = getLabelingStrategy();
708
        if (labelingStrategy != null) {
709
            clonedLayer.setLabelingStrategy(labelingStrategy);
710
        }
711

    
712
        return clonedLayer;
713
    }
714

    
715
    protected boolean isOnePoint(AffineTransform graphicsTransform,
716
            ViewPort viewPort,
717
            double dpi,
718
            CartographicSupport csSym,
719
            Geometry geom,
720
            int[] xyCoords) {
721
        return isOnePoint(graphicsTransform, viewPort, geom, xyCoords)
722
                && csSym.getCartographicSize(viewPort, dpi, geom) <= 1;
723
    }
724

    
725
    private boolean isOnePoint(AffineTransform graphicsTransform,
726
            ViewPort viewPort,
727
            Geometry geom,
728
            int[] xyCoords) {
729
        boolean onePoint = false;
730
        int type = geom.getType();
731
        if (type == Geometry.TYPES.NULL) {
732
            return false;
733
        }
734
        if (type != Geometry.TYPES.POINT && type != Geometry.TYPES.MULTIPOINT) {
735

    
736
            Envelope geomBounds = geom.getEnvelope();
737

    
738
            // ICoordTrans ct = getCoordTrans();
739
            // Se supone que la geometria ya esta reproyectada
740
            // if (ct!=null) {
741
            // // geomBounds = ct.getInverted().convert(geomBounds);
742
            // geomBounds = geomBounds.convert(ct);
743
            // }
744
            double dist1Pixel = viewPort.getDist1pixel();
745

    
746
            onePoint
747
                    = (geomBounds.getLength(0) <= dist1Pixel && geomBounds.getLength(1) <= dist1Pixel);
748

    
749
            if (onePoint) {
750
                // avoid out of range exceptions
751
                org.gvsig.fmap.geom.primitive.Point p;
752
                try {
753
                    p
754
                            = geomManager.createPoint(geomBounds.getMinimum(0),
755
                                    geomBounds.getMinimum(1),
756
                                    SUBTYPES.GEOM2D);
757
                    p.transform(viewPort.getAffineTransform());
758
                    p.transform(graphicsTransform);
759
                    xyCoords[0] = (int) p.getX();
760
                    xyCoords[1] = (int) p.getY();
761
                } catch (org.gvsig.fmap.geom.exception.CreateGeometryException e) {
762
                    logger.error("Error creating a point", e);
763
                }
764

    
765
            }
766

    
767
        }
768
        return onePoint;
769
    }
770

    
771
    public boolean isLabeled() {
772
        return isLabeled;
773
    }
774

    
775
    public void setIsLabeled(boolean isLabeled) {
776
        this.isLabeled = isLabeled;
777
    }
778

    
779
    public ILabelingStrategy getLabelingStrategy() {
780
        return strategy;
781
    }
782

    
783
    public void setLabelingStrategy(ILabelingStrategy strategy) {
784
        this.strategy = strategy;
785
        if (strategy == null) {
786
            return;
787
        }
788
        strategy.setLayer(this);
789
        updateDrawVersion();
790
    }
791

    
792
    public void drawLabels(BufferedImage image,
793
            Graphics2D g,
794
            ViewPort viewPort,
795
            Cancellable cancel,
796
            double scale,
797
            double dpi) throws ReadException {
798
        if (strategy != null && isWithinScale(scale)) {
799
            strategy.draw(image, g, scale, viewPort, cancel, dpi);
800
        }
801
    }
802

    
803
    public void printLabels(Graphics2D g,
804
            ViewPort viewPort,
805
            Cancellable cancel,
806
            double scale,
807
            PrintAttributes properties) throws ReadException {
808
        if (strategy != null) {
809
            strategy.print(g, scale, viewPort, cancel, properties);
810
        }
811
    }
812

    
813
    /**
814
     * Return true, because a Vectorial Layer supports HyperLink
815
     *
816
     * @deprecated the hiperlink functionaliti is out the layer now
817
     */
818
    public boolean allowLinks() {
819
        return false;
820
    }
821

    
822
    public void load() throws LoadLayerException {
823
        super.load();
824
    }
825

    
826
    public FeatureStore getFeatureStore() {
827
        return (FeatureStore) getDataStore();
828
    }
829

    
830
    public FeatureQuery createFeatureQuery() {
831
        if( this.baseQuery==null ) {
832
            return this.getFeatureStore().createFeatureQuery();
833
        }
834
        try {
835
            return (FeatureQuery) baseQuery.clone();
836
        } catch (CloneNotSupportedException ex) {
837
            throw new RuntimeException(ex);
838
        }
839
    }
840

    
841
    /**
842
     * @deprecated use instead
843
     * {@link #queryByPoint(org.gvsig.fmap.geom.primitive.Point, double, FeatureType)}
844
     */
845
    public FeatureSet queryByPoint(Point2D mapPoint,
846
            double tol,
847
            FeatureType featureType) throws DataException {
848
        logger.warn("Deprecated use of queryByPoint.");
849
        GeometryManager manager = GeometryLocator.getGeometryManager();
850
        org.gvsig.fmap.geom.primitive.Point center;
851
        try {
852
            center
853
                    = (org.gvsig.fmap.geom.primitive.Point) manager.create(TYPES.POINT,
854
                            SUBTYPES.GEOM2D);
855
            center.setX(mapPoint.getX());
856
            center.setY(mapPoint.getY());
857
            Circle circle
858
                    = (Circle) manager.create(TYPES.CIRCLE, SUBTYPES.GEOM2D);
859
            circle.setPoints(center, tol);
860
            return queryByGeometry(circle, featureType);
861
        } catch (org.gvsig.fmap.geom.exception.CreateGeometryException e) {
862
            throw new CreateGeometryException(TYPES.CIRCLE, SUBTYPES.GEOM2D, e);
863
        }
864
    }
865

    
866
    public FeatureSet queryByPoint(org.gvsig.fmap.geom.primitive.Point point,
867
            double tol,
868
            FeatureType featureType) throws DataException {
869
        GeometryManager manager = GeometryLocator.getGeometryManager();
870
        try {
871
            Circle circle
872
                    = (Circle) manager.create(TYPES.CIRCLE, SUBTYPES.GEOM2D);
873
            circle.setPoints(point, tol);
874
            return queryByGeometry(circle, featureType);
875
        } catch (org.gvsig.fmap.geom.exception.CreateGeometryException e) {
876
            throw new CreateGeometryException(TYPES.CIRCLE, SUBTYPES.GEOM2D, e);
877
        }
878
    }
879

    
880
    /**
881
     * Input geom must be in the CRS of the view.
882
     *
883
     * @param geom
884
     * @param featureType
885
     * @return
886
     * @throws DataException
887
     */
888
    public FeatureSet queryByGeometry(Geometry geom, FeatureType featureType) throws DataException {
889
        FeatureQuery featureQuery = createFeatureQuery();
890
        String geomName
891
                = featureStore.getDefaultFeatureType()
892
                .getDefaultGeometryAttributeName();
893
        featureQuery.setFeatureType(featureType);
894

    
895
        Geometry query_geo = this.transformToSourceCRS(geom, true);
896
        IProjection query_proj = getMapContext().getProjection();
897
        if (this.getCoordTrans() != null) {
898
            query_proj = this.getCoordTrans().getPOrig();
899
        }
900

    
901
        IntersectsGeometryEvaluator iee
902
                = new IntersectsGeometryEvaluator(
903
                        query_geo,
904
                        query_proj,
905
                        featureStore.getDefaultFeatureType(),
906
                        geomName);
907
        featureQuery.setFilter(iee);
908
        featureQuery.setAttributeNames(null);
909
        return getFeatureStore().getFeatureSet(featureQuery);
910

    
911
    }
912

    
913
    /**
914
     * It return the {@link FeatureSet} that intersects with the envelope.
915
     *
916
     * @param envelope envelope that defines the area for the query.
917
     * @param featureType only the features with this feature type are used in
918
     * the query.
919
     * @return the set of features that intersect with the envelope.
920
     * @throws DataException
921
     */
922
    public FeatureSet queryByEnvelope(Envelope envelope, FeatureType featureType) throws DataException {
923
        return queryByEnvelope(envelope, featureType, null);
924
    }
925

    
926
    /**
927
     * It return the {@link FeatureSet} that intersects with the envelope.
928
     *
929
     * @param envelope envelope that defines the area for the query in viewport
930
     * CRS
931
     * @param featureType only the features with this feature type are used in
932
     * the query.
933
     * @param names the feature attributes that have to be checked.
934
     * @return the set of features that intersect with the envelope.
935
     * @throws DataException
936
     */
937
    public FeatureSet queryByEnvelope(Envelope envelope,
938
            FeatureType featureType,
939
            String[] names) throws DataException {
940
        FeatureQuery featureQuery = createFeatureQuery();
941
        if (names == null) {
942
            featureQuery.setFeatureType(featureType);
943
        } else {
944
            featureQuery.setAttributeNames(names);
945
            featureQuery.setFeatureTypeId(featureType.getId());
946
        }
947
        String geomName = featureStore.getDefaultFeatureType()
948
                .getDefaultGeometryAttributeName();
949

    
950
        Envelope query_env = fromViewPortCRSToSourceCRS(this, envelope);
951
        IProjection query_proj = getMapContext().getProjection();
952
        if (this.getCoordTrans() != null) {
953
            query_proj = this.getCoordTrans().getPOrig();
954
        }
955

    
956
        IntersectsGeometryEvaluator iee
957
                = new IntersectsGeometryEvaluator(
958
                        query_env.getGeometry(), query_proj,
959
                        featureStore.getDefaultFeatureType(),
960
                        geomName);
961
        featureQuery.setFilter(iee);
962
        return getFeatureStore().getFeatureSet(featureQuery);
963

    
964
    }
965

    
966
    public DynObjectSet getInfo(Point p, double tolerance, Cancellable cancel) throws LoadLayerException,
967
            DataException {
968

    
969
        return getInfo(p, tolerance, cancel, true);
970
    }
971

    
972
    public DynObjectSet getInfo(Point p,
973
            double tolerance,
974
            Cancellable cancel,
975
            boolean fast) throws LoadLayerException, DataException {
976
        Point2D infop = new Point2D.Double(p.x, p.y);
977
        Point2D pReal = this.getMapContext().getViewPort().toMapPoint(infop);
978
        return queryByPoint(pReal,
979
                tolerance,
980
                getFeatureStore().getDefaultFeatureType()).getDynObjectSet(fast);
981
    }
982

    
983
    public DynObjectSet getInfo(org.gvsig.fmap.geom.primitive.Point p,
984
            double tolerance) throws LoadLayerException, DataException {
985
        return queryByPoint(p, tolerance, getFeatureStore().getDefaultFeatureType()).getDynObjectSet(false);
986
    }
987

    
988
    @Override
989
    public void legendCleared(LegendClearEvent event) {
990
        this.updateDrawVersion();
991
        LegendChangedEvent e = LegendChangedEvent.createLegendChangedEvent(legend,event);
992
        this.callLegendChanged(e);
993
    }
994

    
995
    @Override
996
    public boolean symbolChanged(SymbolLegendEvent e) {
997
        this.updateDrawVersion();
998
        LegendChangedEvent ev = LegendChangedEvent.createLegendChangedEvent(legend, e);
999
        this.callLegendChanged(ev);
1000
        return true;
1001
    }
1002

    
1003
    public void update(Observable observable, Object notification) {
1004
        if (observable.equals(this.featureStore)) {
1005
            if (notification instanceof FeatureStoreNotification) {
1006
                FeatureStoreNotification event
1007
                        = (FeatureStoreNotification) notification;
1008
                if (event.getType() == FeatureStoreNotification.AFTER_DELETE
1009
                        || event.getType() == FeatureStoreNotification.AFTER_UNDO
1010
                        || event.getType() == FeatureStoreNotification.AFTER_REDO
1011
                        || event.getType() == FeatureStoreNotification.AFTER_REFRESH
1012
                        || event.getType() == FeatureStoreNotification.AFTER_UPDATE
1013
                        || event.getType() == FeatureStoreNotification.AFTER_UPDATE_TYPE
1014
                        || event.getType() == FeatureStoreNotification.SELECTION_CHANGE
1015
                        || event.getType() == FeatureStoreNotification.AFTER_INSERT) {
1016
                    this.updateDrawVersion();
1017

    
1018
                } else if (event.getType() == FeatureStoreNotification.AFTER_CANCELEDITING) {
1019

    
1020
                    setSpatialCacheEnabled(false);
1021
                    callEditionChanged(LayerEvent.createEditionChangedEvent(this, "edition"));
1022
                    this.updateDrawVersion();
1023

    
1024
                } else if (event.getType() == FeatureStoreNotification.AFTER_STARTEDITING) {
1025

    
1026
                    setSpatialCacheEnabled(true);
1027
                    callEditionChanged(LayerEvent.createEditionChangedEvent(this, "edition"));
1028

    
1029
                } else if (event.getType() == FeatureStoreNotification.TRANSFORM_CHANGE) {
1030
                    //If a transform has to be applied, try to reload the layer.
1031
                    try {
1032
                        reload();
1033
                    } catch (ReloadLayerException e) {
1034
                        logger.info("While reloading layer.", e);
1035
                        this.setAvailable(false);
1036
                    }
1037
                } else if (event.getType() == FeatureStoreNotification.RESOURCE_CHANGED) {
1038
                    this.setAvailable(false);
1039
                } else if (event.getType() == FeatureStoreNotification.AFTER_FINISHEDITING) {
1040
                    this.setAvailable(true);
1041
                    setSpatialCacheEnabled(false);
1042
                    callEditionChanged(LayerEvent.createEditionChangedEvent(this, "edition"));
1043
                    this.updateDrawVersion();
1044
                }
1045
            }
1046
        } else if (notification instanceof FeatureDrawnNotification
1047
                && (isEditing() || isLayerToSnap())) {
1048
            // This code is needed in editing mode
1049
            // for all layers involved in snapping
1050
            // (including the layer being edited)
1051
            Geometry geometry
1052
                    = ((FeatureDrawnNotification) notification).getDrawnGeometry();
1053
            spatialCache.insert(geometry.getEnvelope(), geometry);
1054
        }
1055
    }
1056

    
1057
    private boolean isLayerToSnap() {
1058

    
1059
        if (this.getMapContext() == null) {
1060
            /*
1061
             * This happens with the graphics layer because it has no parent
1062
             */
1063
            return false;
1064
        } else {
1065
            return this.getMapContext().getLayersToSnap().contains(this);
1066
        }
1067

    
1068
        /*
1069
         Iterator itersnap = this.getMapContext().getLayersToSnap().iterator();
1070
         Object item = null;
1071
         while (itersnap.hasNext()) {
1072
         item = itersnap.next();
1073
         if (item == this) {
1074
         return true;
1075
         }
1076
         }
1077
         return false;
1078
         */
1079
    }
1080

    
1081
    /*
1082
     * (non-Javadoc)
1083
     *
1084
     * @see org.gvsig.metadata.Metadata#getMetadataChildren()
1085
     */
1086
    public Set getMetadataChildren() {
1087
        Set ret = new TreeSet();
1088
        ret.add(this.featureStore);
1089
        return ret;
1090
    }
1091

    
1092
    /*
1093
     * (non-Javadoc)
1094
     *
1095
     * @see org.gvsig.metadata.Metadata#getMetadataID()
1096
     */
1097
    public Object getMetadataID() throws MetadataException {
1098
        return "Layer(" + this.getName() + "):"
1099
                + this.featureStore.getMetadataID();
1100
    }
1101

    
1102
    public GeometryType getTypeVectorLayer() throws DataException,
1103
            LocatorException,
1104
            GeometryTypeNotSupportedException,
1105
            GeometryTypeNotValidException {
1106
        // FIXME Esto deberia de pedirse a FType!!!!
1107
        FeatureStore fs = this.getFeatureStore();
1108
        FeatureType fType = fs.getDefaultFeatureType();
1109
        FeatureAttributeDescriptor attr
1110
                = fType.getAttributeDescriptor(fType.getDefaultGeometryAttributeIndex());
1111
        GeometryType geomType
1112
                = GeometryLocator.getGeometryManager()
1113
                .getGeometryType(attr.getGeometryType(),
1114
                        attr.getGeometrySubType());
1115
        return geomType;
1116
    }
1117

    
1118
    public static class RegisterPersistence implements Callable {
1119

    
1120
        public Object call() {
1121
            PersistenceManager manager = ToolsLocator.getPersistenceManager();
1122

    
1123
            DynStruct definition
1124
                    = manager.addDefinition(FLyrVect.class,
1125
                            "FLyrVect",
1126
                            "FLyrVect Persistence definition",
1127
                            null,
1128
                            null);
1129
            definition.extend(PersistenceManager.PERSISTENCE_NAMESPACE,
1130
                    "FLyrDefault");
1131

    
1132
            definition.addDynFieldObject("legend")
1133
                    .setClassOfValue(IVectorLegend.class)
1134
                    .setMandatory(true);
1135
            definition.addDynFieldObject("featureStore")
1136
                    .setClassOfValue(FeatureStore.class)
1137
                    .setMandatory(true);
1138
            definition.addDynFieldBoolean("isLabeled").setMandatory(true);
1139
            definition.addDynFieldInt("typeShape").setMandatory(true);
1140
            definition.addDynFieldObject("labelingStrategy")
1141
                    .setClassOfValue(ILabelingStrategy.class)
1142
                    .setMandatory(false);
1143

    
1144
            return Boolean.TRUE;
1145
        }
1146
    }
1147

    
1148
    protected void doDispose() throws BaseException {
1149
        dispose(featureStore);
1150
        spatialCache.clearAll();
1151
    }
1152

    
1153
    /**
1154
     * Returns envelope in layer's data source CRS from envelope provided in
1155
     * viewport CRS
1156
     *
1157
     * @param lyr
1158
     * @param env
1159
     * @return
1160
     */
1161
    public static Envelope fromViewPortCRSToSourceCRS(FLayer lyr, Envelope env) {
1162

    
1163
        if (lyr == null || env == null) {
1164
            return null;
1165
        }
1166

    
1167
        ICoordTrans ct = lyr.getCoordTrans();
1168
        if (ct == null) {
1169
            return env;
1170
        } else {
1171
            return env.convert(ct.getInverted());
1172
        }
1173
    }
1174

    
1175
    public Geometry transformToSourceCRS(Geometry geom, boolean clone) {
1176
        return fromViewPortCRSToSourceCRS(this, geom, clone);
1177
    }
1178

    
1179
    /**
1180
     * Returns geometry in layer's data source CRS from geometry provided in
1181
     * viewport CRS
1182
     *
1183
     * @param lyr
1184
     * @param geo
1185
     * @param clone
1186
     * @return
1187
     * @deprecated use the transformToSourceCRS method of layer.
1188
     */
1189
    public static Geometry fromViewPortCRSToSourceCRS(
1190
            FLayer lyr,
1191
            Geometry geo,
1192
            boolean clone) {
1193

    
1194
        if (lyr == null || geo == null) {
1195
            return null;
1196
        }
1197
        ICoordTrans ct = lyr.getCoordTrans();
1198
        Geometry resp = geo;
1199
        if (clone) {
1200
            resp = resp.cloneGeometry();
1201
        }
1202
        if (ct != null) {
1203
            resp.reProject(ct.getInverted());
1204
        }
1205
        return resp;
1206
    }
1207

    
1208
    public Iterator iterator() {
1209
        return this.getFeatureStore().iterator();
1210
    }
1211

    
1212
    protected class EnvelopeCantBeInitializedException extends BaseException {
1213

    
1214
        /**
1215
         *
1216
         */
1217
        private static final long serialVersionUID = 4572797479347381552L;
1218
        private final static String MESSAGE_FORMAT = "The envelope can't be initialized, maybe the layer has a wrong projection. Change the projection in layer properties of the add layer dialog to try fix it.";
1219
        private final static String MESSAGE_KEY = "_EnvelopeCantBeInitializedException";
1220

    
1221
        public EnvelopeCantBeInitializedException() {
1222
            super(MESSAGE_FORMAT, null, MESSAGE_KEY, serialVersionUID);
1223
        }
1224
    }
1225

    
1226
}