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

History | View | Annotate | Download (27.8 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.DisposableIterator;
45
import org.gvsig.fmap.dal.feature.Feature;
46
import org.gvsig.fmap.dal.feature.FeatureQuery;
47
import org.gvsig.fmap.dal.feature.FeatureSelection;
48
import org.gvsig.fmap.dal.feature.FeatureSet;
49
import org.gvsig.fmap.dal.feature.FeatureStore;
50
import org.gvsig.fmap.dal.feature.exception.ConcurrentDataModificationException;
51
import org.gvsig.fmap.geom.Geometry;
52
import org.gvsig.fmap.geom.GeometryLocator;
53
import org.gvsig.fmap.geom.GeometryManager;
54
import org.gvsig.fmap.geom.Geometry.SUBTYPES;
55
import org.gvsig.fmap.geom.aggregate.Aggregate;
56
import org.gvsig.fmap.geom.exception.CreateGeometryException;
57
import org.gvsig.fmap.geom.operation.DrawInts;
58
import org.gvsig.fmap.geom.operation.DrawOperationContext;
59
import org.gvsig.fmap.geom.operation.GeometryOperationException;
60
import org.gvsig.fmap.geom.operation.GeometryOperationNotSupportedException;
61
import org.gvsig.fmap.geom.primitive.Envelope;
62
import org.gvsig.fmap.geom.primitive.GeneralPathX;
63
import org.gvsig.fmap.mapcontext.MapContext;
64
import org.gvsig.fmap.mapcontext.MapContextException;
65
import org.gvsig.fmap.mapcontext.ViewPort;
66
import org.gvsig.fmap.mapcontext.layers.vectorial.ContainsEnvelopeEvaluator;
67
import org.gvsig.fmap.mapcontext.layers.vectorial.IntersectsEnvelopeEvaluator;
68
import org.gvsig.fmap.mapcontext.rendering.legend.ILegend;
69
import org.gvsig.fmap.mapcontext.rendering.legend.IVectorLegend;
70
import org.gvsig.fmap.mapcontext.rendering.legend.LegendException;
71
import org.gvsig.fmap.mapcontext.rendering.legend.ZSort;
72
import org.gvsig.fmap.mapcontext.rendering.symbols.CartographicSupport;
73
import org.gvsig.fmap.mapcontext.rendering.symbols.IMultiLayerSymbol;
74
import org.gvsig.fmap.mapcontext.rendering.symbols.ISymbol;
75
import org.gvsig.tools.ToolsLocator;
76
import org.gvsig.tools.dynobject.DynClass;
77
import org.gvsig.tools.exception.BaseException;
78
import org.gvsig.tools.persistence.PersistentState;
79
import org.gvsig.tools.persistence.exception.PersistenceException;
80
import org.gvsig.tools.task.Cancellable;
81
import org.gvsig.tools.visitor.VisitCanceledException;
82
import org.gvsig.tools.visitor.Visitor;
83

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

    
95
        public static final String VECTORIAL_LEGEND_DYNCLASS_NAME =
96
                        "VectorialLegend";
97

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

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

    
105
        protected ZSort zSort;
106

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

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

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

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

    
136
                int resolution = properties.getPrintQuality();
137

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

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

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

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

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

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

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

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

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

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

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

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

    
221
                                                csSym = (CartographicSupport) sym;
222
                                        }
223

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

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

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

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

    
275
                IProjection dataProjection;
276
                Envelope reprojectedDataEnvelop;
277

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

    
283
                                reprojectedDataEnvelop = featureStore.getEnvelope();
284
                        } else {
285
                                dataProjection = coordTrans.getPOrig();
286

    
287
                                reprojectedDataEnvelop =
288
                                                featureStore.getEnvelope().convert(coordTrans);
289
                        }
290
                } catch (DataException e) {
291
                        throw new LegendDrawingException(e);
292
                }
293

    
294
                // Gets the view envelope
295
                Envelope viewPortEnvelope = viewPort.getAdjustedEnvelope();
296

    
297
                // Gets the data envelope with the viewport SRS
298
                Envelope myEnvelope = reprojectedDataEnvelop;
299

    
300
                // TODO: in some cases, the legend may need a different check to
301
                // decide if the data must be drawn or not
302
                // Checks if the viewport envelope intersects with the data envelope
303
                if (!viewPortEnvelope.intersects(myEnvelope)) {
304
                        // The data is not visible in the current viewport, do nothing.
305
                        return;
306
                }
307

    
308
                // Check if all the data is contained into the viewport envelope
309
                boolean containsAll = viewPortEnvelope.contains(myEnvelope);
310

    
311
                // Create the drawing notification to be reused on each iteration
312
                DefaultFeatureDrawnNotification drawnNotification =
313
                                new DefaultFeatureDrawnNotification();
314

    
315
                if (cancel.isCanceled()) {
316
                        return;
317
                }
318

    
319
                FeatureSet featureSet = null;
320
                try {
321
                        FeatureSelection selection = featureStore.getFeatureSelection();
322

    
323
                        FeatureQuery featureQuery =
324
                                        createQuery(featureStore, scale, queryParameters,
325
                                                        coordTrans, dataProjection, viewPortEnvelope,
326
                                                        containsAll);
327

    
328
                        featureSet = featureStore.getFeatureSet(featureQuery);
329

    
330
                        if (cancel.isCanceled()) {
331
                                return;
332
                        }
333

    
334
                        drawFeatures(image, g, viewPort, cancel, coordTrans, dpi,
335
                                        drawnNotification, featureSet, selection);
336

    
337
                } catch (BaseException e) {
338
                        throw new LegendDrawingException(e);
339
                } finally {
340
                        if (featureSet != null) {
341
                                featureSet.dispose();
342
                        }
343
                }
344

    
345
        }
346

    
347
        /**
348
         * Creates a {@link FeatureQuery} to load the {@link Feature}s to draw.
349
         */
350
        @SuppressWarnings("unchecked")
351
        private FeatureQuery createQuery(FeatureStore featureStore, double scale,
352
                        Map queryParameters, ICoordTrans coordTrans,
353
                        IProjection dataProjection, Envelope viewPortEnvelope,
354
                        boolean containsAll) throws DataException {
355

    
356
                FeatureQuery featureQuery = featureStore.createFeatureQuery();
357
                featureQuery.setScale(scale);
358
                String[] fieldNames =
359
                        getRequiredFeatureAttributeNames(featureStore);
360
                featureQuery.setAttributeNames(fieldNames);
361
                // TODO: Mobile has it's own IntersectsEnvelopeEvaluator
362
                if (!containsAll) {
363
                        // Gets the viewport envelope with the data SRS
364
                        Envelope viewPortEnvelopeInMyProj = viewPortEnvelope;
365
                        // FIXME
366
                        if (coordTrans != null) {
367
                                viewPortEnvelopeInMyProj =
368
                                                viewPortEnvelope.convert(coordTrans.getInverted());
369
                        }
370

    
371
                        IntersectsEnvelopeEvaluator iee =
372
                                        new IntersectsEnvelopeEvaluator(viewPortEnvelopeInMyProj,
373
                                                        dataProjection,
374
                                                        featureStore.getDefaultFeatureType(),
375
                                                        featureStore.getDefaultFeatureType()
376
                                                                        .getDefaultGeometryAttributeName());
377
                        featureQuery.setFilter(iee);
378
                }
379
                if (queryParameters != null) {
380
                        Iterator iterEntry = queryParameters.entrySet().iterator();
381
                        Entry entry;
382
                        while (iterEntry.hasNext()) {
383
                                entry = (Entry) iterEntry.next();
384
                                featureQuery.setQueryParameter((String) entry.getKey(),
385
                                                entry.getValue());
386
                        }
387
                }
388
                return featureQuery;
389
        }
390

    
391
        /**
392
         * Draws the features from the {@link FeatureSet}, with the symbols of the
393
         * legend.
394
         */
395
        private void drawFeatures(BufferedImage image, Graphics2D g,
396
                        ViewPort viewPort, Cancellable cancel, ICoordTrans coordTrans,
397
                        double dpi, DefaultFeatureDrawnNotification drawnNotification,
398
                        FeatureSet featureSet, FeatureSelection selection)
399
                        throws BaseException {
400
                if (isUseZSort()) {
401
                        drawFeaturesMultiLayer(image, g, viewPort, cancel, coordTrans, dpi,
402
                                        drawnNotification, featureSet, selection);
403
                } else {
404
                        drawFeaturesSingleLayer(image, g, viewPort, cancel, coordTrans,
405
                                        dpi, drawnNotification, featureSet, selection);
406
                }
407
        }
408

    
409
        /**
410
         * Draws the features from the {@link FeatureSet}, with the symbols of the
411
         * legend, using a single drawing layer.
412
         */
413
        private void drawFeaturesSingleLayer(final BufferedImage image,
414
                        final Graphics2D g, final ViewPort viewPort,
415
                        final Cancellable cancel, final ICoordTrans coordTrans,
416
                        final double dpi,
417
                        final DefaultFeatureDrawnNotification drawnNotification,
418
                        FeatureSet featureSet, final FeatureSelection selection)
419
                        throws BaseException {
420

    
421
                try {
422
                        featureSet.accept(new Visitor() {
423
                                public void visit(Object obj) throws VisitCanceledException,
424
                                                BaseException {
425
                                        Feature feat = (Feature) obj;
426
                                        drawFeatureSingleLayer(image, g, viewPort, cancel,
427
                                                        coordTrans, dpi, drawnNotification, feat, selection);
428
                                }
429
                        });
430

    
431
                } catch (ConcurrentDataModificationException e) {
432
                        cancel.setCanceled(true);
433
                        return;
434
                }
435
        }
436

    
437
        /**
438
         * Draws a Feature with the symbols of the legend, using a single drawing
439
         * layer.
440
         */
441
        private void drawFeatureSingleLayer(BufferedImage image, Graphics2D g,
442
                        ViewPort viewPort, Cancellable cancel, ICoordTrans coordTrans,
443
                        double dpi, DefaultFeatureDrawnNotification drawnNotification,
444
                        Feature feat, FeatureSelection selection)
445
                        throws MapContextException, CreateGeometryException {
446

    
447
                Geometry geom = feat.getDefaultGeometry();
448

    
449
                if (geom.getType() == Geometry.TYPES.NULL) {
450
                        return;
451
                }
452

    
453
                ISymbol sym = getSymbol(feat, selection);
454
                if (sym == null) {
455
                        return;
456
                }
457

    
458
                if (coordTrans != null) {
459
                        geom = geom.cloneGeometry();
460
                        geom.reProject(coordTrans);
461
                }
462

    
463
                if (cancel.isCanceled()) {
464
                        return;
465
                }
466

    
467
                drawGeometry(geom, image, feat, sym, viewPort, g, dpi, cancel);
468

    
469
                // Notify the drawing observers
470
                drawnNotification.setFeature(feat);
471
                drawnNotification.setDrawnGeometry(geom);
472
                notifyObservers(drawnNotification);
473
        }
474

    
475
        /**
476
         * Draws the features from the {@link FeatureSet}, with the symbols of the
477
         * legend, using a multiple drawing layer.
478
         */
479
        private void drawFeaturesMultiLayer(BufferedImage image, Graphics2D g,
480
                        ViewPort viewPort, Cancellable cancel, ICoordTrans coordTrans,
481
                        double dpi, DefaultFeatureDrawnNotification drawnNotification,
482
                        FeatureSet featureSet, FeatureSelection selection)
483
                        throws MapContextException, CreateGeometryException, DataException {
484

    
485
                // -- visual FX stuff
486
                long time = System.currentTimeMillis();
487

    
488
                boolean bSymbolLevelError = false;
489
                // render temporary map each screenRefreshRate milliseconds;
490
                int screenRefreshDelay =
491
                                (int) ((1D / MapContext.getDrawFrameRate()) * 3 * 1000);
492
                BufferedImage[] imageLevels = null;
493
                Graphics2D[] graphics = null;
494

    
495
                imageLevels = new BufferedImage[getZSort().getLevelCount()];
496
                graphics = new Graphics2D[imageLevels.length];
497
                for (int i = 0; !cancel.isCanceled() && i < imageLevels.length; i++) {
498

    
499
                        imageLevels[i] =
500
                                        CompatLocator.getGraphicsUtils().createBufferedImage(
501
                                                        image.getWidth(), image.getHeight(),
502
                                                        image.getType());
503

    
504
                        graphics[i] = imageLevels[i].createGraphics();
505
                        graphics[i].setTransform(g.getTransform());
506
                        graphics[i].setRenderingHints(g.getRenderingHints());
507
                }
508
                // -- end visual FX stuff
509

    
510
                DisposableIterator it = null;
511
                try {
512
                        it = featureSet.fastIterator();
513
                        // Iteration over each feature
514
                        while (it.hasNext()) {
515
                                if (cancel.isCanceled()) {
516
                                        return;
517
                                }
518
                                Feature feat = (Feature) it.next();
519

    
520
                                bSymbolLevelError |=
521
                                                drawFeatureMultiLayer(image, g, viewPort, cancel,
522
                                                                coordTrans, dpi, drawnNotification,
523
                                                                selection, time, screenRefreshDelay,
524
                                                                imageLevels, graphics, feat);
525

    
526
                        }
527
                } catch (ConcurrentDataModificationException e) {
528
                        cancel.setCanceled(true);
529
                        return;
530
                } finally {
531
                        if (it != null) {
532
                                it.dispose();
533
                        }
534
                }
535

    
536
                g.drawImage(image, 0, 0, null);
537

    
538
                Point2D offset = viewPort.getOffset();
539
                CompatLocator.getGraphicsUtils().translate(g, offset.getX(),
540
                                offset.getY());
541

    
542
                for (int i = 0; !cancel.isCanceled() && i < imageLevels.length; i++) {
543
                        g.drawImage(imageLevels[i], 0, 0, null);
544
                        imageLevels[i] = null;
545
                        graphics[i] = null;
546
                }
547

    
548
                CompatLocator.getGraphicsUtils().translate(g, -offset.getX(),
549
                                -offset.getY());
550

    
551
                imageLevels = null;
552
                graphics = null;
553

    
554
                if (bSymbolLevelError) {
555
                        setZSort(null);
556
                }
557
        }
558

    
559
        /**
560
         * Draws a Feature with the symbols of the legend, using a multiple drawing
561
         * layer.
562
         */
563
        private boolean drawFeatureMultiLayer(BufferedImage image, Graphics2D g,
564
                        ViewPort viewPort, Cancellable cancel, ICoordTrans coordTrans,
565
                        double dpi, DefaultFeatureDrawnNotification drawnNotification,
566
                        FeatureSelection selection, long time,
567
                        int screenRefreshDelay, BufferedImage[] imageLevels,
568
                        Graphics2D[] graphics, Feature feat) throws MapContextException,
569
                        CreateGeometryException {
570

    
571
                Geometry geom = feat.getDefaultGeometry();
572
                boolean bSymbolLevelError = false;
573
                long drawingTime = time;
574

    
575
                if (geom.getType() == Geometry.TYPES.NULL) {
576
                        return false;
577
                }
578

    
579
                ISymbol sym = getSymbol(feat, selection);
580

    
581
                if (sym == null) {
582
                        return false;
583
                }
584

    
585
                if (coordTrans != null) {
586
                        geom = geom.cloneGeometry();
587
                        geom.reProject(coordTrans);
588
                }
589

    
590
                if (cancel.isCanceled()) {
591
                        return false;
592
                }
593

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

    
630
                // -- visual FX stuff
631
                // Cuando el offset!=0 se est? dibujando sobre el
632
                // Layout y por tanto no tiene que ejecutar el
633
                // siguiente c?digo.
634
                Point2D offset = viewPort.getOffset();
635
                if (offset.getX() == 0 && offset.getY() == 0) {
636
                        if ((System.currentTimeMillis() - drawingTime) > screenRefreshDelay) {
637

    
638
                                BufferedImage virtualBim =
639
                                                CompatLocator.getGraphicsUtils().createBufferedImage(
640
                                                                image.getWidth(), image.getHeight(),
641
                                                                BufferedImage.TYPE_INT_ARGB);
642

    
643
                                Graphics2D virtualGraphics = virtualBim.createGraphics();
644
                                virtualGraphics.drawImage(image, 0, 0, null);
645
                                for (int i = 0; !cancel.isCanceled() && i < imageLevels.length; i++) {
646
                                        virtualGraphics.drawImage(imageLevels[i], 0, 0, null);
647
                                }
648
                                g.clearRect(0, 0, image.getWidth(), image.getHeight());
649
                                g.drawImage(virtualBim, 0, 0, null);
650
                                drawingTime = System.currentTimeMillis();
651
                        }
652
                        // -- end visual FX stuff
653

    
654
                }
655
                // Notify the drawing observers
656
                drawnNotification.setFeature(feat);
657
                drawnNotification.setDrawnGeometry(geom);
658
                notifyObservers(drawnNotification);
659
                return bSymbolLevelError;
660
        }
661

    
662
        /**
663
         * Returns the symbol to use to draw a {@link Feature} taking into account
664
         * if it is selected.
665
         */
666
        private ISymbol getSymbol(Feature feat, FeatureSelection selection)
667
                        throws MapContextException {
668
                // retrieve the symbol associated to such feature
669
                ISymbol sym = getSymbolByFeature(feat);
670

    
671
                if (sym != null && selection.isSelected(feat)) {
672
                        sym = sym.getSymbolForSelection();
673
                }
674
                return sym;
675
        }
676

    
677
        /**
678
         * Returns if the legend is using a ZSort.
679
         */
680
        private boolean isUseZSort() {
681
                return getZSort() != null && getZSort().isUsingZSort();
682
        }
683

    
684
        /**
685
         * Draws a {@link Geometry} using the given {@link ISymbol}, over the
686
         * {@link Graphics2D} object.
687
         */
688
        private void drawGeometry(Geometry geom, BufferedImage image,
689
                        Feature feature, ISymbol symbol, ViewPort viewPort,
690
                        Graphics2D graphics, double dpi, Cancellable cancellable)
691
                        throws CreateGeometryException {
692

    
693
                if (geom instanceof Aggregate) {
694
                        drawAggregate((Aggregate) geom, image, feature, symbol, viewPort,
695
                                        graphics, dpi, cancellable);
696
                } else {
697
                        boolean bDrawCartographicSupport = false;
698
                        if (symbol instanceof CartographicSupport) {
699
                                bDrawCartographicSupport =
700
                                                ((CartographicSupport) symbol).getUnit() != -1;
701
                        }
702

    
703
                        double previousSize = 0.0d;
704

    
705
                        if (bDrawCartographicSupport) {
706
                                // make the symbol to resize itself with the current rendering
707
                                // context
708
                                previousSize =
709
                                                ((CartographicSupport) symbol).toCartographicSize(
710
                                                                viewPort, dpi, geom);
711
                        }
712

    
713
                        try {
714
                                // draw it as normally
715
                                double[] dot = new double[2];
716
                                boolean onePoint =
717
                                                symbol.isOneDotOrPixel(geom, dot, viewPort, (int) dpi);
718

    
719
                                if (onePoint) {
720
                                        if (dot[0] < 0 || dot[1] < 0 || dot[0] >= image.getWidth()
721
                                                        || dot[1] >= image.getHeight()) {
722
                                                return;
723
                                        }
724
                                        // FIXME: setRgb should change to draw
725
                                        image.setRGB((int) dot[0], (int) dot[1],
726
                                                        symbol.getOnePointRgb());
727
                                } else {
728
                                        Geometry decimatedShape =
729
                                                        transformToInts(geom, viewPort.getAffineTransform());
730
                                        symbol.draw(graphics, viewPort.getAffineTransform(),
731
                                                        decimatedShape, feature, cancellable);
732
                                }
733

    
734
                        } finally {
735
                                if (bDrawCartographicSupport) {
736
                                        // restore previous size
737
                                        ((CartographicSupport) symbol).setCartographicSize(
738
                                                        previousSize, geom);
739
                                }
740
                        }
741
                }
742
        }
743

    
744
        /**
745
         * Draws an {@link Aggregate} using the given {@link ISymbol}, over the
746
         * {@link Graphics2D} object.
747
         */
748
        private void drawAggregate(Aggregate aggregate, BufferedImage image,
749
                        Feature feature, ISymbol symbol, ViewPort viewPort,
750
                        Graphics2D graphics, double dpi, Cancellable cancellable)
751
                        throws CreateGeometryException {
752
                for (int i = 0; i < aggregate.getPrimitivesNumber(); i++) {
753
                        Geometry prim = aggregate.getPrimitiveAt(i);
754
                        drawGeometry(prim, image, feature, symbol, viewPort, graphics, dpi,
755
                                        cancellable);
756
                }
757
        }
758

    
759
        /**
760
         * Transforms a Geometry from map coordinates to the view coordinates.
761
         */
762
        private Geometry transformToInts(Geometry gp, AffineTransform at)
763
                        throws CreateGeometryException {
764
                GeneralPathX newGp = new GeneralPathX();
765
                double[] theData = new double[6];
766
                double[] aux = new double[6];
767

    
768
                // newGp.reset();
769
                PathIterator theIterator;
770
                int theType;
771
                int numParts = 0;
772

    
773
                java.awt.geom.Point2D ptDst = new java.awt.geom.Point2D.Double();
774
                java.awt.geom.Point2D ptSrc = new java.awt.geom.Point2D.Double();
775
                boolean bFirst = true;
776
                int xInt, yInt, antX = -1, antY = -1;
777

    
778
                theIterator = gp.getPathIterator(null); // , flatness);
779
                int numSegmentsAdded = 0;
780
                while (!theIterator.isDone()) {
781
                        theType = theIterator.currentSegment(theData);
782

    
783
                        switch (theType) {
784
                        case PathIterator.SEG_MOVETO:
785
                                numParts++;
786
                                ptSrc.setLocation(theData[0], theData[1]);
787
                                at.transform(ptSrc, ptDst);
788
                                antX = (int) ptDst.getX();
789
                                antY = (int) ptDst.getY();
790
                                newGp.moveTo(antX, antY);
791
                                numSegmentsAdded++;
792
                                bFirst = true;
793
                                break;
794

    
795
                        case PathIterator.SEG_LINETO:
796
                                ptSrc.setLocation(theData[0], theData[1]);
797
                                at.transform(ptSrc, ptDst);
798
                                xInt = (int) ptDst.getX();
799
                                yInt = (int) ptDst.getY();
800
                                if ((bFirst) || ((xInt != antX) || (yInt != antY))) {
801
                                        newGp.lineTo(xInt, yInt);
802
                                        antX = xInt;
803
                                        antY = yInt;
804
                                        bFirst = false;
805
                                        numSegmentsAdded++;
806
                                }
807
                                break;
808

    
809
                        case PathIterator.SEG_QUADTO:
810
                                at.transform(theData, 0, aux, 0, 2);
811
                                newGp.quadTo(aux[0], aux[1], aux[2], aux[3]);
812
                                numSegmentsAdded++;
813
                                break;
814

    
815
                        case PathIterator.SEG_CUBICTO:
816
                                at.transform(theData, 0, aux, 0, 3);
817
                                newGp.curveTo(aux[0], aux[1], aux[2], aux[3], aux[4], aux[5]);
818
                                numSegmentsAdded++;
819
                                break;
820

    
821
                        case PathIterator.SEG_CLOSE:
822
                                if (numSegmentsAdded < 3) {
823
                                        newGp.lineTo(antX, antY);
824
                                }
825
                                newGp.closePath();
826

    
827
                                break;
828
                        } // end switch
829

    
830
                        theIterator.next();
831
                } // end while loop
832

    
833
                Geometry geom = null;
834
                switch (gp.getType()) {
835
                case Geometry.TYPES.POINT:
836
                        geom =
837
                                        geomManager.createPoint(ptDst.getX(), ptDst.getY(),
838
                                                        SUBTYPES.GEOM2D);
839
                        break;
840
                case Geometry.TYPES.CURVE:
841
                case Geometry.TYPES.ARC:
842
                        geom = geomManager.createCurve(newGp, SUBTYPES.GEOM2D);
843
                        break;
844

    
845
                case Geometry.TYPES.SURFACE:
846
                case Geometry.TYPES.CIRCLE:
847
                case Geometry.TYPES.ELLIPSE:
848

    
849
                        geom = geomManager.createSurface(newGp, SUBTYPES.GEOM2D);
850
                        break;
851
                }
852
                return geom;
853
        }
854

    
855
        public Object clone() throws CloneNotSupportedException {
856
                AbstractVectorialLegend clone = (AbstractVectorialLegend) super.clone();
857

    
858
                // Clone zSort
859
                ZSort zSort = getZSort();
860
                if (zSort != null) {
861
                        clone.setZSort(new ZSort(clone));
862
                }
863
                return clone;
864
        }
865

    
866
        public void loadFromState(PersistentState state)
867
                        throws PersistenceException {
868
                // Set parent properties
869
                super.loadFromState(state);
870
                // Set own properties
871
                setShapeType(state.getInt(FIELD_SHAPETYPE));
872
                if (state.getBoolean(FIELD_HAS_ZSORT)) {
873
                        setZSort(new ZSort(this));
874
                }
875
                setDefaultSymbol((ISymbol) state.get(FIELD_DEFAULT_SYMBOL));
876
        }
877

    
878
        public void saveToState(PersistentState state) throws PersistenceException {
879
                // Save parent properties
880
                super.saveToState(state);
881
                // Save own properties
882
                state.set(FIELD_SHAPETYPE, getShapeType());
883
                if (getZSort() != null) {
884
                        state.set(FIELD_HAS_ZSORT, true);
885
                }
886
                if (getDefaultSymbol() != null) {
887
                        state.set(FIELD_DEFAULT_SYMBOL, getDefaultSymbol());
888
                }
889
        }
890

    
891
        /**
892
         * Registers the legend dynClass definition.
893
         */
894
        public static void registerPersistence() {
895
                // Add the DynClass definition.
896
                DynClass dynClass =
897
                                ToolsLocator.getDynObjectManager().add(
898
                                                VECTORIAL_LEGEND_DYNCLASS_NAME);
899

    
900
                // Extend the Legend base definition
901
                dynClass.extend(LEGEND_DYNCLASS_NAME);
902

    
903
                // Shapetype
904
                dynClass.addDynFieldInt(FIELD_SHAPETYPE);
905
                // ZSort
906
                dynClass.addDynFieldBoolean(FIELD_HAS_ZSORT);
907
                // Default symbol
908
                dynClass.addDynFieldObject(FIELD_DEFAULT_SYMBOL);
909
        }
910

    
911
        /**
912
         * Returns the names of the {@link Feature} attributes required for the
913
         * {@link ILegend} to operate.
914
         * 
915
         * @param featureStore
916
         *            the store where the {@link Feature}s belong to
917
         * @return the names of required {@link Feature} attribute names
918
         * @throws DataException
919
         *             if there is an error getting the attribute names
920
         */
921
        protected abstract String[] getRequiredFeatureAttributeNames(
922
                        FeatureStore featureStore) throws DataException;
923
}