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

History | View | Annotate | Download (29 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

    
41
import org.gvsig.compat.CompatLocator;
42
import org.gvsig.compat.print.PrintAttributes;
43
import org.gvsig.fmap.dal.exception.DataException;
44
import org.gvsig.fmap.dal.exception.ReadException;
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.Geometry.SUBTYPES;
53
import org.gvsig.fmap.geom.GeometryLocator;
54
import org.gvsig.fmap.geom.GeometryManager;
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.dispose.DisposableIterator;
77
import org.gvsig.tools.dynobject.DynStruct;
78
import org.gvsig.tools.exception.BaseException;
79
import org.gvsig.tools.persistence.PersistenceManager;
80
import org.gvsig.tools.persistence.PersistentState;
81
import org.gvsig.tools.persistence.exception.PersistenceException;
82
import org.gvsig.tools.task.Cancellable;
83
import org.gvsig.tools.task.SimpleTaskStatus;
84
import org.gvsig.tools.util.Callable;
85
import org.gvsig.tools.visitor.VisitCanceledException;
86
import org.gvsig.tools.visitor.Visitor;
87

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

    
99
        public static final String VECTORIAL_LEGEND_PERSISTENCE_DEFINITION_NAME = "VectorialLegend";
100

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

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

    
108
        protected ZSort zSort;
109

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

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

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

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

    
139
                int resolution = properties.getPrintQuality();
140

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

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

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

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

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

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

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

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

    
190
                                                        // Check if this symbol is a multilayer
191
                                                        if (sym instanceof IMultiLayerSymbol) {
192
                                                                // if so, get the layer corresponding to the
193
                                                                // current level. If none, continue to next
194
                                                                // iteration
195
                                                                IMultiLayerSymbol mlSym = (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
                SimpleTaskStatus taskStatus = null;
268
                try {
269
                        taskStatus = ToolsLocator.getTaskStatusManager()
270
                                        .creteDefaultSimpleTaskStatus(featureStore.getName());
271
                        taskStatus.add();
272
                        if (!getDefaultSymbol().isShapeVisible()) {
273
                                return;
274
                        }
275

    
276
                        if (cancel.isCanceled()) {
277
                                return;
278
                        }
279

    
280
                        IProjection dataProjection;
281
                        Envelope reprojectedDataEnvelop;
282

    
283
                        try {
284
                                if (coordTrans == null) {
285
                                        dataProjection = featureStore.getDefaultFeatureType()
286
                                                        .getDefaultSRS();
287

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

    
295
                                        reprojectedDataEnvelop = featureStore.getEnvelope();
296
                                } else {
297
                                        dataProjection = coordTrans.getPOrig();
298

    
299
                                        reprojectedDataEnvelop = featureStore.getEnvelope()
300
                                                        .convert(coordTrans);
301
                                }
302
                        } catch (DataException e) {
303
                                throw new LegendDrawingException(e);
304
                        }
305

    
306
                        // Gets the view envelope
307
                        Envelope viewPortEnvelope = viewPort.getAdjustedEnvelope();
308

    
309
                        // Gets the data envelope with the viewport SRS
310
                        Envelope myEnvelope = reprojectedDataEnvelop;
311

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

    
320
                        // Check if all the data is contained into the viewport envelope
321
                        boolean containsAll = viewPortEnvelope.contains(myEnvelope);
322

    
323
                        // Create the drawing notification to be reused on each iteration
324
                        DefaultFeatureDrawnNotification drawnNotification = new DefaultFeatureDrawnNotification();
325

    
326
                        if (cancel.isCanceled()) {
327
                                return;
328
                        }
329

    
330
                        FeatureSet featureSet = null;
331
                        try {
332
                                taskStatus.message("Retrieve selection");
333
                                FeatureSelection selection = featureStore.getFeatureSelection();
334

    
335
                                FeatureQuery featureQuery = createQuery(featureStore, scale,
336
                                                queryParameters, coordTrans, dataProjection,
337
                                                viewPortEnvelope, containsAll);
338

    
339
                                taskStatus.message("Retrieve data");
340
                                featureSet = featureStore.getFeatureSet(featureQuery);
341

    
342
                                if (cancel.isCanceled()) {
343
                                        return;
344
                                }
345

    
346
                                taskStatus.message("Drawing");
347
                                drawFeatures(image, g, viewPort, cancel, coordTrans, dpi,
348
                                                drawnNotification, featureSet, selection);
349

    
350
                        } catch (BaseException e) {
351
                                throw new LegendDrawingException(e);
352
                        } finally {
353
                                if (featureSet != null) {
354
                                        featureSet.dispose();
355
                                }
356
                        }
357
                } finally {
358
                        if (taskStatus != null) {
359
                                taskStatus.terminate();
360
                                taskStatus.remove();
361
                                taskStatus = null;
362
                        }
363
                }
364

    
365
        }
366

    
367
        /**
368
         * Creates a {@link FeatureQuery} to load the {@link Feature}s to draw.
369
         */
370
        @SuppressWarnings("unchecked")
371
        private FeatureQuery createQuery(FeatureStore featureStore, double scale,
372
                        Map queryParameters, ICoordTrans coordTrans,
373
                        IProjection dataProjection, Envelope viewPortEnvelope,
374
                        boolean containsAll) throws DataException {
375

    
376
                FeatureQuery featureQuery = featureStore.createFeatureQuery();
377
                featureQuery.setScale(scale);
378
                String[] fieldNames = getRequiredFeatureAttributeNames(featureStore);
379
                featureQuery.setAttributeNames(fieldNames);
380
                // TODO: Mobile has it's own IntersectsEnvelopeEvaluator
381
                if (!containsAll) {
382
                        // Gets the viewport envelope with the data SRS
383
                        Envelope viewPortEnvelopeInMyProj = viewPortEnvelope;
384
                        // FIXME
385
                        if (coordTrans != null) {
386
                                viewPortEnvelopeInMyProj = viewPortEnvelope.convert(coordTrans
387
                                                .getInverted());
388
                        }
389

    
390
                        if (dataProjection == null) {
391
                                throw new IllegalArgumentException(
392
                                                "Error, the projection parameter value is null");
393
                        }
394

    
395
                        IntersectsEnvelopeEvaluator iee = new IntersectsEnvelopeEvaluator(
396
                                        viewPortEnvelopeInMyProj, dataProjection,
397
                                        featureStore.getDefaultFeatureType(), featureStore
398
                                                        .getDefaultFeatureType()
399
                                                        .getDefaultGeometryAttributeName());
400
                        featureQuery.setFilter(iee);
401
                }
402
                if (queryParameters != null) {
403
                        Iterator iterEntry = queryParameters.entrySet().iterator();
404
                        Entry entry;
405
                        while (iterEntry.hasNext()) {
406
                                entry = (Entry) iterEntry.next();
407
                                featureQuery.setQueryParameter((String) entry.getKey(),
408
                                                entry.getValue());
409
                        }
410
                }
411
                return featureQuery;
412
        }
413

    
414
        /**
415
         * Draws the features from the {@link FeatureSet}, with the symbols of the
416
         * legend.
417
         */
418
        private void drawFeatures(BufferedImage image, Graphics2D g,
419
                        ViewPort viewPort, Cancellable cancel, ICoordTrans coordTrans,
420
                        double dpi, DefaultFeatureDrawnNotification drawnNotification,
421
                        FeatureSet featureSet, FeatureSelection selection)
422
                        throws BaseException {
423
                if (isUseZSort()) {
424
                        drawFeaturesMultiLayer(image, g, viewPort, cancel, coordTrans, dpi,
425
                                        drawnNotification, featureSet, selection);
426
                } else {
427
                        drawFeaturesSingleLayer(image, g, viewPort, cancel, coordTrans,
428
                                        dpi, drawnNotification, featureSet, selection);
429
                }
430
        }
431

    
432
        /**
433
         * Draws the features from the {@link FeatureSet}, with the symbols of the
434
         * legend, using a single drawing layer.
435
         */
436
        private void drawFeaturesSingleLayer(final BufferedImage image,
437
                        final Graphics2D g, final ViewPort viewPort,
438
                        final Cancellable cancel, final ICoordTrans coordTrans,
439
                        final double dpi,
440
                        final DefaultFeatureDrawnNotification drawnNotification,
441
                        FeatureSet featureSet, final FeatureSelection selection)
442
                        throws BaseException {
443

    
444
                try {
445
                        featureSet.accept(new Visitor() {
446
                                public void visit(Object obj) throws VisitCanceledException,
447
                                                BaseException {
448
                                        Feature feat = (Feature) obj;
449
                                        drawFeatureSingleLayer(image, g, viewPort, cancel,
450
                                                        coordTrans, dpi, drawnNotification, feat, selection);
451
                                }
452
                        });
453

    
454
                } catch (ConcurrentDataModificationException e) {
455
                        cancel.setCanceled(true);
456
                        return;
457
                }
458
        }
459

    
460
        /**
461
         * Draws a Feature with the symbols of the legend, using a single drawing
462
         * layer.
463
         */
464
        private void drawFeatureSingleLayer(BufferedImage image, Graphics2D g,
465
                        ViewPort viewPort, Cancellable cancel, ICoordTrans coordTrans,
466
                        double dpi, DefaultFeatureDrawnNotification drawnNotification,
467
                        Feature feat, FeatureSelection selection)
468
                        throws MapContextException, CreateGeometryException {
469

    
470
                Geometry geom = feat.getDefaultGeometry();
471
                if (geom == null) {
472
                        return;
473
                }
474

    
475
                if (geom.getType() == Geometry.TYPES.NULL) {
476
                        return;
477
                }
478

    
479
                ISymbol sym = getSymbol(feat, selection);
480
                if (sym == null) {
481
                        return;
482
                }
483

    
484
                if (coordTrans != null) {
485
                        geom = geom.cloneGeometry();
486
                        geom.reProject(coordTrans);
487
                }
488

    
489
                if (cancel.isCanceled()) {
490
                        return;
491
                }
492

    
493
                drawGeometry(geom, image, feat, sym, viewPort, g, dpi, cancel);
494

    
495
                // Notify the drawing observers
496
                drawnNotification.setFeature(feat);
497
                drawnNotification.setDrawnGeometry(geom);
498
                notifyObservers(drawnNotification);
499
        }
500

    
501
        /**
502
         * Draws the features from the {@link FeatureSet}, with the symbols of the
503
         * legend, using a multiple drawing layer.
504
         */
505
        private void drawFeaturesMultiLayer(BufferedImage image, Graphics2D g,
506
                        ViewPort viewPort, Cancellable cancel, ICoordTrans coordTrans,
507
                        double dpi, DefaultFeatureDrawnNotification drawnNotification,
508
                        FeatureSet featureSet, FeatureSelection selection)
509
                        throws MapContextException, CreateGeometryException, DataException {
510

    
511
                // -- visual FX stuff
512
                long time = System.currentTimeMillis();
513

    
514
                boolean bSymbolLevelError = false;
515
                // render temporary map each screenRefreshRate milliseconds;
516
                int screenRefreshDelay = (int) ((1D / MapContext.getDrawFrameRate()) * 3 * 1000);
517
                BufferedImage[] imageLevels = null;
518
                Graphics2D[] graphics = null;
519

    
520
                imageLevels = new BufferedImage[getZSort().getLevelCount()];
521
                graphics = new Graphics2D[imageLevels.length];
522
                for (int i = 0; !cancel.isCanceled() && i < imageLevels.length; i++) {
523

    
524
                        imageLevels[i] = CompatLocator.getGraphicsUtils()
525
                                        .createBufferedImage(image.getWidth(), image.getHeight(),
526
                                                        image.getType());
527

    
528
                        graphics[i] = imageLevels[i].createGraphics();
529
                        graphics[i].setTransform(g.getTransform());
530
                        graphics[i].setRenderingHints(g.getRenderingHints());
531
                }
532
                // -- end visual FX stuff
533

    
534
                DisposableIterator it = null;
535
                try {
536
                        it = featureSet.fastIterator();
537
                        // Iteration over each feature
538
                        while (it.hasNext()) {
539
                                if (cancel.isCanceled()) {
540
                                        return;
541
                                }
542
                                Feature feat = (Feature) it.next();
543

    
544
                                bSymbolLevelError |= drawFeatureMultiLayer(image, g, viewPort,
545
                                                cancel, coordTrans, dpi, drawnNotification, selection,
546
                                                time, screenRefreshDelay, imageLevels, graphics, feat);
547

    
548
                        }
549
                } catch (ConcurrentDataModificationException e) {
550
                        cancel.setCanceled(true);
551
                        return;
552
                } finally {
553
                        if (it != null) {
554
                                it.dispose();
555
                        }
556
                }
557

    
558
                g.drawImage(image, 0, 0, null);
559

    
560
                Point2D offset = viewPort.getOffset();
561
                CompatLocator.getGraphicsUtils().translate(g, offset.getX(),
562
                                offset.getY());
563

    
564
                for (int i = 0; !cancel.isCanceled() && i < imageLevels.length; i++) {
565
                        g.drawImage(imageLevels[i], 0, 0, null);
566
                        imageLevels[i] = null;
567
                        graphics[i] = null;
568
                }
569

    
570
                CompatLocator.getGraphicsUtils().translate(g, -offset.getX(),
571
                                -offset.getY());
572

    
573
                imageLevels = null;
574
                graphics = null;
575

    
576
                if (bSymbolLevelError) {
577
                        setZSort(null);
578
                }
579
        }
580

    
581
        /**
582
         * Draws a Feature with the symbols of the legend, using a multiple drawing
583
         * layer.
584
         */
585
        private boolean drawFeatureMultiLayer(BufferedImage image, Graphics2D g,
586
                        ViewPort viewPort, Cancellable cancel, ICoordTrans coordTrans,
587
                        double dpi, DefaultFeatureDrawnNotification drawnNotification,
588
                        FeatureSelection selection, long time, int screenRefreshDelay,
589
                        BufferedImage[] imageLevels, Graphics2D[] graphics, Feature feat)
590
                        throws MapContextException, CreateGeometryException {
591

    
592
                Geometry geom = feat.getDefaultGeometry();
593
                boolean bSymbolLevelError = false;
594
                long drawingTime = time;
595

    
596
                if (geom.getType() == Geometry.TYPES.NULL) {
597
                        return false;
598
                }
599

    
600
                ISymbol sym = getSymbol(feat, selection);
601

    
602
                if (sym == null) {
603
                        return false;
604
                }
605

    
606
                if (coordTrans != null) {
607
                        geom = geom.cloneGeometry();
608
                        geom.reProject(coordTrans);
609
                }
610

    
611
                if (cancel.isCanceled()) {
612
                        return false;
613
                }
614

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

    
651
                // -- visual FX stuff
652
                // Cuando el offset!=0 se est? dibujando sobre el
653
                // Layout y por tanto no tiene que ejecutar el
654
                // siguiente c?digo.
655
                Point2D offset = viewPort.getOffset();
656
                if (offset.getX() == 0 && offset.getY() == 0) {
657
                        if ((System.currentTimeMillis() - drawingTime) > screenRefreshDelay) {
658

    
659
                                BufferedImage virtualBim = CompatLocator.getGraphicsUtils()
660
                                                .createBufferedImage(image.getWidth(),
661
                                                                image.getHeight(), BufferedImage.TYPE_INT_ARGB);
662

    
663
                                Graphics2D virtualGraphics = virtualBim.createGraphics();
664
                                virtualGraphics.drawImage(image, 0, 0, null);
665
                                for (int i = 0; !cancel.isCanceled() && i < imageLevels.length; i++) {
666
                                        virtualGraphics.drawImage(imageLevels[i], 0, 0, null);
667
                                }
668
                                g.clearRect(0, 0, image.getWidth(), image.getHeight());
669
                                g.drawImage(virtualBim, 0, 0, null);
670
                                drawingTime = System.currentTimeMillis();
671
                        }
672
                        // -- end visual FX stuff
673

    
674
                }
675
                // Notify the drawing observers
676
                drawnNotification.setFeature(feat);
677
                drawnNotification.setDrawnGeometry(geom);
678
                notifyObservers(drawnNotification);
679
                return bSymbolLevelError;
680
        }
681

    
682
        /**
683
         * Returns the symbol to use to draw a {@link Feature} taking into account
684
         * if it is selected.
685
         */
686
        private ISymbol getSymbol(Feature feat, FeatureSelection selection)
687
                        throws MapContextException {
688
                // retrieve the symbol associated to such feature
689
                ISymbol sym = getSymbolByFeature(feat);
690

    
691
                if (sym != null && selection.isSelected(feat)) {
692
                        sym = sym.getSymbolForSelection();
693
                }
694
                return sym;
695
        }
696

    
697
        /**
698
         * Returns if the legend is using a ZSort.
699
         */
700
        private boolean isUseZSort() {
701
                return getZSort() != null && getZSort().isUsingZSort();
702
        }
703

    
704
        /**
705
         * Draws a {@link Geometry} using the given {@link ISymbol}, over the
706
         * {@link Graphics2D} object.
707
         */
708
        private void drawGeometry(Geometry geom, BufferedImage image,
709
                        Feature feature, ISymbol symbol, ViewPort viewPort,
710
                        Graphics2D graphics, double dpi, Cancellable cancellable)
711
                        throws CreateGeometryException {
712

    
713
                if (geom instanceof Aggregate) {
714
                        drawAggregate((Aggregate) geom, image, feature, symbol, viewPort,
715
                                        graphics, dpi, cancellable);
716
                } else {
717
                        boolean bDrawCartographicSupport = false;
718
                        if (symbol instanceof CartographicSupport) {
719
                                bDrawCartographicSupport = ((CartographicSupport) symbol)
720
                                                .getUnit() != -1;
721
                        }
722

    
723
                        double previousSize = 0.0d;
724

    
725
                        if (bDrawCartographicSupport) {
726
                                // make the symbol to resize itself with the current rendering
727
                                // context
728
                                previousSize = ((CartographicSupport) symbol)
729
                                                .toCartographicSize(viewPort, dpi, geom);
730
                        }
731

    
732
                        try {
733
                                // draw it as normally
734
                                double[] dot = new double[2];
735
                                boolean onePoint = symbol.isOneDotOrPixel(geom, dot, viewPort,
736
                                                (int) dpi);
737

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

    
753
                        } finally {
754
                                if (bDrawCartographicSupport) {
755
                                        // restore previous size
756
                                        ((CartographicSupport) symbol).setCartographicSize(
757
                                                        previousSize, geom);
758
                                }
759
                        }
760
                }
761
        }
762

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

    
778
        /**
779
         * Transforms a Geometry from map coordinates to the view coordinates.
780
         */
781
        private Geometry transformToInts(Geometry gp, AffineTransform at)
782
                        throws CreateGeometryException {
783
                GeneralPathX newGp = new GeneralPathX();
784
                double[] theData = new double[6];
785
                double[] aux = new double[6];
786

    
787
                // newGp.reset();
788
                PathIterator theIterator;
789
                int theType;
790
                int numParts = 0;
791

    
792
                java.awt.geom.Point2D ptDst = new java.awt.geom.Point2D.Double();
793
                java.awt.geom.Point2D ptSrc = new java.awt.geom.Point2D.Double();
794
                boolean bFirst = true;
795
                int xInt, yInt, antX = -1, antY = -1;
796

    
797
                theIterator = gp.getPathIterator(null); // , flatness);
798
                int numSegmentsAdded = 0;
799
                while (!theIterator.isDone()) {
800
                        theType = theIterator.currentSegment(theData);
801

    
802
                        switch (theType) {
803
                        case PathIterator.SEG_MOVETO:
804
                                numParts++;
805
                                ptSrc.setLocation(theData[0], theData[1]);
806
                                at.transform(ptSrc, ptDst);
807
                                antX = (int) ptDst.getX();
808
                                antY = (int) ptDst.getY();
809
                                newGp.moveTo(antX, antY);
810
                                numSegmentsAdded++;
811
                                bFirst = true;
812
                                break;
813

    
814
                        case PathIterator.SEG_LINETO:
815
                                ptSrc.setLocation(theData[0], theData[1]);
816
                                at.transform(ptSrc, ptDst);
817
                                xInt = (int) ptDst.getX();
818
                                yInt = (int) ptDst.getY();
819
                                if ((bFirst) || ((xInt != antX) || (yInt != antY))) {
820
                                        newGp.lineTo(xInt, yInt);
821
                                        antX = xInt;
822
                                        antY = yInt;
823
                                        bFirst = false;
824
                                        numSegmentsAdded++;
825
                                }
826
                                break;
827

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

    
834
                        case PathIterator.SEG_CUBICTO:
835
                                at.transform(theData, 0, aux, 0, 3);
836
                                newGp.curveTo(aux[0], aux[1], aux[2], aux[3], aux[4], aux[5]);
837
                                numSegmentsAdded++;
838
                                break;
839

    
840
                        case PathIterator.SEG_CLOSE:
841
                                if (numSegmentsAdded < 3) {
842
                                        newGp.lineTo(antX, antY);
843
                                }
844
                                newGp.closePath();
845

    
846
                                break;
847
                        } // end switch
848

    
849
                        theIterator.next();
850
                } // end while loop
851

    
852
                Geometry geom = null;
853
                switch (gp.getType()) {
854
                case Geometry.TYPES.POINT:
855
                        geom = geomManager.createPoint(ptDst.getX(), ptDst.getY(),
856
                                        SUBTYPES.GEOM2D);
857
                        break;
858
                case Geometry.TYPES.CURVE:
859
                case Geometry.TYPES.ARC:
860
                        geom = geomManager.createCurve(newGp, SUBTYPES.GEOM2D);
861
                        break;
862

    
863
                case Geometry.TYPES.SURFACE:
864
                case Geometry.TYPES.CIRCLE:
865
                case Geometry.TYPES.ELLIPSE:
866

    
867
                        geom = geomManager.createSurface(newGp, SUBTYPES.GEOM2D);
868
                        break;
869
                }
870
                return geom;
871
        }
872

    
873
        public Object clone() throws CloneNotSupportedException {
874
                AbstractVectorialLegend clone = (AbstractVectorialLegend) super.clone();
875

    
876
                // Clone zSort
877
                ZSort zSort = getZSort();
878
                if (zSort != null) {
879
                        clone.setZSort(new ZSort(clone));
880
                }
881
                return clone;
882
        }
883

    
884
        public void loadFromState(PersistentState state)
885
                        throws PersistenceException {
886
                // Set parent properties
887
                super.loadFromState(state);
888
                // Set own properties
889

    
890
                setShapeType(state.getInt(FIELD_SHAPETYPE));
891
                if (state.getBoolean(FIELD_HAS_ZSORT)) {
892
                        setZSort(new ZSort(this));
893
                }
894
                setDefaultSymbol((ISymbol) state.get(FIELD_DEFAULT_SYMBOL));
895
        }
896

    
897
        public void saveToState(PersistentState state) throws PersistenceException {
898
                // Save parent properties
899
                super.saveToState(state);
900
                // Save own properties
901
                state.set(FIELD_SHAPETYPE, getShapeType());
902
                state.set(FIELD_HAS_ZSORT, this.zSort != null);
903
                state.set(FIELD_DEFAULT_SYMBOL, getDefaultSymbol());
904
        }
905

    
906
        public static class RegisterPersistence implements Callable {
907

    
908
                public Object call() throws Exception {
909
                        PersistenceManager manager = ToolsLocator.getPersistenceManager();
910
                        if (manager
911
                                        .getDefinition(VECTORIAL_LEGEND_PERSISTENCE_DEFINITION_NAME) == null) {
912
                                DynStruct definition = manager.addDefinition(
913
                                                AbstractVectorialLegend.class,
914
                                                VECTORIAL_LEGEND_PERSISTENCE_DEFINITION_NAME,
915
                                                VECTORIAL_LEGEND_PERSISTENCE_DEFINITION_NAME
916
                                                                + " persistence definition", null, null);
917
                                // Extend the Legend base definition
918
                                definition.extend(manager
919
                                                .getDefinition(LEGEND_PERSISTENCE_DEFINITION_NAME));
920

    
921
                                // Shapetype
922
                                definition.addDynFieldInt(FIELD_SHAPETYPE).setMandatory(true);
923
                                // ZSort
924
                                definition.addDynFieldBoolean(FIELD_HAS_ZSORT).setMandatory(
925
                                                true);
926
                                // Default symbol
927
                                definition.addDynFieldObject(FIELD_DEFAULT_SYMBOL)
928
                                                .setClassOfValue(ISymbol.class).setMandatory(true);
929
                        }
930
                        return Boolean.TRUE;
931
                }
932

    
933
        }
934

    
935
        /**
936
         * Returns the names of the {@link Feature} attributes required for the
937
         * {@link ILegend} to operate.
938
         * 
939
         * @param featureStore
940
         *            the store where the {@link Feature}s belong to
941
         * @return the names of required {@link Feature} attribute names
942
         * @throws DataException
943
         *             if there is an error getting the attribute names
944
         */
945
        protected abstract String[] getRequiredFeatureAttributeNames(
946
                        FeatureStore featureStore) throws DataException;
947
}