Statistics
| Revision:

root / branches / v2_0_0_prep / extensions / org.gvsig.symbology / src / main / java / org / gvsig / symbology / fmap / mapcontext / rendering / legend / impl / AbstractVectorialLegend.java @ 33331

History | View | Annotate | Download (28.4 KB)

1
/* gvSIG. Geographic Information System of the Valencian Government
2
 *
3
 * Copyright (C) 2007-2008 Infrastructures and Transports Department
4
 * of the Valencian Government (CIT)
5
 * 
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 2
9
 * of the License, or (at your option) any later version.
10
 * 
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 * 
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 
19
 * MA  02110-1301, USA.
20
 * 
21
 */
22

    
23
/*
24
 * AUTHORS (In addition to CIT):
25
 * 2009 {DiSiD Technologies}  {{Task}}
26
 */
27
package org.gvsig.symbology.fmap.mapcontext.rendering.legend.impl;
28

    
29
import java.awt.Graphics2D;
30
import java.awt.geom.AffineTransform;
31
import java.awt.geom.PathIterator;
32
import java.awt.geom.Point2D;
33
import java.awt.image.BufferedImage;
34
import java.util.Iterator;
35
import java.util.Map;
36
import java.util.Map.Entry;
37

    
38
import org.cresques.cts.ICoordTrans;
39
import org.cresques.cts.IProjection;
40
import org.gvsig.compat.CompatLocator;
41
import org.gvsig.compat.print.PrintAttributes;
42
import org.gvsig.fmap.dal.exception.DataException;
43
import org.gvsig.fmap.dal.exception.ReadException;
44
import org.gvsig.fmap.dal.feature.Feature;
45
import org.gvsig.fmap.dal.feature.FeatureQuery;
46
import org.gvsig.fmap.dal.feature.FeatureSelection;
47
import org.gvsig.fmap.dal.feature.FeatureSet;
48
import org.gvsig.fmap.dal.feature.FeatureStore;
49
import org.gvsig.fmap.dal.feature.exception.ConcurrentDataModificationException;
50
import org.gvsig.fmap.geom.Geometry;
51
import org.gvsig.fmap.geom.Geometry.SUBTYPES;
52
import org.gvsig.fmap.geom.GeometryLocator;
53
import org.gvsig.fmap.geom.GeometryManager;
54
import org.gvsig.fmap.geom.aggregate.Aggregate;
55
import org.gvsig.fmap.geom.exception.CreateGeometryException;
56
import org.gvsig.fmap.geom.operation.DrawInts;
57
import org.gvsig.fmap.geom.operation.DrawOperationContext;
58
import org.gvsig.fmap.geom.operation.GeometryOperationException;
59
import org.gvsig.fmap.geom.operation.GeometryOperationNotSupportedException;
60
import org.gvsig.fmap.geom.primitive.Envelope;
61
import org.gvsig.fmap.geom.primitive.GeneralPathX;
62
import org.gvsig.fmap.mapcontext.MapContext;
63
import org.gvsig.fmap.mapcontext.MapContextException;
64
import org.gvsig.fmap.mapcontext.ViewPort;
65
import org.gvsig.fmap.mapcontext.layers.vectorial.ContainsEnvelopeEvaluator;
66
import org.gvsig.fmap.mapcontext.layers.vectorial.IntersectsEnvelopeEvaluator;
67
import org.gvsig.fmap.mapcontext.rendering.legend.ILegend;
68
import org.gvsig.fmap.mapcontext.rendering.legend.IVectorLegend;
69
import org.gvsig.fmap.mapcontext.rendering.legend.LegendException;
70
import org.gvsig.fmap.mapcontext.rendering.legend.ZSort;
71
import org.gvsig.fmap.mapcontext.rendering.symbols.CartographicSupport;
72
import org.gvsig.fmap.mapcontext.rendering.symbols.IMultiLayerSymbol;
73
import org.gvsig.fmap.mapcontext.rendering.symbols.ISymbol;
74
import org.gvsig.tools.ToolsLocator;
75
import org.gvsig.tools.dispose.DisposableIterator;
76
import org.gvsig.tools.dynobject.DynStruct;
77
import org.gvsig.tools.exception.BaseException;
78
import org.gvsig.tools.persistence.PersistenceManager;
79
import org.gvsig.tools.persistence.PersistentState;
80
import org.gvsig.tools.persistence.exception.PersistenceException;
81
import org.gvsig.tools.task.Cancellable;
82
import org.gvsig.tools.visitor.VisitCanceledException;
83
import org.gvsig.tools.visitor.Visitor;
84

    
85
/**
86
 * Base implementation for Vectorial data Legends.
87
 * 
88
 * Provides a draw method implementation which loads the {@link Feature}s and
89
 * uses the {@link ISymbol} objects to draw the {@link Geometry} objects.
90
 * 
91
 * @author 2009- <a href="cordinyana@gvsig.org">C?sar Ordi?ana</a> - gvSIG team
92
 */
93
public abstract class AbstractVectorialLegend extends AbstractLegend implements
94
                IVectorLegend {
95

    
96
        public static final String VECTORIAL_LEGEND_PERSISTENCE_DEFINITION_NAME =
97
                        "VectorialLegend";
98

    
99
        private static final String FIELD_HAS_ZSORT = "hasZSort";
100
        private static final String FIELD_SHAPETYPE = "shapeType";
101
        private static final String FIELD_DEFAULT_SYMBOL = "defaultSymbol";
102

    
103
        private static final GeometryManager geomManager =
104
                        GeometryLocator.getGeometryManager();
105

    
106
        protected ZSort zSort;
107

    
108
        public ZSort getZSort() {
109
                return zSort;
110
        }
111

    
112
        public void setZSort(ZSort zSort) {
113
                if (zSort == null) {
114
                        removeLegendListener(this.zSort);
115
                }
116
                this.zSort = zSort;
117
                addLegendListener(zSort);
118
        }
119

    
120
        @SuppressWarnings("unchecked")
121
        public void draw(BufferedImage image, Graphics2D g, ViewPort viewPort,
122
                        Cancellable cancel, double scale, Map queryParameters,
123
                        ICoordTrans coordTrans, FeatureStore featureStore)
124
                        throws LegendException {
125
                double dpi = MapContext.getScreenDPI();
126
                draw(image, g, viewPort, cancel, scale, queryParameters, coordTrans,
127
                                featureStore, dpi);
128
        }
129

    
130
        @SuppressWarnings("unchecked")
131
        public void print(Graphics2D g, ViewPort viewPort, Cancellable cancel,
132
                        double scale, Map queryParameters, ICoordTrans coordTrans,
133
                        FeatureStore featureStore, PrintAttributes properties)
134
                        throws LegendException {
135
                double dpi = 72;
136

    
137
                int resolution = properties.getPrintQuality();
138

    
139
                if (resolution == PrintAttributes.PRINT_QUALITY_NORMAL) {
140
                        dpi = 300;
141
                } else if (resolution == PrintAttributes.PRINT_QUALITY_HIGH) {
142
                        dpi = 600;
143
                } else if (resolution == PrintAttributes.PRINT_QUALITY_DRAFT) {
144
                        dpi = 72;
145
                }
146

    
147
                FeatureSet featureSet = null;
148
                DisposableIterator it = null;
149
                try {
150
                        ZSort zSort = getZSort();
151

    
152
                        // if layer has map levels it will use a ZSort
153
                        boolean useZSort = zSort != null && zSort.isUsingZSort();
154

    
155
                        int mapLevelCount = (useZSort) ? zSort.getLevelCount() : 1;
156
                        for (int mapPass = 0; mapPass < mapLevelCount; mapPass++) {
157
                                // Get the iterator over the visible features
158
                                String[] fieldNames =
159
                                                getRequiredFeatureAttributeNames(featureStore);
160

    
161
                                FeatureQuery featureQuery = featureStore.createFeatureQuery();
162
                                featureQuery.setAttributeNames(fieldNames);
163
                                featureQuery.setScale(scale);
164

    
165
                                ContainsEnvelopeEvaluator iee =
166
                                                new ContainsEnvelopeEvaluator(
167
                                                                viewPort.getAdjustedEnvelope(),
168
                                                                viewPort.getProjection(),
169
                                                                featureStore.getDefaultFeatureType(),
170
                                                                featureStore.getDefaultFeatureType()
171
                                                                                .getDefaultGeometryAttributeName());
172
                                featureQuery.setFilter(iee);
173
                                featureSet = featureStore.getFeatureSet(featureQuery);
174
                                it = featureSet.fastIterator();
175

    
176
                                // Iteration over each feature
177
                                while (!cancel.isCanceled() && it.hasNext()) {
178
                                        Feature feat = (Feature) it.next();
179
                                        Geometry geom = feat.getDefaultGeometry();
180

    
181
                                        // retrieve the symbol associated to such feature
182
                                        ISymbol sym = getSymbolByFeature(feat);
183
                                        if (sym == null) {
184
                                                continue;
185
                                        }
186
                                        if (useZSort) {
187
                                                int[] symLevels = zSort.getLevels(sym);
188
                                                if (symLevels != null) {
189

    
190
                                                        // Check if this symbol is a multilayer
191
                                                        if (sym instanceof IMultiLayerSymbol) {
192
                                                                // if so, get the layer corresponding to the
193
                                                                // current level. If none, continue to next
194
                                                                // iteration
195
                                                                IMultiLayerSymbol mlSym =
196
                                                                                (IMultiLayerSymbol) sym;
197
                                                                for (int i = 0; i < mlSym.getLayerCount(); i++) {
198
                                                                        ISymbol mySym = mlSym.getLayer(i);
199
                                                                        if (symLevels[i] == mapPass) {
200
                                                                                sym = mySym;
201
                                                                                break;
202
                                                                        }
203
                                                                }
204

    
205
                                                        } else {
206
                                                                // else, just draw the symbol in its level
207
                                                                if (symLevels[0] != mapPass) {
208
                                                                        continue;
209
                                                                }
210
                                                        }
211
                                                }
212
                                        }
213

    
214
                                        // Check if this symbol is sized with CartographicSupport
215
                                        CartographicSupport csSym = null;
216
                                        int symbolType = sym.getSymbolType();
217

    
218
                                        if (symbolType == Geometry.TYPES.POINT
219
                                                        || symbolType == Geometry.TYPES.CURVE
220
                                                        || sym instanceof CartographicSupport) {
221

    
222
                                                csSym = (CartographicSupport) sym;
223
                                        }
224

    
225
                                        DrawOperationContext doc = new DrawOperationContext();
226
                                        doc.setGraphics(g);
227
                                        doc.setViewPort(viewPort);
228
                                        if (csSym == null) {
229
                                                doc.setSymbol(sym);
230
                                        } else {
231
                                                doc.setDPI(dpi);
232
                                                doc.setCancellable(cancel);
233
                                                doc.setSymbol((ISymbol) csSym);
234
                                        }
235
                                        geom.invokeOperation(DrawInts.CODE, doc);
236
                                }
237
                        }
238
                } catch (ReadException e) {
239
                        throw new LegendDrawingException(e);
240
                } catch (GeometryOperationNotSupportedException e) {
241
                        throw new LegendDrawingException(e);
242
                } catch (GeometryOperationException e) {
243
                        throw new LegendDrawingException(e);
244
                } catch (DataException e) {
245
                        throw new LegendDrawingException(e);
246
                } catch (MapContextException e) {
247
                        throw new LegendDrawingException(e);
248
                } finally {
249
                        if (it != null) {
250
                                it.dispose();
251
                        }
252
                        if (featureSet != null) {
253
                                featureSet.dispose();
254
                        }
255
                }
256
        }
257

    
258
        /**
259
         * Draws the features from the {@link FeatureStore}, filtered with the scale
260
         * and the query parameters, with the symbols of the legend.
261
         */
262
        @SuppressWarnings("unchecked")
263
        protected void draw(BufferedImage image, Graphics2D g, ViewPort viewPort,
264
                        Cancellable cancel, double scale, Map queryParameters,
265
                        ICoordTrans coordTrans, FeatureStore featureStore, double dpi)
266
                        throws LegendException {
267

    
268
                if (!getDefaultSymbol().isShapeVisible()) {
269
                        return;
270
                }
271

    
272
                if (cancel.isCanceled()) {
273
                        return;
274
                }
275

    
276
                IProjection dataProjection;
277
                Envelope reprojectedDataEnvelop;
278

    
279
                try {
280
                        if (coordTrans == null) {
281
                                dataProjection =
282
                                                featureStore.getDefaultFeatureType().getDefaultSRS();
283

    
284
                                // If the data does not provide a projection, use the current
285
                                // view one
286
                                if (dataProjection == null) {
287
                                        dataProjection = viewPort.getProjection();
288
                                }
289

    
290
                                reprojectedDataEnvelop = featureStore.getEnvelope();
291
                        } else {
292
                                dataProjection = coordTrans.getPOrig();
293

    
294
                                reprojectedDataEnvelop =
295
                                                featureStore.getEnvelope().convert(coordTrans);
296
                        }
297
                } catch (DataException e) {
298
                        throw new LegendDrawingException(e);
299
                }
300

    
301
                // Gets the view envelope
302
                Envelope viewPortEnvelope = viewPort.getAdjustedEnvelope();
303

    
304
                // Gets the data envelope with the viewport SRS
305
                Envelope myEnvelope = reprojectedDataEnvelop;
306

    
307
                // TODO: in some cases, the legend may need a different check to
308
                // decide if the data must be drawn or not
309
                // Checks if the viewport envelope intersects with the data envelope
310
                if (!viewPortEnvelope.intersects(myEnvelope)) {
311
                        // The data is not visible in the current viewport, do nothing.
312
                        return;
313
                }
314

    
315
                // Check if all the data is contained into the viewport envelope
316
                boolean containsAll = viewPortEnvelope.contains(myEnvelope);
317

    
318
                // Create the drawing notification to be reused on each iteration
319
                DefaultFeatureDrawnNotification drawnNotification =
320
                                new DefaultFeatureDrawnNotification();
321

    
322
                if (cancel.isCanceled()) {
323
                        return;
324
                }
325

    
326
                FeatureSet featureSet = null;
327
                try {
328
                        FeatureSelection selection = featureStore.getFeatureSelection();
329

    
330
                        FeatureQuery featureQuery =
331
                                        createQuery(featureStore, scale, queryParameters,
332
                                                        coordTrans, dataProjection, viewPortEnvelope,
333
                                                        containsAll);
334

    
335
                        featureSet = featureStore.getFeatureSet(featureQuery);
336

    
337
                        if (cancel.isCanceled()) {
338
                                return;
339
                        }
340

    
341
                        drawFeatures(image, g, viewPort, cancel, coordTrans, dpi,
342
                                        drawnNotification, featureSet, selection);
343

    
344
                } catch (BaseException e) {
345
                        throw new LegendDrawingException(e);
346
                } finally {
347
                        if (featureSet != null) {
348
                                featureSet.dispose();
349
                        }
350
                }
351

    
352
        }
353

    
354
        /**
355
         * Creates a {@link FeatureQuery} to load the {@link Feature}s to draw.
356
         */
357
        @SuppressWarnings("unchecked")
358
        private FeatureQuery createQuery(FeatureStore featureStore, double scale,
359
                        Map queryParameters, ICoordTrans coordTrans,
360
                        IProjection dataProjection, Envelope viewPortEnvelope,
361
                        boolean containsAll) throws DataException {
362

    
363
                FeatureQuery featureQuery = featureStore.createFeatureQuery();
364
                featureQuery.setScale(scale);
365
                String[] fieldNames =
366
                        getRequiredFeatureAttributeNames(featureStore);
367
                featureQuery.setAttributeNames(fieldNames);
368
                // TODO: Mobile has it's own IntersectsEnvelopeEvaluator
369
                if (!containsAll) {
370
                        // Gets the viewport envelope with the data SRS
371
                        Envelope viewPortEnvelopeInMyProj = viewPortEnvelope;
372
                        // FIXME
373
                        if (coordTrans != null) {
374
                                viewPortEnvelopeInMyProj =
375
                                                viewPortEnvelope.convert(coordTrans.getInverted());
376
                        }
377

    
378
                        if (dataProjection == null) {
379
                                throw new IllegalArgumentException(
380
                                                "Error, the projection parameter value is null");
381
                        }
382

    
383
                        IntersectsEnvelopeEvaluator iee =
384
                                        new IntersectsEnvelopeEvaluator(viewPortEnvelopeInMyProj,
385
                                                        dataProjection,
386
                                                        featureStore.getDefaultFeatureType(),
387
                                                        featureStore.getDefaultFeatureType()
388
                                                                        .getDefaultGeometryAttributeName());
389
                        featureQuery.setFilter(iee);
390
                }
391
                if (queryParameters != null) {
392
                        Iterator iterEntry = queryParameters.entrySet().iterator();
393
                        Entry entry;
394
                        while (iterEntry.hasNext()) {
395
                                entry = (Entry) iterEntry.next();
396
                                featureQuery.setQueryParameter((String) entry.getKey(),
397
                                                entry.getValue());
398
                        }
399
                }
400
                return featureQuery;
401
        }
402

    
403
        /**
404
         * Draws the features from the {@link FeatureSet}, with the symbols of the
405
         * legend.
406
         */
407
        private void drawFeatures(BufferedImage image, Graphics2D g,
408
                        ViewPort viewPort, Cancellable cancel, ICoordTrans coordTrans,
409
                        double dpi, DefaultFeatureDrawnNotification drawnNotification,
410
                        FeatureSet featureSet, FeatureSelection selection)
411
                        throws BaseException {
412
                if (isUseZSort()) {
413
                        drawFeaturesMultiLayer(image, g, viewPort, cancel, coordTrans, dpi,
414
                                        drawnNotification, featureSet, selection);
415
                } else {
416
                        drawFeaturesSingleLayer(image, g, viewPort, cancel, coordTrans,
417
                                        dpi, drawnNotification, featureSet, selection);
418
                }
419
        }
420

    
421
        /**
422
         * Draws the features from the {@link FeatureSet}, with the symbols of the
423
         * legend, using a single drawing layer.
424
         */
425
        private void drawFeaturesSingleLayer(final BufferedImage image,
426
                        final Graphics2D g, final ViewPort viewPort,
427
                        final Cancellable cancel, final ICoordTrans coordTrans,
428
                        final double dpi,
429
                        final DefaultFeatureDrawnNotification drawnNotification,
430
                        FeatureSet featureSet, final FeatureSelection selection)
431
                        throws BaseException {
432

    
433
                try {
434
                        featureSet.accept(new Visitor() {
435
                                public void visit(Object obj) throws VisitCanceledException,
436
                                                BaseException {
437
                                        Feature feat = (Feature) obj;
438
                                        drawFeatureSingleLayer(image, g, viewPort, cancel,
439
                                                        coordTrans, dpi, drawnNotification, feat, selection);
440
                                }
441
                        });
442

    
443
                } catch (ConcurrentDataModificationException e) {
444
                        cancel.setCanceled(true);
445
                        return;
446
                }
447
        }
448

    
449
        /**
450
         * Draws a Feature with the symbols of the legend, using a single drawing
451
         * layer.
452
         */
453
        private void drawFeatureSingleLayer(BufferedImage image, Graphics2D g,
454
                        ViewPort viewPort, Cancellable cancel, ICoordTrans coordTrans,
455
                        double dpi, DefaultFeatureDrawnNotification drawnNotification,
456
                        Feature feat, FeatureSelection selection)
457
                        throws MapContextException, CreateGeometryException {
458

    
459
                Geometry geom = feat.getDefaultGeometry();
460

    
461
                if (geom.getType() == Geometry.TYPES.NULL) {
462
                        return;
463
                }
464

    
465
                ISymbol sym = getSymbol(feat, selection);
466
                if (sym == null) {
467
                        return;
468
                }
469

    
470
                if (coordTrans != null) {
471
                        geom = geom.cloneGeometry();
472
                        geom.reProject(coordTrans);
473
                }
474

    
475
                if (cancel.isCanceled()) {
476
                        return;
477
                }
478

    
479
                drawGeometry(geom, image, feat, sym, viewPort, g, dpi, cancel);
480

    
481
                // Notify the drawing observers
482
                drawnNotification.setFeature(feat);
483
                drawnNotification.setDrawnGeometry(geom);
484
                notifyObservers(drawnNotification);
485
        }
486

    
487
        /**
488
         * Draws the features from the {@link FeatureSet}, with the symbols of the
489
         * legend, using a multiple drawing layer.
490
         */
491
        private void drawFeaturesMultiLayer(BufferedImage image, Graphics2D g,
492
                        ViewPort viewPort, Cancellable cancel, ICoordTrans coordTrans,
493
                        double dpi, DefaultFeatureDrawnNotification drawnNotification,
494
                        FeatureSet featureSet, FeatureSelection selection)
495
                        throws MapContextException, CreateGeometryException, DataException {
496

    
497
                // -- visual FX stuff
498
                long time = System.currentTimeMillis();
499

    
500
                boolean bSymbolLevelError = false;
501
                // render temporary map each screenRefreshRate milliseconds;
502
                int screenRefreshDelay =
503
                                (int) ((1D / MapContext.getDrawFrameRate()) * 3 * 1000);
504
                BufferedImage[] imageLevels = null;
505
                Graphics2D[] graphics = null;
506

    
507
                imageLevels = new BufferedImage[getZSort().getLevelCount()];
508
                graphics = new Graphics2D[imageLevels.length];
509
                for (int i = 0; !cancel.isCanceled() && i < imageLevels.length; i++) {
510

    
511
                        imageLevels[i] =
512
                                        CompatLocator.getGraphicsUtils().createBufferedImage(
513
                                                        image.getWidth(), image.getHeight(),
514
                                                        image.getType());
515

    
516
                        graphics[i] = imageLevels[i].createGraphics();
517
                        graphics[i].setTransform(g.getTransform());
518
                        graphics[i].setRenderingHints(g.getRenderingHints());
519
                }
520
                // -- end visual FX stuff
521

    
522
                DisposableIterator it = null;
523
                try {
524
                        it = featureSet.fastIterator();
525
                        // Iteration over each feature
526
                        while (it.hasNext()) {
527
                                if (cancel.isCanceled()) {
528
                                        return;
529
                                }
530
                                Feature feat = (Feature) it.next();
531

    
532
                                bSymbolLevelError |=
533
                                                drawFeatureMultiLayer(image, g, viewPort, cancel,
534
                                                                coordTrans, dpi, drawnNotification,
535
                                                                selection, time, screenRefreshDelay,
536
                                                                imageLevels, graphics, feat);
537

    
538
                        }
539
                } catch (ConcurrentDataModificationException e) {
540
                        cancel.setCanceled(true);
541
                        return;
542
                } finally {
543
                        if (it != null) {
544
                                it.dispose();
545
                        }
546
                }
547

    
548
                g.drawImage(image, 0, 0, null);
549

    
550
                Point2D offset = viewPort.getOffset();
551
                CompatLocator.getGraphicsUtils().translate(g, offset.getX(),
552
                                offset.getY());
553

    
554
                for (int i = 0; !cancel.isCanceled() && i < imageLevels.length; i++) {
555
                        g.drawImage(imageLevels[i], 0, 0, null);
556
                        imageLevels[i] = null;
557
                        graphics[i] = null;
558
                }
559

    
560
                CompatLocator.getGraphicsUtils().translate(g, -offset.getX(),
561
                                -offset.getY());
562

    
563
                imageLevels = null;
564
                graphics = null;
565

    
566
                if (bSymbolLevelError) {
567
                        setZSort(null);
568
                }
569
        }
570

    
571
        /**
572
         * Draws a Feature with the symbols of the legend, using a multiple drawing
573
         * layer.
574
         */
575
        private boolean drawFeatureMultiLayer(BufferedImage image, Graphics2D g,
576
                        ViewPort viewPort, Cancellable cancel, ICoordTrans coordTrans,
577
                        double dpi, DefaultFeatureDrawnNotification drawnNotification,
578
                        FeatureSelection selection, long time,
579
                        int screenRefreshDelay, BufferedImage[] imageLevels,
580
                        Graphics2D[] graphics, Feature feat) throws MapContextException,
581
                        CreateGeometryException {
582

    
583
                Geometry geom = feat.getDefaultGeometry();
584
                boolean bSymbolLevelError = false;
585
                long drawingTime = time;
586

    
587
                if (geom.getType() == Geometry.TYPES.NULL) {
588
                        return false;
589
                }
590

    
591
                ISymbol sym = getSymbol(feat, selection);
592

    
593
                if (sym == null) {
594
                        return false;
595
                }
596

    
597
                if (coordTrans != null) {
598
                        geom = geom.cloneGeometry();
599
                        geom.reProject(coordTrans);
600
                }
601

    
602
                if (cancel.isCanceled()) {
603
                        return false;
604
                }
605

    
606
                // Check if this symbol is a multilayer
607
                int[] symLevels = getZSort().getLevels(sym);
608
                if (sym instanceof IMultiLayerSymbol) {
609
                        // if so, treat each of its layers as a single
610
                        // symbol
611
                        // in its corresponding map level
612
                        IMultiLayerSymbol mlSym = (IMultiLayerSymbol) sym;
613
                        for (int i = 0; !cancel.isCanceled() && i < mlSym.getLayerCount(); i++) {
614
                                ISymbol mySym = mlSym.getLayer(i);
615
                                int symbolLevel = 0;
616
                                if (symLevels != null) {
617
                                        symbolLevel = symLevels[i];
618
                                } else {
619
                                        /*
620
                                         * an error occured when managing symbol levels some of the
621
                                         * legend changed events regarding the symbols did not
622
                                         * finish satisfactory and the legend is now inconsistent.
623
                                         * For this drawing, it will finish as it was at the bottom
624
                                         * (level 0) but, when done, the ZSort will be reset to
625
                                         * avoid app crashes. This is a bug that has to be fixed.
626
                                         */
627
                                        bSymbolLevelError = true;
628
                                }
629
                                drawGeometry(geom, imageLevels[symbolLevel], feat, mySym,
630
                                                viewPort, graphics[symbolLevel], dpi, cancel);
631
                        }
632
                } else {
633
                        // else, just draw the symbol in its level
634
                        int symbolLevel = 0;
635
                        if (symLevels != null) {
636
                                symbolLevel = symLevels[0];
637
                        }
638
                        drawGeometry(geom, imageLevels[symbolLevel], feat, sym, viewPort,
639
                                        graphics[symbolLevel], dpi, cancel);
640
                }
641

    
642
                // -- visual FX stuff
643
                // Cuando el offset!=0 se est? dibujando sobre el
644
                // Layout y por tanto no tiene que ejecutar el
645
                // siguiente c?digo.
646
                Point2D offset = viewPort.getOffset();
647
                if (offset.getX() == 0 && offset.getY() == 0) {
648
                        if ((System.currentTimeMillis() - drawingTime) > screenRefreshDelay) {
649

    
650
                                BufferedImage virtualBim =
651
                                                CompatLocator.getGraphicsUtils().createBufferedImage(
652
                                                                image.getWidth(), image.getHeight(),
653
                                                                BufferedImage.TYPE_INT_ARGB);
654

    
655
                                Graphics2D virtualGraphics = virtualBim.createGraphics();
656
                                virtualGraphics.drawImage(image, 0, 0, null);
657
                                for (int i = 0; !cancel.isCanceled() && i < imageLevels.length; i++) {
658
                                        virtualGraphics.drawImage(imageLevels[i], 0, 0, null);
659
                                }
660
                                g.clearRect(0, 0, image.getWidth(), image.getHeight());
661
                                g.drawImage(virtualBim, 0, 0, null);
662
                                drawingTime = System.currentTimeMillis();
663
                        }
664
                        // -- end visual FX stuff
665

    
666
                }
667
                // Notify the drawing observers
668
                drawnNotification.setFeature(feat);
669
                drawnNotification.setDrawnGeometry(geom);
670
                notifyObservers(drawnNotification);
671
                return bSymbolLevelError;
672
        }
673

    
674
        /**
675
         * Returns the symbol to use to draw a {@link Feature} taking into account
676
         * if it is selected.
677
         */
678
        private ISymbol getSymbol(Feature feat, FeatureSelection selection)
679
                        throws MapContextException {
680
                // retrieve the symbol associated to such feature
681
                ISymbol sym = getSymbolByFeature(feat);
682

    
683
                if (sym != null && selection.isSelected(feat)) {
684
                        sym = sym.getSymbolForSelection();
685
                }
686
                return sym;
687
        }
688

    
689
        /**
690
         * Returns if the legend is using a ZSort.
691
         */
692
        private boolean isUseZSort() {
693
                return getZSort() != null && getZSort().isUsingZSort();
694
        }
695

    
696
        /**
697
         * Draws a {@link Geometry} using the given {@link ISymbol}, over the
698
         * {@link Graphics2D} object.
699
         */
700
        private void drawGeometry(Geometry geom, BufferedImage image,
701
                        Feature feature, ISymbol symbol, ViewPort viewPort,
702
                        Graphics2D graphics, double dpi, Cancellable cancellable)
703
                        throws CreateGeometryException {
704

    
705
                if (geom instanceof Aggregate) {
706
                        drawAggregate((Aggregate) geom, image, feature, symbol, viewPort,
707
                                        graphics, dpi, cancellable);
708
                } else {
709
                        boolean bDrawCartographicSupport = false;
710
                        if (symbol instanceof CartographicSupport) {
711
                                bDrawCartographicSupport =
712
                                                ((CartographicSupport) symbol).getUnit() != -1;
713
                        }
714

    
715
                        double previousSize = 0.0d;
716

    
717
                        if (bDrawCartographicSupport) {
718
                                // make the symbol to resize itself with the current rendering
719
                                // context
720
                                previousSize =
721
                                                ((CartographicSupport) symbol).toCartographicSize(
722
                                                                viewPort, dpi, geom);
723
                        }
724

    
725
                        try {
726
                                // draw it as normally
727
                                double[] dot = new double[2];
728
                                boolean onePoint =
729
                                                symbol.isOneDotOrPixel(geom, dot, viewPort, (int) dpi);
730

    
731
                                if (onePoint) {
732
                                        if (dot[0] < 0 || dot[1] < 0 || dot[0] >= image.getWidth()
733
                                                        || dot[1] >= image.getHeight()) {
734
                                                return;
735
                                        }
736
                                        // FIXME: setRgb should change to draw
737
                                        image.setRGB((int) dot[0], (int) dot[1],
738
                                                        symbol.getOnePointRgb());
739
                                } else {
740
                                        Geometry decimatedShape =
741
                                                        transformToInts(geom, viewPort.getAffineTransform());
742
                                        symbol.draw(graphics, viewPort.getAffineTransform(),
743
                                                        decimatedShape, feature, cancellable);
744
                                }
745

    
746
                        } finally {
747
                                if (bDrawCartographicSupport) {
748
                                        // restore previous size
749
                                        ((CartographicSupport) symbol).setCartographicSize(
750
                                                        previousSize, geom);
751
                                }
752
                        }
753
                }
754
        }
755

    
756
        /**
757
         * Draws an {@link Aggregate} using the given {@link ISymbol}, over the
758
         * {@link Graphics2D} object.
759
         */
760
        private void drawAggregate(Aggregate aggregate, BufferedImage image,
761
                        Feature feature, ISymbol symbol, ViewPort viewPort,
762
                        Graphics2D graphics, double dpi, Cancellable cancellable)
763
                        throws CreateGeometryException {
764
                for (int i = 0; i < aggregate.getPrimitivesNumber(); i++) {
765
                        Geometry prim = aggregate.getPrimitiveAt(i);
766
                        drawGeometry(prim, image, feature, symbol, viewPort, graphics, dpi,
767
                                        cancellable);
768
                }
769
        }
770

    
771
        /**
772
         * Transforms a Geometry from map coordinates to the view coordinates.
773
         */
774
        private Geometry transformToInts(Geometry gp, AffineTransform at)
775
                        throws CreateGeometryException {
776
                GeneralPathX newGp = new GeneralPathX();
777
                double[] theData = new double[6];
778
                double[] aux = new double[6];
779

    
780
                // newGp.reset();
781
                PathIterator theIterator;
782
                int theType;
783
                int numParts = 0;
784

    
785
                java.awt.geom.Point2D ptDst = new java.awt.geom.Point2D.Double();
786
                java.awt.geom.Point2D ptSrc = new java.awt.geom.Point2D.Double();
787
                boolean bFirst = true;
788
                int xInt, yInt, antX = -1, antY = -1;
789

    
790
                theIterator = gp.getPathIterator(null); // , flatness);
791
                int numSegmentsAdded = 0;
792
                while (!theIterator.isDone()) {
793
                        theType = theIterator.currentSegment(theData);
794

    
795
                        switch (theType) {
796
                        case PathIterator.SEG_MOVETO:
797
                                numParts++;
798
                                ptSrc.setLocation(theData[0], theData[1]);
799
                                at.transform(ptSrc, ptDst);
800
                                antX = (int) ptDst.getX();
801
                                antY = (int) ptDst.getY();
802
                                newGp.moveTo(antX, antY);
803
                                numSegmentsAdded++;
804
                                bFirst = true;
805
                                break;
806

    
807
                        case PathIterator.SEG_LINETO:
808
                                ptSrc.setLocation(theData[0], theData[1]);
809
                                at.transform(ptSrc, ptDst);
810
                                xInt = (int) ptDst.getX();
811
                                yInt = (int) ptDst.getY();
812
                                if ((bFirst) || ((xInt != antX) || (yInt != antY))) {
813
                                        newGp.lineTo(xInt, yInt);
814
                                        antX = xInt;
815
                                        antY = yInt;
816
                                        bFirst = false;
817
                                        numSegmentsAdded++;
818
                                }
819
                                break;
820

    
821
                        case PathIterator.SEG_QUADTO:
822
                                at.transform(theData, 0, aux, 0, 2);
823
                                newGp.quadTo(aux[0], aux[1], aux[2], aux[3]);
824
                                numSegmentsAdded++;
825
                                break;
826

    
827
                        case PathIterator.SEG_CUBICTO:
828
                                at.transform(theData, 0, aux, 0, 3);
829
                                newGp.curveTo(aux[0], aux[1], aux[2], aux[3], aux[4], aux[5]);
830
                                numSegmentsAdded++;
831
                                break;
832

    
833
                        case PathIterator.SEG_CLOSE:
834
                                if (numSegmentsAdded < 3) {
835
                                        newGp.lineTo(antX, antY);
836
                                }
837
                                newGp.closePath();
838

    
839
                                break;
840
                        } // end switch
841

    
842
                        theIterator.next();
843
                } // end while loop
844

    
845
                Geometry geom = null;
846
                switch (gp.getType()) {
847
                case Geometry.TYPES.POINT:
848
                        geom =
849
                                        geomManager.createPoint(ptDst.getX(), ptDst.getY(),
850
                                                        SUBTYPES.GEOM2D);
851
                        break;
852
                case Geometry.TYPES.CURVE:
853
                case Geometry.TYPES.ARC:
854
                        geom = geomManager.createCurve(newGp, SUBTYPES.GEOM2D);
855
                        break;
856

    
857
                case Geometry.TYPES.SURFACE:
858
                case Geometry.TYPES.CIRCLE:
859
                case Geometry.TYPES.ELLIPSE:
860

    
861
                        geom = geomManager.createSurface(newGp, SUBTYPES.GEOM2D);
862
                        break;
863
                }
864
                return geom;
865
        }
866

    
867
        public Object clone() throws CloneNotSupportedException {
868
                AbstractVectorialLegend clone = (AbstractVectorialLegend) super.clone();
869

    
870
                // Clone zSort
871
                ZSort zSort = getZSort();
872
                if (zSort != null) {
873
                        clone.setZSort(new ZSort(clone));
874
                }
875
                return clone;
876
        }
877

    
878
        public void loadFromState(PersistentState state)
879
                        throws PersistenceException {
880
                // Set parent properties
881
                super.loadFromState(state);
882
                // Set own properties
883
                
884
                setShapeType(state.getInt(FIELD_SHAPETYPE));
885
                if (state.getBoolean(FIELD_HAS_ZSORT)) {
886
                        setZSort(new ZSort(this));
887
                }
888
                setDefaultSymbol((ISymbol) state.get(FIELD_DEFAULT_SYMBOL));
889
        }
890

    
891
        public void saveToState(PersistentState state) throws PersistenceException {
892
                // Save parent properties
893
                super.saveToState(state);
894
                // Save own properties
895
                state.set(FIELD_SHAPETYPE, getShapeType());
896
                state.set(FIELD_HAS_ZSORT, this.zSort!=null);
897
                state.set(FIELD_DEFAULT_SYMBOL, getDefaultSymbol());
898
        }
899

    
900
        /**
901
         * Registers the legend dynClass definition.
902
         */
903
        public static void registerPersistence() {
904
                // Add the DynClass definition.
905
                PersistenceManager manager = ToolsLocator.getPersistenceManager();
906
                DynStruct definition = manager.addDefinition(
907
                        AbstractVectorialLegend.class, 
908
                        VECTORIAL_LEGEND_PERSISTENCE_DEFINITION_NAME, 
909
                        VECTORIAL_LEGEND_PERSISTENCE_DEFINITION_NAME + " persistence definition", 
910
                        null, 
911
                        null
912
                );
913
                // Extend the Legend base definition
914
                definition.extend(LEGEND_PERSISTENCE_DEFINITION_NAME);
915

    
916
                // Shapetype
917
                definition.addDynFieldInt(FIELD_SHAPETYPE)
918
                        .setMandatory(true);
919
                // ZSort
920
                definition.addDynFieldBoolean(FIELD_HAS_ZSORT)
921
                        .setMandatory(true);
922
                // Default symbol
923
                definition.addDynFieldObject(FIELD_DEFAULT_SYMBOL)
924
                        .setClassOfValue(ISymbol.class)
925
                        .setMandatory(true);
926
        }
927

    
928
        /**
929
         * Returns the names of the {@link Feature} attributes required for the
930
         * {@link ILegend} to operate.
931
         * 
932
         * @param featureStore
933
         *            the store where the {@link Feature}s belong to
934
         * @return the names of required {@link Feature} attribute names
935
         * @throws DataException
936
         *             if there is an error getting the attribute names
937
         */
938
        protected abstract String[] getRequiredFeatureAttributeNames(
939
                        FeatureStore featureStore) throws DataException;
940
}