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

History | View | Annotate | Download (44.2 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.io.File;
31
import java.util.Iterator;
32
import java.util.Set;
33
import java.util.TreeSet;
34
import java.util.logging.Level;
35
import java.util.logging.Logger;
36

    
37
import org.cresques.cts.ICoordTrans;
38
import org.cresques.cts.IProjection;
39
import org.slf4j.LoggerFactory;
40

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

    
104
/**
105
 * Capa b?sica Vectorial.
106
 *
107
 */
108
public class FLyrVect
109
        extends FLyrDefault
110
        implements VectorLayer,
111
        LegendContentsChangedListener, Observer {
112

    
113
    final static private org.slf4j.Logger logger
114
            = LoggerFactory.getLogger(FLyrVect.class);
115
    private final GeometryManager geomManager
116
            = GeometryLocator.getGeometryManager();
117

    
118
    /**
119
     * Leyenda de la capa vectorial
120
     */
121
    private IVectorLegend legend;
122
    private int typeShape = -1;
123
    private FeatureStore featureStore = null;
124
    private SpatialCache spatialCache = new SpatialCache();
125

    
126
    /**
127
     * An implementation of gvSIG spatial index
128
     */
129
    // protected ISpatialIndex spatialIndex = null;
130
    private IVectorLegend loadLegend = null;
131

    
132
    private boolean isLabeled;
133
    protected ILabelingStrategy strategy;
134
//        private ReprojectDefaultGeometry reprojectTransform;
135
    private FeatureQuery baseQuery = null;
136

    
137
    public FLyrVect() {
138
        super();
139
    }
140

    
141
    @Override
142
    public String getTocImageIcon() {
143
        return MapContextLocator.getMapContextManager().getIconLayer(this.getDataStore());
144
    }
145

    
146
    /**
147
     * Return the store associated to the layer.
148
     *
149
     * @return the store
150
     */
151
    @Override
152
    public DataStore getDataStore() {
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
        if (getFeatureStore() == null) {
215
            return null;
216
        }
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
    public void addBaseFilter(String filter) {
270
        try {
271
            this.addBaseFilter(DALLocator.getDataManager().createExpresion(filter));
272
        } catch (InitializeException ex) {
273
            throw new RuntimeException("Can't create filter with '" + filter + "'", ex);
274
        }
275
    }
276

    
277
    /**
278
     * Draws using IFeatureIterator. This method will replace the old draw(...)
279
     * one.
280
     *
281
     * @autor jaume dominguez faus - jaume.dominguez@iver.es
282
     * @param image
283
     * @param g
284
     * @param viewPort
285
     * @param cancel
286
     * @param scale
287
     * @throws ReadDriverException
288
     */
289
    public void draw(BufferedImage image,
290
            Graphics2D g,
291
            ViewPort viewPort,
292
            Cancellable cancel,
293
            double scale) throws ReadException {
294

    
295
        if (legend == null) {
296
            return;
297
        }
298

    
299
        if (!this.isWithinScale(scale)) {
300
            return;
301
        }
302
        if (cancel.isCanceled()) {
303
            return;
304
        }
305

    
306
        if (spatialCache.isEnabled()) {
307
            spatialCache.clearAll();
308
            legend.addDrawingObserver(this);
309
        }
310

    
311
        FeatureQuery featureQuery = createFeatureQuery();
312

    
313
        try {
314
            FeatureAttributeDescriptor featureAttributeDescriptor
315
                    = getFeatureStore().getDefaultFeatureType().getDefaultTimeAttribute();
316

    
317
            if ((viewPort.getTime() != null) && (featureAttributeDescriptor != null)) {
318
                IntersectsTimeEvaluator intersectsTimeEvaluator
319
                        = new IntersectsTimeEvaluator(viewPort.getTime(), featureAttributeDescriptor.getName());
320
                featureQuery.addFilter(intersectsTimeEvaluator);
321
            }
322
        } catch (DataException e1) {
323
            logger.error("Impossible to get the temporal filter", e1);
324
        }
325

    
326
        try {
327

    
328
            long tini = System.currentTimeMillis();
329

    
330
            legend.draw(image,
331
                    g,
332
                    viewPort,
333
                    cancel,
334
                    scale,
335
                    null,
336
                    getCoordTrans(),
337
                    getFeatureStore(),
338
                    featureQuery);
339

    
340
            logger.debug("Layer " + this.getName() + " drawn in "
341
                    + (System.currentTimeMillis() - tini) + " milliseconds.");
342

    
343
        } catch (LegendException e) {
344
            this.setAvailable(false);
345
            this.setError(e);
346
            throw new ReadException(getName(), e);
347
        } finally {
348
            if (spatialCache.isEnabled()) {
349
                legend.deleteDrawingObserver(this);
350
            }
351
        }
352
    }
353

    
354
    public void print(Graphics2D g,
355
            ViewPort viewPort,
356
            Cancellable cancel,
357
            double scale,
358
            PrintAttributes properties) throws ReadException {
359
        if (!this.isWithinScale(scale)) {
360
            return;
361
        }
362
        if (cancel.isCanceled()) {
363
            return;
364
        }
365
        FeatureQuery featureQuery = createFeatureQuery();
366

    
367
        try {
368
            legend.print(g,
369
                    viewPort,
370
                    cancel,
371
                    scale,
372
                    null,
373
                    getCoordTrans(),
374
                    getFeatureStore(),
375
                    featureQuery,
376
                    properties);
377

    
378
        } catch (LegendException e) {
379
            this.setVisible(false);
380
            this.setActive(false);
381
            throw new ReadException(getName(), e);
382
        }
383
    }
384

    
385
    public void setLegend(IVectorLegend legend) throws LegendLayerException {
386
        if (this.legend == legend) {
387
            return;
388
        }
389
        if (this.legend != null && this.legend.equals(legend)) {
390
            return;
391
        }
392
        IVectorLegend oldLegend = this.legend;
393
        this.legend = legend;
394
        if (oldLegend != null) {
395
            oldLegend.removeLegendListener(this);
396
            oldLegend.deleteDrawingObserver(this);
397
        }
398
        if (legend != null) {
399
            this.legend.addDrawingObserver(this);
400
            this.legend.addLegendListener(this);
401
        }
402
        LegendChangedEvent e = LegendChangedEvent.createLegendChangedEvent(oldLegend, this.legend);
403
        e.setLayer(this);
404
        updateDrawVersion();
405
        callLegendChanged(e);
406
    }
407

    
408
    /**
409
     * Devuelve la Leyenda de la capa.
410
     *
411
     * @return Leyenda.
412
     */
413
    public ILegend getLegend() {
414
        return legend;
415
    }
416

    
417
    public int getShapeType() throws ReadException {
418
        if (typeShape == -1) {
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
            if (featureType != null) {
429
                int indexGeom = featureType.getDefaultGeometryAttributeIndex();
430
                typeShape
431
                        = featureType.getAttributeDescriptor(indexGeom).getGeometryType();
432
            }
433
        }
434
        return typeShape;
435
    }
436

    
437
    /**
438
     * Returns the layer's geometry type
439
     *
440
     * @return the geometry type
441
     *
442
     * @throws ReadException if there is an error getting the geometry type
443
     */
444
    public GeometryType getGeometryType() throws ReadException {
445
        FeatureType featureType = null;
446
        try {
447
            if (getDataStore() != null) {
448
                featureType
449
                        = (((FeatureStore) getDataStore()).getDefaultFeatureType());
450
            }
451
        } catch (DataException e) {
452
            throw new ReadException(getName(), e);
453
        }
454
        return featureType == null ? null : featureType
455
                .getDefaultGeometryAttribute().getGeomType();
456
    }
457

    
458
    public void saveToState(PersistentState state) throws PersistenceException {
459

    
460
        FeatureStore featureStore = null;
461

    
462
        if (!this.isAvailable()) {
463
            logger.info("The '" + this.getName() + "' layer is not available, it will persist not.");
464
            return;
465
        }
466

    
467
        try {
468
            super.saveToState(state);
469

    
470
            if (getLegend() != null) {
471
                state.set("legend", getLegend());
472
            }
473

    
474
            featureStore = getFeatureStore();
475

    
476
            if (featureStore != null) {
477
                state.set("featureStore", featureStore);
478
            }
479

    
480
            state.set("isLabeled", isLabeled);
481

    
482
            if (strategy != null) {
483
                state.set("labelingStrategy", strategy);
484
            }
485

    
486
            if (getLinkProperties() != null) {
487
                state.set("linkProperties", getLinkProperties());
488
            }
489

    
490
            state.set("typeShape", typeShape);
491
        } catch (PersistenceException ex) {
492
            logger.warn("Can't persist to state the layer '" + this.getName() + "'.", ex);
493
            throw ex;
494
        } catch (RuntimeException ex) {
495
            logger.warn("Can't persist to state the layer '" + this.getName() + "'.", ex);
496
            throw ex;
497
        }
498

    
499
    }
500

    
501
    @Override
502
    public void loadFromState(PersistentState state) throws PersistenceException {
503

    
504
        FeatureStore store = null;
505
        try {
506
            super.loadFromState(state);
507

    
508
            this.setIsLabeled(false);
509
            this.setLabelingStrategy(null);
510

    
511
            typeShape = state.getInt("typeShape", Geometry.TYPES.GEOMETRY);
512

    
513
            store = (FeatureStore) state.get("featureStore");
514
            if (store == null) { // Averiguar como puede llegar aqui un null.
515
                this.setAvailable(false);
516
                this.addError(new RuntimeException("Can't retrieve store information."));
517
            } else {
518
                if (store.isBroken()) {
519
                    this.setAvailable(false);
520
                    this.addError(store.getBreakingsCause());
521
                }
522
            }
523
            try {
524
                this.bindToDataStore(store);
525
            } catch (Exception ex) {
526
                this.setAvailable(false);
527
                this.addError(ex);
528
            }
529

    
530
            try {
531
                IVectorLegend vectorLegend = (IVectorLegend) state.get("legend");
532
                this.setLegend(vectorLegend);
533
            } catch (Exception ex) {
534
                this.setAvailable(false);
535
                this.addError(ex);
536
            }
537

    
538
            try {
539
                if (state.getBoolean("isLabeled", false)) {
540
                    this.setIsLabeled(true);
541
                    ILabelingStrategy labelingStrategy = (ILabelingStrategy) state.get("labelingStrategy");
542
                    this.setLabelingStrategy(labelingStrategy);
543
                }
544
            } catch (Exception ex) {
545
                this.setAvailable(false);
546
                this.addError(ex);
547
            }
548

    
549
        } catch (Throwable e) {
550
            String storeName = (store == null) ? "unknow" : store.getFullName();
551
            logger.warn("can't load layer '" + this.getName() + "' (store=" + storeName + ") from persisted state.", e);
552
            this.setAvailable(false);
553
            this.addError(e);
554
        }
555

    
556
    }
557

    
558
    /**
559
     * Sobreimplementaci?n del m?todo toString para que las bases de datos
560
     * identifiquen la capa.
561
     *
562
     * @return DOCUMENT ME!
563
     */
564
    public String toString() {
565
        /*
566
         * Se usa internamente para que la parte de datos identifique de forma
567
         * un?voca las tablas
568
         */
569
        String ret = super.toString();
570

    
571
        return ret; //"layer" + ret.substring(ret.indexOf('@') + 1);
572
    }
573

    
574
    public boolean isEditing() {
575
        FeatureStore fs = getFeatureStore();
576
        if (fs == null) {
577
            /*
578
             * This happens when layer is not available, for example,
579
             * it was not possible to load from persistence
580
             */
581
            return false;
582
        } else {
583
            return fs.isEditing();
584
        }
585
    }
586

    
587
    public void setEditing(boolean b) throws StartEditionLayerException {
588

    
589
        try {
590
            throw new RuntimeException();
591
        } catch (Throwable th) {
592
            logger.info("This method is deprecated. ", th);
593
        }
594

    
595
        if (b == super.isEditing()) {
596
            return;
597
        }
598

    
599
        super.setEditing(b);
600
        FeatureStore fs = getFeatureStore();
601
        if (b) {
602
            try {
603
                fs.edit();
604
            } catch (DataException e) {
605
                throw new StartEditionLayerException(getName(), e);
606
            }
607
        }
608
        setSpatialCacheEnabled(b);
609
        callEditionChanged(LayerEvent.createEditionChangedEvent(this, "edition"));
610
    }
611

    
612
    /**
613
     * @deprecated Use {@link #getSpatialCache()}
614
     */
615
    public void clearSpatialCache() {
616
        spatialCache.clearAll();
617
    }
618

    
619
    /**
620
     * @deprecated Use {@link #getSpatialCache()}
621
     */
622
    public boolean isSpatialCacheEnabled() {
623
        return spatialCache.isEnabled();
624
    }
625

    
626
    /**
627
     * @deprecated Use {@link #getSpatialCache()}
628
     */
629
    public void setSpatialCacheEnabled(boolean spatialCacheEnabled) {
630
        spatialCache.setEnabled(spatialCacheEnabled);
631
    }
632

    
633
    public SpatialCache getSpatialCache() {
634
        return spatialCache;
635
    }
636

    
637
    /**
638
     * Siempre es un numero mayor de 1000
639
     *
640
     * @param maxFeatures
641
     */
642
    public void setMaxFeaturesInEditionCache(int maxFeatures) {
643
        if (maxFeatures > spatialCache.getMaxFeatures()) {
644
            spatialCache.setMaxFeatures(maxFeatures);
645
        }
646

    
647
    }
648

    
649
    /**
650
     * This method returns a boolean that is used by the FPopMenu to make
651
     * visible the properties menu or not. It is visible by default, and if a
652
     * later don't have to show this menu only has to override this method.
653
     *
654
     * @return If the properties menu is visible (or not)
655
     */
656
    public boolean isPropertiesMenuVisible() {
657
        return true;
658
    }
659

    
660
    public void reload() throws ReloadLayerException {
661
        super.reload();
662
        try {
663
            getFeatureStore().refresh();
664
        } catch (Exception e) {
665
            throw new ReloadLayerException(getName(), e);
666
        }
667
    }
668

    
669
    protected void setLoadSelection(Object xml) {
670
        // this.loadSelection = xml;
671
    }
672

    
673
    protected void setLoadLegend(IVectorLegend legend) {
674
        this.loadLegend = legend;
675
    }
676

    
677
    protected void putLoadSelection() {
678
        // if (this.loadSelection == null) return;
679
        // try {
680
        // this.getRecordset().getSelectionSupport().setXMLEntity(this.loadSelection);
681
        // } catch (ReadDriverException e) {
682
        // throw new XMLException(e);
683
        // }
684
        // this.loadSelection = null;
685

    
686
    }
687

    
688
    protected void putLoadLegend() throws LegendLayerException {
689
        if (this.loadLegend == null) {
690
            return;
691
        }
692
        this.setLegend(this.loadLegend);
693
        this.loadLegend = null;
694
    }
695

    
696
    protected void cleanLoadOptions() {
697
        this.loadLegend = null;
698
    }
699

    
700
    public boolean isWritable() {
701
        return getFeatureStore().allowWrite();
702
    }
703

    
704
    public FLayer cloneLayer() throws Exception {
705
        FLyrVect clonedLayer = new FLyrVect();
706
        clonedLayer.bindToDataStore(getDataStore());
707
        // if (isJoined()) {
708
        // clonedLayer.setIsJoined(true);
709
        // }
710
        clonedLayer.setVisible(isVisible());
711
        // clonedLayer.setISpatialIndex(getISpatialIndex());
712
        clonedLayer.setName(getName());
713
        clonedLayer.setCoordTrans(getCoordTrans());
714

    
715
        clonedLayer.setLegend((IVectorLegend) getLegend().cloneLegend());
716

    
717
        clonedLayer.setIsLabeled(isLabeled());
718
        ILabelingStrategy labelingStrategy = getLabelingStrategy();
719
        if (labelingStrategy != null) {
720
            clonedLayer.setLabelingStrategy(labelingStrategy);
721
        }
722

    
723
        return clonedLayer;
724
    }
725

    
726
    protected boolean isOnePoint(AffineTransform graphicsTransform,
727
            ViewPort viewPort,
728
            double dpi,
729
            CartographicSupport csSym,
730
            Geometry geom,
731
            int[] xyCoords) {
732
        return isOnePoint(graphicsTransform, viewPort, geom, xyCoords)
733
                && csSym.getCartographicSize(viewPort, dpi, geom) <= 1;
734
    }
735

    
736
    private boolean isOnePoint(AffineTransform graphicsTransform,
737
            ViewPort viewPort,
738
            Geometry geom,
739
            int[] xyCoords) {
740
        boolean onePoint = false;
741
        int type = geom.getType();
742
        if (type == Geometry.TYPES.NULL) {
743
            return false;
744
        }
745
        if (type != Geometry.TYPES.POINT && type != Geometry.TYPES.MULTIPOINT) {
746

    
747
            Envelope geomBounds = geom.getEnvelope();
748

    
749
            // ICoordTrans ct = getCoordTrans();
750
            // Se supone que la geometria ya esta reproyectada
751
            // if (ct!=null) {
752
            // // geomBounds = ct.getInverted().convert(geomBounds);
753
            // geomBounds = geomBounds.convert(ct);
754
            // }
755
            double dist1Pixel = viewPort.getDist1pixel();
756

    
757
            onePoint
758
                    = (geomBounds.getLength(0) <= dist1Pixel && geomBounds.getLength(1) <= dist1Pixel);
759

    
760
            if (onePoint) {
761
                // avoid out of range exceptions
762
                org.gvsig.fmap.geom.primitive.Point p;
763
                try {
764
                    p
765
                            = geomManager.createPoint(geomBounds.getMinimum(0),
766
                                    geomBounds.getMinimum(1),
767
                                    SUBTYPES.GEOM2D);
768
                    p.transform(viewPort.getAffineTransform());
769
                    p.transform(graphicsTransform);
770
                    xyCoords[0] = (int) p.getX();
771
                    xyCoords[1] = (int) p.getY();
772
                } catch (org.gvsig.fmap.geom.exception.CreateGeometryException e) {
773
                    logger.error("Error creating a point", e);
774
                }
775

    
776
            }
777

    
778
        }
779
        return onePoint;
780
    }
781

    
782
    public boolean isLabeled() {
783
        return isLabeled;
784
    }
785

    
786
    public void setIsLabeled(boolean isLabeled) {
787
        this.isLabeled = isLabeled;
788
    }
789

    
790
    public ILabelingStrategy getLabelingStrategy() {
791
        return strategy;
792
    }
793

    
794
    public void setLabelingStrategy(ILabelingStrategy strategy) {
795
        this.strategy = strategy;
796
        if (strategy == null) {
797
            return;
798
        }
799
        strategy.setLayer(this);
800
        updateDrawVersion();
801
    }
802

    
803
    public void drawLabels(BufferedImage image,
804
            Graphics2D g,
805
            ViewPort viewPort,
806
            Cancellable cancel,
807
            double scale,
808
            double dpi) throws ReadException {
809
        if (strategy != null && isWithinScale(scale)) {
810
            strategy.draw(image, g, scale, viewPort, cancel, dpi);
811
        }
812
    }
813

    
814
    public void printLabels(Graphics2D g,
815
            ViewPort viewPort,
816
            Cancellable cancel,
817
            double scale,
818
            PrintAttributes properties) throws ReadException {
819
        if (strategy != null) {
820
            strategy.print(g, scale, viewPort, cancel, properties);
821
        }
822
    }
823

    
824
    /**
825
     * Return true, because a Vectorial Layer supports HyperLink
826
     *
827
     * @deprecated the hiperlink functionaliti is out the layer now
828
     */
829
    public boolean allowLinks() {
830
        return false;
831
    }
832

    
833
    public void load() throws LoadLayerException {
834
        super.load();
835
    }
836

    
837
    public FeatureStore getFeatureStore() {
838
        return (FeatureStore) getDataStore();
839
    }
840

    
841
    public FeatureQuery createFeatureQuery() {
842
        if (this.baseQuery == null) {
843
            return this.getFeatureStore().createFeatureQuery();
844
        }
845
        try {
846
            return (FeatureQuery) baseQuery.clone();
847
        } catch (CloneNotSupportedException ex) {
848
            throw new RuntimeException(ex);
849
        }
850
    }
851

    
852
    /**
853
     * @deprecated use instead
854
     * {@link #queryByPoint(org.gvsig.fmap.geom.primitive.Point, double, FeatureType)}
855
     */
856
    public FeatureSet queryByPoint(Point2D mapPoint,
857
            double tol,
858
            FeatureType featureType) throws DataException {
859
        logger.warn("Deprecated use of queryByPoint.");
860
        GeometryManager manager = GeometryLocator.getGeometryManager();
861
        org.gvsig.fmap.geom.primitive.Point center;
862
        try {
863
            center
864
                    = (org.gvsig.fmap.geom.primitive.Point) manager.create(TYPES.POINT,
865
                            SUBTYPES.GEOM2D);
866
            center.setX(mapPoint.getX());
867
            center.setY(mapPoint.getY());
868
            Circle circle
869
                    = (Circle) manager.create(TYPES.CIRCLE, SUBTYPES.GEOM2D);
870
            circle.setPoints(center, tol);
871
            return queryByGeometry(circle, featureType);
872
        } catch (org.gvsig.fmap.geom.exception.CreateGeometryException e) {
873
            throw new CreateGeometryException(TYPES.CIRCLE, SUBTYPES.GEOM2D, e);
874
        }
875
    }
876

    
877
    public FeatureSet queryByPoint(org.gvsig.fmap.geom.primitive.Point point,
878
            double tol,
879
            FeatureType featureType) throws DataException {
880
        GeometryManager manager = GeometryLocator.getGeometryManager();
881
        try {
882
            Circle circle
883
                    = (Circle) manager.create(TYPES.CIRCLE, SUBTYPES.GEOM2D);
884
            circle.setPoints(point, tol);
885
            return queryByGeometry(circle, featureType);
886
        } catch (org.gvsig.fmap.geom.exception.CreateGeometryException e) {
887
            throw new CreateGeometryException(TYPES.CIRCLE, SUBTYPES.GEOM2D, e);
888
        }
889
    }
890

    
891
    /**
892
     * Input geom must be in the CRS of the view.
893
     *
894
     * @param geom
895
     * @param featureType
896
     * @return
897
     * @throws DataException
898
     */
899
    public FeatureSet queryByGeometry(Geometry geom, FeatureType featureType) throws DataException {
900
        FeatureQuery featureQuery = createFeatureQuery();
901
        featureQuery.setFeatureType(featureType);
902

    
903
        Geometry query_geo = this.transformToSourceCRS(geom, true);
904
        IProjection query_proj = getMapContext().getProjection();
905
        if (this.getCoordTrans() != null) {
906
            query_proj = this.getCoordTrans().getPOrig();
907
        }
908
        Evaluator iee = SpatialEvaluatorsFactory.getInstance().intersects(
909
                query_geo,
910
                query_proj,
911
                featureStore
912
        );
913
        featureQuery.setFilter(iee);
914
        featureQuery.setAttributeNames(null);
915
        return getFeatureStore().getFeatureSet(featureQuery);
916

    
917
    }
918

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

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

    
954
        Envelope query_env = fromViewPortCRSToSourceCRS(this, envelope);
955
        IProjection query_proj = getMapContext().getProjection();
956
        if (this.getCoordTrans() != null) {
957
            query_proj = this.getCoordTrans().getPOrig();
958
        }
959

    
960
        Evaluator iee = SpatialEvaluatorsFactory.getInstance().intersects(
961
                query_env.getGeometry(),
962
                query_proj,
963
                featureStore
964
        );
965
        featureQuery.setFilter(iee);
966
        return getFeatureStore().getFeatureSet(featureQuery);
967

    
968
    }
969

    
970
    public DynObjectSet getInfo(Point p, double tolerance, Cancellable cancel) throws LoadLayerException,
971
            DataException {
972

    
973
        return getInfo(p, tolerance, cancel, true);
974
    }
975

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

    
987
    public DynObjectSet getInfo(org.gvsig.fmap.geom.primitive.Point p,
988
            double tolerance) throws LoadLayerException, DataException {
989
        return queryByPoint(p, tolerance, getFeatureStore().getDefaultFeatureType()).getDynObjectSet(false);
990
    }
991

    
992
    @Override
993
    public void legendCleared(LegendClearEvent event) {
994
        this.updateDrawVersion();
995
        LegendChangedEvent e = LegendChangedEvent.createLegendChangedEvent(legend, event);
996
        this.callLegendChanged(e);
997
    }
998

    
999
    @Override
1000
    public boolean symbolChanged(SymbolLegendEvent e) {
1001
        this.updateDrawVersion();
1002
        LegendChangedEvent ev = LegendChangedEvent.createLegendChangedEvent(legend, e);
1003
        this.callLegendChanged(ev);
1004
        return true;
1005
    }
1006

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

    
1022
                } else if (event.getType() == FeatureStoreNotification.AFTER_CANCELEDITING) {
1023

    
1024
                    setSpatialCacheEnabled(false);
1025
                    callEditionChanged(LayerEvent.createEditionChangedEvent(this, "edition"));
1026
                    this.updateDrawVersion();
1027

    
1028
                } else if (event.getType() == FeatureStoreNotification.AFTER_STARTEDITING) {
1029

    
1030
                    setSpatialCacheEnabled(true);
1031
                    callEditionChanged(LayerEvent.createEditionChangedEvent(this, "edition"));
1032

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

    
1061
    private boolean isLayerToSnap() {
1062

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

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

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

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

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

    
1122
    public static class RegisterPersistence
1123
            implements Callable {
1124

    
1125
        public Object call() {
1126
            PersistenceManager manager = ToolsLocator.getPersistenceManager();
1127

    
1128
            DynStruct definition
1129
                    = manager.addDefinition(FLyrVect.class,
1130
                            "FLyrVect",
1131
                            "FLyrVect Persistence definition",
1132
                            null,
1133
                            null);
1134
            definition.extend(PersistenceManager.PERSISTENCE_NAMESPACE,
1135
                    "FLyrDefault");
1136

    
1137
            definition.addDynFieldObject("legend")
1138
                    .setClassOfValue(IVectorLegend.class)
1139
                    .setMandatory(true);
1140
            definition.addDynFieldObject("featureStore")
1141
                    .setClassOfValue(FeatureStore.class)
1142
                    .setMandatory(true);
1143
            definition.addDynFieldBoolean("isLabeled").setMandatory(true);
1144
            definition.addDynFieldInt("typeShape").setMandatory(true);
1145
            definition.addDynFieldObject("labelingStrategy")
1146
                    .setClassOfValue(ILabelingStrategy.class)
1147
                    .setMandatory(false);
1148

    
1149
            return Boolean.TRUE;
1150
        }
1151
    }
1152

    
1153
    protected void doDispose() throws BaseException {
1154
        dispose(featureStore);
1155
        spatialCache.clearAll();
1156
    }
1157

    
1158
    /**
1159
     * Returns envelope in layer's data source CRS from envelope provided in
1160
     * viewport CRS
1161
     *
1162
     * @param lyr
1163
     * @param env
1164
     * @return
1165
     */
1166
    public static Envelope fromViewPortCRSToSourceCRS(FLayer lyr, Envelope env) {
1167

    
1168
        if (lyr == null || env == null) {
1169
            return null;
1170
        }
1171

    
1172
        ICoordTrans ct = lyr.getCoordTrans();
1173
        if (ct == null) {
1174
            return env;
1175
        } else {
1176
            return env.convert(ct.getInverted());
1177
        }
1178
    }
1179

    
1180
    public Geometry transformToSourceCRS(Geometry geom, boolean clone) {
1181
        return fromViewPortCRSToSourceCRS(this, geom, clone);
1182
    }
1183

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

    
1199
        if (lyr == null || geo == null) {
1200
            return null;
1201
        }
1202
        ICoordTrans ct = lyr.getCoordTrans();
1203
        Geometry resp = geo;
1204
        if (clone) {
1205
            resp = resp.cloneGeometry();
1206
        }
1207
        if (ct != null) {
1208
            resp.reProject(ct.getInverted());
1209
        }
1210
        return resp;
1211
    }
1212

    
1213
    public Iterator iterator() {
1214
        return this.getFeatureStore().iterator();
1215
    }
1216

    
1217
    protected class EnvelopeCantBeInitializedException
1218
            extends BaseException {
1219

    
1220
        /**
1221
         *
1222
         */
1223
        private static final long serialVersionUID = 4572797479347381552L;
1224
        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.";
1225
        private final static String MESSAGE_KEY = "_EnvelopeCantBeInitializedException";
1226

    
1227
        public EnvelopeCantBeInitializedException() {
1228
            super(MESSAGE_FORMAT, null, MESSAGE_KEY, serialVersionUID);
1229
        }
1230
    }
1231

    
1232
    @Override
1233
    public String getInfoString() {
1234
        LayerInformationBuilder builder = MapContextLocator.getMapContextManager().createLayerInformationBuilder();
1235

    
1236
        if (!this.isAvailable()) {
1237
            builder.title().labelkey("Warning");
1238
            builder.text().value("The layer is not available.");
1239
        }
1240

    
1241
        Envelope env = null;
1242
        FeatureAttributeDescriptor attrgeom = null;
1243

    
1244
        try {
1245
            env = this.getFullEnvelope();
1246
        } catch (Exception ex) {
1247
        }
1248
        FeatureStore store = this.getFeatureStore();
1249
        try {
1250
            FeatureType ft = store.getDefaultFeatureType();
1251
            attrgeom = ft.getDefaultGeometryAttribute();
1252
        } catch (Exception ex) {
1253
        }
1254

    
1255
        builder.title().labelkey("Data_source");
1256
        builder.property().labelkey("Source_type").value(store.getProviderName());
1257
        builder.property().labelkey("origen").value(store.getFullName());
1258
        if (store.getParameters() instanceof FilesystemStoreParameters) {
1259
            File f = ((FilesystemStoreParameters) (store.getParameters())).getFile();
1260
            if (f != null && f.exists()) {
1261
                builder.property().labelkey("_Size").value("%d bytes", f.length());
1262
            }
1263
        }
1264
        if (attrgeom != null) {
1265
            builder.property().labelkey("_Geometry_column").value(attrgeom.getName());
1266
            builder.property().labelkey("_Geometry_type").value(attrgeom.getGeomType().getFullName());
1267
        }
1268

    
1269
        builder.title().labelkey("_Coordenadas_geograficas");
1270
        if (this.isAvailable()) {
1271
            builder.envelope().value(env);
1272
        }
1273

    
1274
        builder.title().labelkey("_Projection");
1275
        if (this.isAvailable()) {
1276
            builder.property().value(this.getProjection());
1277
            builder.text().asWKT(this.getProjection());
1278
        }
1279
        return builder.toString();
1280
    }
1281

    
1282
    @Override
1283
    public boolean isTemporary() {
1284
        if (this.featureStore != null) {
1285
            if (this.featureStore.isTemporary()) {
1286
                return true;
1287
            }
1288
        }
1289
        return super.isTemporary();
1290
    }
1291
}