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

History | View | Annotate | Download (40.6 KB)

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

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

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

    
40
import org.gvsig.compat.print.PrintAttributes;
41
import org.gvsig.fmap.dal.DataStore;
42
import org.gvsig.fmap.dal.exception.DataException;
43
import org.gvsig.fmap.dal.exception.ReadException;
44
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
45
import org.gvsig.fmap.dal.feature.FeatureQuery;
46
import org.gvsig.fmap.dal.feature.FeatureSet;
47
import org.gvsig.fmap.dal.feature.FeatureStore;
48
import org.gvsig.fmap.dal.feature.FeatureStoreNotification;
49
import org.gvsig.fmap.dal.feature.FeatureType;
50
import org.gvsig.fmap.dal.feature.exception.CreateGeometryException;
51
import org.gvsig.fmap.geom.Geometry;
52
import org.gvsig.fmap.geom.Geometry.SUBTYPES;
53
import org.gvsig.fmap.geom.Geometry.TYPES;
54
import org.gvsig.fmap.geom.GeometryLocator;
55
import org.gvsig.fmap.geom.GeometryManager;
56
import org.gvsig.fmap.geom.exception.CreateEnvelopeException;
57
import org.gvsig.fmap.geom.primitive.Circle;
58
import org.gvsig.fmap.geom.primitive.Envelope;
59
import org.gvsig.fmap.geom.type.GeometryType;
60
import org.gvsig.fmap.geom.type.GeometryTypeNotSupportedException;
61
import org.gvsig.fmap.geom.type.GeometryTypeNotValidException;
62
import org.gvsig.fmap.mapcontext.MapContextLocator;
63
import org.gvsig.fmap.mapcontext.MapContextManager;
64
import org.gvsig.fmap.mapcontext.ViewPort;
65
import org.gvsig.fmap.mapcontext.exceptions.LegendLayerException;
66
import org.gvsig.fmap.mapcontext.exceptions.LoadLayerException;
67
import org.gvsig.fmap.mapcontext.exceptions.ReloadLayerException;
68
import org.gvsig.fmap.mapcontext.exceptions.StartEditionLayerException;
69
import org.gvsig.fmap.mapcontext.layers.FLayer;
70
import org.gvsig.fmap.mapcontext.layers.FLyrDefault;
71
import org.gvsig.fmap.mapcontext.layers.LayerEvent;
72
import org.gvsig.fmap.mapcontext.layers.SpatialCache;
73
import org.gvsig.fmap.mapcontext.rendering.legend.ILegend;
74
import org.gvsig.fmap.mapcontext.rendering.legend.IVectorLegend;
75
import org.gvsig.fmap.mapcontext.rendering.legend.LegendException;
76
import org.gvsig.fmap.mapcontext.rendering.legend.events.FeatureDrawnNotification;
77
import org.gvsig.fmap.mapcontext.rendering.legend.events.LegendChangedEvent;
78
import org.gvsig.fmap.mapcontext.rendering.legend.events.LegendClearEvent;
79
import org.gvsig.fmap.mapcontext.rendering.legend.events.LegendContentsChangedListener;
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.exception.BaseException;
88
import org.gvsig.tools.locator.LocatorException;
89
import org.gvsig.tools.observer.Observable;
90
import org.gvsig.tools.observer.Observer;
91
import org.gvsig.tools.persistence.PersistenceManager;
92
import org.gvsig.tools.persistence.PersistentState;
93
import org.gvsig.tools.persistence.exception.PersistenceException;
94
import org.gvsig.tools.task.Cancellable;
95
import org.gvsig.tools.util.Callable;
96

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

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

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

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

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

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

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

    
143
    }
144

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

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

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

    
180
        featureStore = (FeatureStore) dataStore;
181

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

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

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

    
193
        this.setLegend(legend);
194

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

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

    
206
        this.delegate(dataStore);
207

    
208
        dataStore.addObserver(this);
209

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

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

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

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

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

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

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

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

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

    
286
        FeatureQuery featureQuery = createFeatureQuery();
287

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

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

    
301
        try {
302

    
303
            long tini = System.currentTimeMillis();
304

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

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

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

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

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

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

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

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

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

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

    
434
    public void saveToState(PersistentState state) throws PersistenceException {
435

    
436
        FeatureStore featureStore = null;
437

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

    
443
        try {
444
            super.saveToState(state);
445

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

    
450
            featureStore = getFeatureStore();
451

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

    
456
            state.set("isLabeled", isLabeled);
457

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

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

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

    
475
    }
476

    
477
    public void loadFromState(PersistentState state) throws PersistenceException {
478

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

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

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

    
494
            vectorLegend = (IVectorLegend) state.get("legend");
495

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

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

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

    
520
            typeShape = state.getInt("typeShape");
521

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

    
529
    }
530

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

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

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

    
560
    public void setEditing(boolean b) throws StartEditionLayerException {
561

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

    
568
        if (b == super.isEditing()) {
569
            return;
570
        }
571

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

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

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

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

    
606
    public SpatialCache getSpatialCache() {
607
        return spatialCache;
608
    }
609

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

    
620
    }
621

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

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

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

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

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

    
659
    }
660

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

    
669
    protected void cleanLoadOptions() {
670
        this.loadLegend = null;
671
    }
672

    
673
    public boolean isWritable() {
674
        return getFeatureStore().allowWrite();
675
    }
676

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

    
688
        clonedLayer.setLegend((IVectorLegend) getLegend().cloneLegend());
689

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

    
696
        return clonedLayer;
697
    }
698

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

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

    
720
            Envelope geomBounds = geom.getEnvelope();
721

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

    
730
            onePoint
731
                    = (geomBounds.getLength(0) <= dist1Pixel && geomBounds.getLength(1) <= dist1Pixel);
732

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

    
749
            }
750

    
751
        }
752
        return onePoint;
753
    }
754

    
755
    public boolean isLabeled() {
756
        return isLabeled;
757
    }
758

    
759
    public void setIsLabeled(boolean isLabeled) {
760
        this.isLabeled = isLabeled;
761
    }
762

    
763
    public ILabelingStrategy getLabelingStrategy() {
764
        return strategy;
765
    }
766

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

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

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

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

    
806
    public void load() throws LoadLayerException {
807
        super.load();
808
    }
809

    
810
    public FeatureStore getFeatureStore() {
811
        return (FeatureStore) getDataStore();
812
    }
813

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

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

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

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

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

    
895
    }
896

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

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

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

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

    
948
    }
949

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

    
953
        return getInfo(p, tolerance, cancel, true);
954
    }
955

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

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

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

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

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

    
1002
                } else if (event.getType() == FeatureStoreNotification.AFTER_CANCELEDITING) {
1003

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

    
1008
                } else if (event.getType() == FeatureStoreNotification.AFTER_STARTEDITING) {
1009

    
1010
                    setSpatialCacheEnabled(true);
1011
                    callEditionChanged(LayerEvent.createEditionChangedEvent(this, "edition"));
1012

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

    
1041
    private boolean isLayerToSnap() {
1042

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

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

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

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

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

    
1102
    public static class RegisterPersistence implements Callable {
1103

    
1104
        public Object call() {
1105
            PersistenceManager manager = ToolsLocator.getPersistenceManager();
1106

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

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

    
1128
            return Boolean.TRUE;
1129
        }
1130
    }
1131

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

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

    
1147
        if (lyr == null || env == null) {
1148
            return null;
1149
        }
1150

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

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

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

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

    
1192
    public Iterator iterator() {
1193
        return this.getFeatureStore().iterator();
1194
    }
1195
}