Statistics
| Revision:

root / branches / v2_0_0_prep / libraries / org.gvsig.symbology / org.gvsig.symbology.lib / org.gvsig.symbology.lib.impl / src / main / java / org / gvsig / symbology / fmap / mapcontext / rendering / legend / impl / AbstractVectorialLegend.java @ 34294

History | View | Annotate | Download (28.6 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.util.Callable;
83
import org.gvsig.tools.visitor.VisitCanceledException;
84
import org.gvsig.tools.visitor.Visitor;
85

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

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

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

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

    
107
        protected ZSort zSort;
108

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

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

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

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

    
138
                int resolution = properties.getPrintQuality();
139

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

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

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

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

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

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

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

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

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

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

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

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

    
223
                                                csSym = (CartographicSupport) sym;
224
                                        }
225

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

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

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

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

    
277
                IProjection dataProjection;
278
                Envelope reprojectedDataEnvelop;
279

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

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

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

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

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

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

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

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

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

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

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

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

    
336
                        featureSet = featureStore.getFeatureSet(featureQuery);
337

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

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

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

    
353
        }
354

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

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

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

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

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

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

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

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

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

    
460
                Geometry geom = feat.getDefaultGeometry();
461

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
564
                imageLevels = null;
565
                graphics = null;
566

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
716
                        double previousSize = 0.0d;
717

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
840
                                break;
841
                        } // end switch
842

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

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

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

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

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

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

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

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

    
901
        public static class RegisterPersistence implements Callable {
902

    
903
                public Object call() throws Exception {
904
                        PersistenceManager manager = ToolsLocator.getPersistenceManager();
905
                        if( manager.getDefinition(VECTORIAL_LEGEND_PERSISTENCE_DEFINITION_NAME)==null ) {
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(manager.getDefinition(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
                        return Boolean.TRUE;
928
                }
929
                
930
        }
931
        
932
        
933
        /**
934
         * Returns the names of the {@link Feature} attributes required for the
935
         * {@link ILegend} to operate.
936
         * 
937
         * @param featureStore
938
         *            the store where the {@link Feature}s belong to
939
         * @return the names of required {@link Feature} attribute names
940
         * @throws DataException
941
         *             if there is an error getting the attribute names
942
         */
943
        protected abstract String[] getRequiredFeatureAttributeNames(
944
                        FeatureStore featureStore) throws DataException;
945
}