Statistics
| Revision:

root / tags / v2_0_0_Build_2047 / libraries / org.gvsig.symbology / 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 @ 38365

History | View | Annotate | Download (33.2 KB)

1 34294 fdiaz
/* 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.Point2D;
31
import java.awt.image.BufferedImage;
32 38207 cordinyana
import java.util.ConcurrentModificationException;
33 34294 fdiaz
import java.util.Iterator;
34
import java.util.Map;
35
import java.util.Map.Entry;
36
37
import org.cresques.cts.ICoordTrans;
38
import org.cresques.cts.IProjection;
39
import org.gvsig.compat.CompatLocator;
40
import org.gvsig.compat.print.PrintAttributes;
41
import org.gvsig.fmap.dal.exception.DataException;
42
import org.gvsig.fmap.dal.exception.ReadException;
43
import org.gvsig.fmap.dal.feature.Feature;
44
import org.gvsig.fmap.dal.feature.FeatureQuery;
45
import org.gvsig.fmap.dal.feature.FeatureSelection;
46
import org.gvsig.fmap.dal.feature.FeatureSet;
47
import org.gvsig.fmap.dal.feature.FeatureStore;
48
import org.gvsig.fmap.dal.feature.exception.ConcurrentDataModificationException;
49
import org.gvsig.fmap.geom.Geometry;
50
import org.gvsig.fmap.geom.GeometryLocator;
51
import org.gvsig.fmap.geom.GeometryManager;
52
import org.gvsig.fmap.geom.aggregate.Aggregate;
53
import org.gvsig.fmap.geom.exception.CreateGeometryException;
54
import org.gvsig.fmap.geom.operation.DrawInts;
55
import org.gvsig.fmap.geom.operation.DrawOperationContext;
56
import org.gvsig.fmap.geom.operation.GeometryOperationException;
57
import org.gvsig.fmap.geom.operation.GeometryOperationNotSupportedException;
58
import org.gvsig.fmap.geom.primitive.Envelope;
59
import org.gvsig.fmap.mapcontext.MapContext;
60
import org.gvsig.fmap.mapcontext.MapContextException;
61
import org.gvsig.fmap.mapcontext.ViewPort;
62
import org.gvsig.fmap.mapcontext.layers.vectorial.IntersectsEnvelopeEvaluator;
63
import org.gvsig.fmap.mapcontext.rendering.legend.ILegend;
64
import org.gvsig.fmap.mapcontext.rendering.legend.IVectorLegend;
65
import org.gvsig.fmap.mapcontext.rendering.legend.LegendException;
66
import org.gvsig.fmap.mapcontext.rendering.legend.ZSort;
67
import org.gvsig.fmap.mapcontext.rendering.symbols.CartographicSupport;
68
import org.gvsig.fmap.mapcontext.rendering.symbols.IMultiLayerSymbol;
69
import org.gvsig.fmap.mapcontext.rendering.symbols.ISymbol;
70
import org.gvsig.tools.ToolsLocator;
71
import org.gvsig.tools.dispose.DisposableIterator;
72
import org.gvsig.tools.dynobject.DynStruct;
73
import org.gvsig.tools.exception.BaseException;
74
import org.gvsig.tools.persistence.PersistenceManager;
75
import org.gvsig.tools.persistence.PersistentState;
76
import org.gvsig.tools.persistence.exception.PersistenceException;
77
import org.gvsig.tools.task.Cancellable;
78 34935 jjdelcerro
import org.gvsig.tools.task.SimpleTaskStatus;
79 34294 fdiaz
import org.gvsig.tools.util.Callable;
80
import org.gvsig.tools.visitor.VisitCanceledException;
81
import org.gvsig.tools.visitor.Visitor;
82
83
/**
84
 * Base implementation for Vectorial data Legends.
85
 *
86
 * Provides a draw method implementation which loads the {@link Feature}s and
87
 * uses the {@link ISymbol} objects to draw the {@link Geometry} objects.
88
 *
89
 * @author 2009- <a href="cordinyana@gvsig.org">C?sar Ordi?ana</a> - gvSIG team
90
 */
91
public abstract class AbstractVectorialLegend extends AbstractLegend implements
92 35329 jpiera
IVectorLegend {
93 34294 fdiaz
94 38207 cordinyana
    private static final int DRAW_MAX_ATTEMPTS = 3;
95 34294 fdiaz
96 38207 cordinyana
        public static final String VECTORIAL_LEGEND_PERSISTENCE_DEFINITION_NAME = "VectorialLegend";
97
98 35329 jpiera
    private static final String FIELD_HAS_ZSORT = "hasZSort";
99
    private static final String FIELD_SHAPETYPE = "shapeType";
100
    private static final String FIELD_DEFAULT_SYMBOL = "defaultSymbol";
101 34294 fdiaz
102 35329 jpiera
    private static final GeometryManager geomManager = GeometryLocator
103
    .getGeometryManager();
104 34294 fdiaz
105 35329 jpiera
    protected ZSort zSort;
106 34294 fdiaz
107 35329 jpiera
    public ZSort getZSort() {
108
        return zSort;
109
    }
110 34294 fdiaz
111 35329 jpiera
    public void setZSort(ZSort zSort) {
112
        if (zSort == null) {
113
            removeLegendListener(this.zSort);
114
        }
115
        this.zSort = zSort;
116
        addLegendListener(zSort);
117
    }
118
119
    @SuppressWarnings("unchecked")
120
    public void draw(BufferedImage image, Graphics2D g, ViewPort viewPort,
121
        Cancellable cancel, double scale, Map queryParameters,
122
        ICoordTrans coordTrans, FeatureStore featureStore)
123
    throws LegendException {
124
        double dpi = MapContext.getScreenDPI();
125
        draw(image, g, viewPort, cancel, scale, queryParameters, coordTrans,
126
            featureStore, null, dpi);
127
    }
128
129
    @SuppressWarnings("unchecked")
130
    public void draw(BufferedImage image, Graphics2D g, ViewPort viewPort,
131
        Cancellable cancel, double scale, Map queryParameters,
132
        ICoordTrans coordTrans, FeatureStore featureStore, FeatureQuery featureQuery)
133
    throws LegendException {
134
        double dpi = MapContext.getScreenDPI();
135
        draw(image, g, viewPort, cancel, scale, queryParameters, coordTrans,
136
            featureStore, featureQuery, dpi);
137
    }
138 34294 fdiaz
139 35329 jpiera
    @SuppressWarnings("unchecked")
140
    public void print(Graphics2D g, ViewPort viewPort, Cancellable cancel,
141
        double scale, Map queryParameters, ICoordTrans coordTrans,
142
        FeatureStore featureStore, PrintAttributes properties)
143
    throws LegendException {
144
        print(g, viewPort, cancel, scale, queryParameters, coordTrans,
145
            featureStore, null, properties);
146
    }
147
148
    @SuppressWarnings("unchecked")
149
    public void print(Graphics2D g, ViewPort viewPort, Cancellable cancel,
150
        double scale, Map queryParameters, ICoordTrans coordTrans,
151
        FeatureStore featureStore, FeatureQuery featureQuery, PrintAttributes properties)
152
    throws LegendException {
153
        double dpi = 72;
154 34294 fdiaz
155 35329 jpiera
        int resolution = properties.getPrintQuality();
156 34294 fdiaz
157 35329 jpiera
        if (resolution == PrintAttributes.PRINT_QUALITY_NORMAL) {
158
            dpi = 300;
159
        } else if (resolution == PrintAttributes.PRINT_QUALITY_HIGH) {
160
            dpi = 600;
161
        } else if (resolution == PrintAttributes.PRINT_QUALITY_DRAFT) {
162
            dpi = 72;
163
        }
164 34294 fdiaz
165 35329 jpiera
        FeatureSet featureSet = null;
166
        DisposableIterator it = null;
167
        try {
168
            ZSort zSort = getZSort();
169 34294 fdiaz
170 35329 jpiera
            // if layer has map levels it will use a ZSort
171
            boolean useZSort = zSort != null && zSort.isUsingZSort();
172 34294 fdiaz
173 35329 jpiera
            int mapLevelCount = (useZSort) ? zSort.getLevelCount() : 1;
174
            for (int mapPass = 0; mapPass < mapLevelCount; mapPass++) {
175
                // Get the iterator over the visible features
176
                String[] fieldNames = getRequiredFeatureAttributeNames(featureStore);
177 34294 fdiaz
178 35329 jpiera
                if (featureQuery == null){
179
                    featureQuery = featureStore.createFeatureQuery();
180
                }
181
                featureQuery.setAttributeNames(fieldNames);
182
                featureQuery.setScale(scale);
183 34294 fdiaz
184 37421 jpiera
                IntersectsEnvelopeEvaluator iee = new IntersectsEnvelopeEvaluator(
185 35329 jpiera
                    viewPort.getAdjustedEnvelope(),
186
                    viewPort.getProjection(),
187
                    featureStore.getDefaultFeatureType(), featureStore
188
                    .getDefaultFeatureType()
189
                    .getDefaultGeometryAttributeName());
190
                featureQuery.addFilter(iee);
191
                featureSet = featureStore.getFeatureSet(featureQuery);
192
                it = featureSet.fastIterator();
193 34294 fdiaz
194 35329 jpiera
                // Iteration over each feature
195
                while (!cancel.isCanceled() && it.hasNext()) {
196
                    Feature feat = (Feature) it.next();
197
                    Geometry geom = feat.getDefaultGeometry();
198 34294 fdiaz
199 35329 jpiera
                    // retrieve the symbol associated to such feature
200
                    ISymbol sym = getSymbolByFeature(feat);
201
                    if (sym == null) {
202
                        continue;
203
                    }
204
                    if (useZSort) {
205
                        int[] symLevels = zSort.getLevels(sym);
206
                        if (symLevels != null) {
207 34294 fdiaz
208 35329 jpiera
                            // Check if this symbol is a multilayer
209
                            if (sym instanceof IMultiLayerSymbol) {
210
                                // if so, get the layer corresponding to the
211
                                // current level. If none, continue to next
212
                                // iteration
213
                                IMultiLayerSymbol mlSym = (IMultiLayerSymbol) sym;
214
                                for (int i = 0; i < mlSym.getLayerCount(); i++) {
215
                                    ISymbol mySym = mlSym.getLayer(i);
216
                                    if (symLevels[i] == mapPass) {
217
                                        sym = mySym;
218
                                        break;
219
                                    }
220
                                }
221 34294 fdiaz
222 35329 jpiera
                            } else {
223
                                // else, just draw the symbol in its level
224
                                if (symLevels[0] != mapPass) {
225
                                    continue;
226
                                }
227
                            }
228
                        }
229
                    }
230 34294 fdiaz
231 35329 jpiera
                    // Check if this symbol is sized with CartographicSupport
232
                    CartographicSupport csSym = null;
233
                    int symbolType = sym.getSymbolType();
234 34294 fdiaz
235 35329 jpiera
                    if (symbolType == Geometry.TYPES.POINT
236
                        || symbolType == Geometry.TYPES.CURVE
237
                        || sym instanceof CartographicSupport) {
238 34294 fdiaz
239 35329 jpiera
                        csSym = (CartographicSupport) sym;
240
                    }
241 34294 fdiaz
242 35329 jpiera
                    DrawOperationContext doc = new DrawOperationContext();
243
                    doc.setGraphics(g);
244
                    doc.setViewPort(viewPort);
245
                    if (csSym == null) {
246
                        doc.setSymbol(sym);
247
                    } else {
248
                        doc.setDPI(dpi);
249
                        doc.setCancellable(cancel);
250
                        doc.setSymbol((ISymbol) csSym);
251
                    }
252
                    geom.invokeOperation(DrawInts.CODE, doc);
253
                }
254
            }
255
        } catch (ReadException e) {
256
            throw new LegendDrawingException(e);
257
        } catch (GeometryOperationNotSupportedException e) {
258
            throw new LegendDrawingException(e);
259
        } catch (GeometryOperationException e) {
260
            throw new LegendDrawingException(e);
261
        } catch (DataException e) {
262
            throw new LegendDrawingException(e);
263
        } catch (MapContextException e) {
264
            throw new LegendDrawingException(e);
265
        } finally {
266
            if (it != null) {
267
                it.dispose();
268
            }
269
            if (featureSet != null) {
270
                featureSet.dispose();
271
            }
272
        }
273
    }
274 34294 fdiaz
275 35329 jpiera
    /**
276
     * Draws the features from the {@link FeatureStore}, filtered with the scale
277
     * and the query parameters, with the symbols of the legend.
278
     */
279
    @SuppressWarnings("unchecked")
280
    protected void draw(BufferedImage image, Graphics2D g, ViewPort viewPort,
281
        Cancellable cancel, double scale, Map queryParameters,
282
        ICoordTrans coordTrans, FeatureStore featureStore, FeatureQuery featureQuery, double dpi)
283
    throws LegendException {
284 34294 fdiaz
285 38207 cordinyana
                int retryCount = 0;
286
                SimpleTaskStatus taskStatus = ToolsLocator.getTaskStatusManager()
287
                                .createDefaultSimpleTaskStatus(featureStore.getName());
288
                taskStatus.add();
289
                boolean drawPerformed = false;
290
                while (retryCount < DRAW_MAX_ATTEMPTS && !drawPerformed) {
291
                        try {
292
                                internalDraw(image, g, viewPort, cancel, scale,
293
                                                queryParameters, coordTrans, featureStore,
294
                                                featureQuery, dpi, taskStatus);
295
                                drawPerformed = true;
296 38221 cordinyana
                        } catch (IllegalStateException e) {
297 38207 cordinyana
                                retryCount++;
298
                                if (retryCount >= DRAW_MAX_ATTEMPTS) {
299
                                        throw e;
300
                                }
301 38221 cordinyana
                    } catch (ConcurrentModificationException e) {
302
                                retryCount++;
303
                                if (retryCount >= DRAW_MAX_ATTEMPTS) {
304
                                        throw e;
305
                                }
306
                } catch (ConcurrentDataModificationException e) {
307
                                retryCount++;
308
                                if (retryCount >= DRAW_MAX_ATTEMPTS) {
309
                                        throw e;
310
                                }
311 38207 cordinyana
                        } finally {
312
                                if (taskStatus != null) {
313
                                        taskStatus.terminate();
314
                                        taskStatus.remove();
315
                                        taskStatus = null;
316
                                }
317
                        }
318
                }
319
    }
320 34294 fdiaz
321 38207 cordinyana
        protected void internalDraw(BufferedImage image, Graphics2D g,
322
                        ViewPort viewPort, Cancellable cancel, double scale,
323
                        Map queryParameters, ICoordTrans coordTrans,
324
                        FeatureStore featureStore, FeatureQuery featureQuery, double dpi,
325
                        SimpleTaskStatus taskStatus) throws LegendDrawingException {
326 34294 fdiaz
327 38207 cordinyana
                if (!getDefaultSymbol().isShapeVisible()) {
328
                        return;
329
                }
330 34294 fdiaz
331 38207 cordinyana
                if (cancel.isCanceled()) {
332
                        return;
333
                }
334 34294 fdiaz
335 38207 cordinyana
                IProjection dataProjection;
336
                Envelope reprojectedDataEnvelop;
337 34294 fdiaz
338 38207 cordinyana
                try {
339
                        if (coordTrans == null) {
340
                                dataProjection = featureStore.getDefaultFeatureType()
341
                                                .getDefaultSRS();
342 34294 fdiaz
343 38207 cordinyana
                                // If the data does not provide a projection, use the
344
                                // current view one
345
                                if (dataProjection == null) {
346
                                        dataProjection = viewPort.getProjection();
347
                                }
348 34294 fdiaz
349 38207 cordinyana
                                reprojectedDataEnvelop = featureStore.getEnvelope();
350
                        } else {
351
                                dataProjection = coordTrans.getPOrig();
352 34294 fdiaz
353 38207 cordinyana
                                Envelope env = featureStore.getEnvelope();
354
                                if (!env.isEmpty()) {
355
                                        reprojectedDataEnvelop = env.convert(coordTrans);
356
                                } else {
357
                                        reprojectedDataEnvelop = env;
358
                                }
359
                        }
360
                } catch (DataException e) {
361
                        throw new LegendDrawingException(e);
362
                }
363 34294 fdiaz
364 38207 cordinyana
                // Gets the view envelope
365
                Envelope viewPortEnvelope = viewPort.getAdjustedEnvelope();
366 34294 fdiaz
367 38207 cordinyana
                // Gets the data envelope with the viewport SRS
368
                Envelope myEnvelope = reprojectedDataEnvelop;
369 34294 fdiaz
370 38207 cordinyana
                // TODO: in some cases, the legend may need a different check to
371
                // decide if the data must be drawn or not
372
                // Checks if the viewport envelope intersects with the data envelope
373
                if (!viewPortEnvelope.intersects(myEnvelope)) {
374
                        // The data is not visible in the current viewport, do nothing.
375
                        return;
376
                }
377 34294 fdiaz
378 38207 cordinyana
                // Check if all the data is contained into the viewport envelope
379
                boolean containsAll = viewPortEnvelope.contains(myEnvelope);
380 34294 fdiaz
381 38207 cordinyana
                // Create the drawing notification to be reused on each iteration
382
                DefaultFeatureDrawnNotification drawnNotification = new DefaultFeatureDrawnNotification();
383 34294 fdiaz
384 38207 cordinyana
                if (cancel.isCanceled()) {
385
                        return;
386
                }
387 34294 fdiaz
388 38207 cordinyana
                FeatureSet featureSet = null;
389
                try {
390
                        taskStatus.message("Retrieve selection");
391
                        FeatureSelection selection = featureStore.getFeatureSelection();
392 34294 fdiaz
393 38207 cordinyana
                        if (featureQuery == null) {
394
                                featureQuery = featureStore.createFeatureQuery();
395
                        }
396 34294 fdiaz
397 38207 cordinyana
                        completeQuery(featureStore, featureQuery, scale, queryParameters,
398
                                        coordTrans, dataProjection, viewPortEnvelope, containsAll);
399 34294 fdiaz
400 38207 cordinyana
                        taskStatus.message("Retrieve data");
401
                        featureSet = featureStore.getFeatureSet(featureQuery);
402 34294 fdiaz
403 38207 cordinyana
                        if (cancel.isCanceled()) {
404
                                return;
405
                        }
406 34294 fdiaz
407 38207 cordinyana
                        taskStatus.message("Drawing");
408
                        drawFeatures(image, g, viewPort, cancel, coordTrans, dpi,
409
                                        drawnNotification, featureSet, selection);
410
411
                } catch (BaseException e) {
412
                        throw new LegendDrawingException(e);
413
                } finally {
414
                        if (featureSet != null) {
415
                                featureSet.dispose();
416
                        }
417
                }
418
        }
419
420 35329 jpiera
    /**
421
     * Complete a {@link FeatureQuery} to load the {@link Feature}s to draw.
422
     */
423
    @SuppressWarnings("unchecked")
424
    private FeatureQuery completeQuery(FeatureStore featureStore, FeatureQuery featureQuery, double scale,
425
        Map queryParameters, ICoordTrans coordTrans,
426
        IProjection dataProjection, Envelope viewPortEnvelope,
427
        boolean containsAll) throws DataException {
428 34294 fdiaz
429 35329 jpiera
        featureQuery.setScale(scale);
430
431
        //Adds the attributes
432
        String[] fieldNames = getRequiredFeatureAttributeNames(featureStore);
433
        for (int i=0 ; i<fieldNames.length ;i++){
434
            featureQuery.addAttributeName(fieldNames[i]);
435
        }
436
437
        // TODO: Mobile has it's own IntersectsEnvelopeEvaluator
438
        if (!containsAll) {
439
            // Gets the viewport envelope with the data SRS
440
            Envelope viewPortEnvelopeInMyProj = viewPortEnvelope;
441
            // FIXME
442
            if (coordTrans != null) {
443
                viewPortEnvelopeInMyProj = viewPortEnvelope.convert(coordTrans
444
                    .getInverted());
445
            }
446 34294 fdiaz
447 35329 jpiera
            if (dataProjection == null) {
448
                throw new IllegalArgumentException(
449
                "Error, the projection parameter value is null");
450
            }
451 34294 fdiaz
452 35329 jpiera
            IntersectsEnvelopeEvaluator iee = new IntersectsEnvelopeEvaluator(
453
                viewPortEnvelopeInMyProj, dataProjection,
454
                featureStore.getDefaultFeatureType(), featureStore
455
                .getDefaultFeatureType()
456
                .getDefaultGeometryAttributeName());
457
            featureQuery.addFilter(iee);
458
        }
459
        if (queryParameters != null) {
460
            Iterator iterEntry = queryParameters.entrySet().iterator();
461
            Entry entry;
462
            while (iterEntry.hasNext()) {
463
                entry = (Entry) iterEntry.next();
464
                featureQuery.setQueryParameter((String) entry.getKey(),
465
                    entry.getValue());
466
            }
467
        }
468
        return featureQuery;
469
    }
470 34294 fdiaz
471 35329 jpiera
    /**
472
     * Draws the features from the {@link FeatureSet}, with the symbols of the
473
     * legend.
474
     */
475
    private void drawFeatures(BufferedImage image, Graphics2D g,
476
        ViewPort viewPort, Cancellable cancel, ICoordTrans coordTrans,
477
        double dpi, DefaultFeatureDrawnNotification drawnNotification,
478
        FeatureSet featureSet, FeatureSelection selection)
479
    throws BaseException {
480
        if (isUseZSort()) {
481
            drawFeaturesMultiLayer(image, g, viewPort, cancel, coordTrans, dpi,
482
                drawnNotification, featureSet, selection);
483
        } else {
484
            drawFeaturesSingleLayer(image, g, viewPort, cancel, coordTrans,
485
                dpi, drawnNotification, featureSet, selection);
486
        }
487
    }
488 34294 fdiaz
489 35329 jpiera
    /**
490
     * Draws the features from the {@link FeatureSet}, with the symbols of the
491
     * legend, using a single drawing layer.
492
     */
493
    private void drawFeaturesSingleLayer(final BufferedImage image,
494
        final Graphics2D g, final ViewPort viewPort,
495
        final Cancellable cancel, final ICoordTrans coordTrans,
496
        final double dpi,
497
        final DefaultFeatureDrawnNotification drawnNotification,
498
        FeatureSet featureSet, final FeatureSelection selection)
499
    throws BaseException {
500 34294 fdiaz
501 35329 jpiera
        try {
502
            featureSet.accept(new Visitor() {
503
                public void visit(Object obj) throws VisitCanceledException,
504
                BaseException {
505
                    Feature feat = (Feature) obj;
506
                    drawFeatureSingleLayer(image, g, viewPort, cancel,
507
                        coordTrans, dpi, drawnNotification, feat, selection);
508
                }
509
            });
510 34294 fdiaz
511 35329 jpiera
        } catch (ConcurrentDataModificationException e) {
512
            cancel.setCanceled(true);
513
            return;
514
        }
515
    }
516 34294 fdiaz
517 35329 jpiera
    /**
518
     * Draws a Feature with the symbols of the legend, using a single drawing
519
     * layer.
520
     */
521
    private void drawFeatureSingleLayer(BufferedImage image, Graphics2D g,
522
        ViewPort viewPort, Cancellable cancel, ICoordTrans coordTrans,
523
        double dpi, DefaultFeatureDrawnNotification drawnNotification,
524
        Feature feat, FeatureSelection selection)
525
    throws MapContextException, CreateGeometryException {
526 34294 fdiaz
527 35329 jpiera
        Geometry geom = feat.getDefaultGeometry();
528
        if (geom == null) {
529
            return;
530
        }
531 34294 fdiaz
532 35329 jpiera
        if (geom.getType() == Geometry.TYPES.NULL) {
533
            return;
534
        }
535 34294 fdiaz
536 35329 jpiera
        ISymbol sym = getSymbol(feat, selection);
537
        if (sym == null) {
538
            return;
539
        }
540 34294 fdiaz
541 35329 jpiera
        if (coordTrans != null) {
542
            geom = geom.cloneGeometry();
543
            geom.reProject(coordTrans);
544
        }
545 34294 fdiaz
546 35329 jpiera
        if (cancel.isCanceled()) {
547
            return;
548
        }
549 34294 fdiaz
550 35329 jpiera
        drawGeometry(geom, image, feat, sym, viewPort, g, dpi, cancel);
551 34294 fdiaz
552 35329 jpiera
        // Notify the drawing observers
553
        drawnNotification.setFeature(feat);
554
        drawnNotification.setDrawnGeometry(geom);
555
        notifyObservers(drawnNotification);
556
    }
557 34294 fdiaz
558 35329 jpiera
    /**
559
     * Draws the features from the {@link FeatureSet}, with the symbols of the
560
     * legend, using a multiple drawing layer.
561
     */
562
    private void drawFeaturesMultiLayer(BufferedImage image, Graphics2D g,
563
        ViewPort viewPort, Cancellable cancel, ICoordTrans coordTrans,
564
        double dpi, DefaultFeatureDrawnNotification drawnNotification,
565
        FeatureSet featureSet, FeatureSelection selection)
566
    throws MapContextException, CreateGeometryException, DataException {
567 34294 fdiaz
568 35329 jpiera
        // -- visual FX stuff
569
        long time = System.currentTimeMillis();
570 34294 fdiaz
571 35329 jpiera
        boolean bSymbolLevelError = false;
572
        // render temporary map each screenRefreshRate milliseconds;
573
        int screenRefreshDelay = (int) ((1D / MapContext.getDrawFrameRate()) * 3 * 1000);
574
        BufferedImage[] imageLevels = null;
575
        Graphics2D[] graphics = null;
576 34294 fdiaz
577 35329 jpiera
        imageLevels = new BufferedImage[getZSort().getLevelCount()];
578
        graphics = new Graphics2D[imageLevels.length];
579
        for (int i = 0; !cancel.isCanceled() && i < imageLevels.length; i++) {
580 34294 fdiaz
581 35329 jpiera
            imageLevels[i] = CompatLocator.getGraphicsUtils()
582
            .createBufferedImage(image.getWidth(), image.getHeight(),
583
                image.getType());
584 34294 fdiaz
585 35329 jpiera
            graphics[i] = imageLevels[i].createGraphics();
586
            graphics[i].setTransform(g.getTransform());
587
            graphics[i].setRenderingHints(g.getRenderingHints());
588
        }
589
        // -- end visual FX stuff
590 34294 fdiaz
591 35329 jpiera
        DisposableIterator it = null;
592
        try {
593
            it = featureSet.fastIterator();
594
            // Iteration over each feature
595
            while (it.hasNext()) {
596
                if (cancel.isCanceled()) {
597
                    return;
598
                }
599
                Feature feat = (Feature) it.next();
600 34294 fdiaz
601 35329 jpiera
                bSymbolLevelError |= drawFeatureMultiLayer(image, g, viewPort,
602
                    cancel, coordTrans, dpi, drawnNotification, selection,
603
                    time, screenRefreshDelay, imageLevels, graphics, feat);
604 34294 fdiaz
605 35329 jpiera
            }
606
        } catch (ConcurrentDataModificationException e) {
607
            cancel.setCanceled(true);
608
            return;
609
        } finally {
610
            if (it != null) {
611
                it.dispose();
612
            }
613
        }
614 34294 fdiaz
615 35329 jpiera
        g.drawImage(image, 0, 0, null);
616 34294 fdiaz
617 35329 jpiera
        Point2D offset = viewPort.getOffset();
618
        CompatLocator.getGraphicsUtils().translate(g, offset.getX(),
619
            offset.getY());
620 34294 fdiaz
621 35329 jpiera
        for (int i = 0; !cancel.isCanceled() && i < imageLevels.length; i++) {
622
            g.drawImage(imageLevels[i], 0, 0, null);
623
            imageLevels[i] = null;
624
            graphics[i] = null;
625
        }
626 34294 fdiaz
627 35329 jpiera
        CompatLocator.getGraphicsUtils().translate(g, -offset.getX(),
628
            -offset.getY());
629 34294 fdiaz
630 35329 jpiera
        imageLevels = null;
631
        graphics = null;
632 34294 fdiaz
633 35329 jpiera
        if (bSymbolLevelError) {
634
            setZSort(null);
635
        }
636
    }
637 34294 fdiaz
638 35329 jpiera
    /**
639
     * Draws a Feature with the symbols of the legend, using a multiple drawing
640
     * layer.
641
     */
642
    private boolean drawFeatureMultiLayer(BufferedImage image, Graphics2D g,
643
        ViewPort viewPort, Cancellable cancel, ICoordTrans coordTrans,
644
        double dpi, DefaultFeatureDrawnNotification drawnNotification,
645
        FeatureSelection selection, long time, int screenRefreshDelay,
646
        BufferedImage[] imageLevels, Graphics2D[] graphics, Feature feat)
647
    throws MapContextException, CreateGeometryException {
648 34294 fdiaz
649 35329 jpiera
        Geometry geom = feat.getDefaultGeometry();
650
        boolean bSymbolLevelError = false;
651
        long drawingTime = time;
652 34294 fdiaz
653 35329 jpiera
        if (geom.getType() == Geometry.TYPES.NULL) {
654
            return false;
655
        }
656 34294 fdiaz
657 35329 jpiera
        ISymbol sym = getSymbol(feat, selection);
658 34294 fdiaz
659 35329 jpiera
        if (sym == null) {
660
            return false;
661
        }
662 34294 fdiaz
663 35329 jpiera
        if (coordTrans != null) {
664
            geom = geom.cloneGeometry();
665
            geom.reProject(coordTrans);
666
        }
667 34294 fdiaz
668 35329 jpiera
        if (cancel.isCanceled()) {
669
            return false;
670
        }
671 34294 fdiaz
672 35329 jpiera
        // Check if this symbol is a multilayer
673
        int[] symLevels = getZSort().getLevels(sym);
674
        if (sym instanceof IMultiLayerSymbol) {
675
            // if so, treat each of its layers as a single
676
            // symbol
677
            // in its corresponding map level
678
            IMultiLayerSymbol mlSym = (IMultiLayerSymbol) sym;
679
            for (int i = 0; !cancel.isCanceled() && i < mlSym.getLayerCount(); i++) {
680
                ISymbol mySym = mlSym.getLayer(i);
681
                int symbolLevel = 0;
682
                if (symLevels != null) {
683
                    symbolLevel = symLevels[i];
684
                } else {
685
                    /*
686
                     * an error occured when managing symbol levels some of the
687
                     * legend changed events regarding the symbols did not
688
                     * finish satisfactory and the legend is now inconsistent.
689
                     * For this drawing, it will finish as it was at the bottom
690
                     * (level 0) but, when done, the ZSort will be reset to
691
                     * avoid app crashes. This is a bug that has to be fixed.
692
                     */
693
                    bSymbolLevelError = true;
694
                }
695
                drawGeometry(geom, imageLevels[symbolLevel], feat, mySym,
696
                    viewPort, graphics[symbolLevel], dpi, cancel);
697
            }
698
        } else {
699
            // else, just draw the symbol in its level
700
            int symbolLevel = 0;
701
            if (symLevels != null) {
702
                symbolLevel = symLevels[0];
703
            }
704
            drawGeometry(geom, imageLevels[symbolLevel], feat, sym, viewPort,
705
                graphics[symbolLevel], dpi, cancel);
706
        }
707 34294 fdiaz
708 35329 jpiera
        // -- visual FX stuff
709
        // Cuando el offset!=0 se est? dibujando sobre el
710
        // Layout y por tanto no tiene que ejecutar el
711
        // siguiente c?digo.
712
        Point2D offset = viewPort.getOffset();
713
        if (offset.getX() == 0 && offset.getY() == 0) {
714
            if ((System.currentTimeMillis() - drawingTime) > screenRefreshDelay) {
715 34294 fdiaz
716 35329 jpiera
                BufferedImage virtualBim = CompatLocator.getGraphicsUtils()
717
                .createBufferedImage(image.getWidth(),
718
                    image.getHeight(), BufferedImage.TYPE_INT_ARGB);
719 34294 fdiaz
720 35329 jpiera
                Graphics2D virtualGraphics = virtualBim.createGraphics();
721
                virtualGraphics.drawImage(image, 0, 0, null);
722
                for (int i = 0; !cancel.isCanceled() && i < imageLevels.length; i++) {
723
                    virtualGraphics.drawImage(imageLevels[i], 0, 0, null);
724
                }
725
                g.clearRect(0, 0, image.getWidth(), image.getHeight());
726
                g.drawImage(virtualBim, 0, 0, null);
727
                drawingTime = System.currentTimeMillis();
728
            }
729
            // -- end visual FX stuff
730 34294 fdiaz
731 35329 jpiera
        }
732
        // Notify the drawing observers
733
        drawnNotification.setFeature(feat);
734
        drawnNotification.setDrawnGeometry(geom);
735
        notifyObservers(drawnNotification);
736
        return bSymbolLevelError;
737
    }
738 34294 fdiaz
739 35329 jpiera
    /**
740
     * Returns the symbol to use to draw a {@link Feature} taking into account
741
     * if it is selected.
742
     */
743
    private ISymbol getSymbol(Feature feat, FeatureSelection selection)
744
    throws MapContextException {
745
        // retrieve the symbol associated to such feature
746
        ISymbol sym = getSymbolByFeature(feat);
747 34294 fdiaz
748 35329 jpiera
        if (sym != null && selection.isSelected(feat)) {
749
            sym = sym.getSymbolForSelection();
750
        }
751
        return sym;
752
    }
753 34294 fdiaz
754 35329 jpiera
    /**
755
     * Returns if the legend is using a ZSort.
756
     */
757
    private boolean isUseZSort() {
758
        return getZSort() != null && getZSort().isUsingZSort();
759
    }
760 34294 fdiaz
761 35329 jpiera
    /**
762
     * Draws a {@link Geometry} using the given {@link ISymbol}, over the
763
     * {@link Graphics2D} object.
764
     */
765
    private void drawGeometry(Geometry geom, BufferedImage image,
766
        Feature feature, ISymbol symbol, ViewPort viewPort,
767
        Graphics2D graphics, double dpi, Cancellable cancellable)
768
    throws CreateGeometryException {
769 34294 fdiaz
770 35329 jpiera
        if (geom instanceof Aggregate) {
771
            drawAggregate((Aggregate) geom, image, feature, symbol, viewPort,
772
                graphics, dpi, cancellable);
773
        } else {
774
            boolean bDrawCartographicSupport = false;
775
            if (symbol instanceof CartographicSupport) {
776
                bDrawCartographicSupport = ((CartographicSupport) symbol)
777
                .getUnit() != -1;
778
            }
779 34294 fdiaz
780 35329 jpiera
            double previousSize = 0.0d;
781 34294 fdiaz
782 35329 jpiera
            if (bDrawCartographicSupport) {
783
                // make the symbol to resize itself with the current rendering
784
                // context
785
                previousSize = ((CartographicSupport) symbol)
786
                .toCartographicSize(viewPort, dpi, geom);
787
            }
788 34294 fdiaz
789 35329 jpiera
            try {
790
                // draw it as normally
791
                double[] dot = new double[2];
792
                boolean onePoint = symbol.isOneDotOrPixel(geom, dot, viewPort,
793
                    (int) dpi);
794 34294 fdiaz
795 35329 jpiera
                if (onePoint) {
796
                    if (dot[0] < 0 || dot[1] < 0 || dot[0] >= image.getWidth()
797
                        || dot[1] >= image.getHeight()) {
798
                        return;
799
                    }
800
                    // FIXME: setRgb should change to draw
801
                    image.setRGB((int) dot[0], (int) dot[1],
802
                        symbol.getOnePointRgb());
803
                } else {
804
                    symbol.draw(graphics, viewPort.getAffineTransform(),
805 35472 jpiera
                        geom, feature, cancellable);
806 35329 jpiera
                }
807 34294 fdiaz
808 35329 jpiera
            } finally {
809
                if (bDrawCartographicSupport) {
810
                    // restore previous size
811
                    ((CartographicSupport) symbol).setCartographicSize(
812
                        previousSize, geom);
813
                }
814
            }
815
        }
816
    }
817 34294 fdiaz
818 35329 jpiera
    /**
819
     * Draws an {@link Aggregate} using the given {@link ISymbol}, over the
820
     * {@link Graphics2D} object.
821
     */
822
    private void drawAggregate(Aggregate aggregate, BufferedImage image,
823
        Feature feature, ISymbol symbol, ViewPort viewPort,
824
        Graphics2D graphics, double dpi, Cancellable cancellable)
825
    throws CreateGeometryException {
826
        for (int i = 0; i < aggregate.getPrimitivesNumber(); i++) {
827
            Geometry prim = aggregate.getPrimitiveAt(i);
828
            drawGeometry(prim, image, feature, symbol, viewPort, graphics, dpi,
829
                cancellable);
830
        }
831
    }
832 34294 fdiaz
833 35329 jpiera
    public Object clone() throws CloneNotSupportedException {
834
        AbstractVectorialLegend clone = (AbstractVectorialLegend) super.clone();
835 34294 fdiaz
836 35329 jpiera
        // Clone zSort
837
        ZSort zSort = getZSort();
838
        if (zSort != null) {
839
            clone.setZSort(new ZSort(clone));
840
        }
841
        return clone;
842
    }
843 34294 fdiaz
844 35329 jpiera
    public void loadFromState(PersistentState state)
845
    throws PersistenceException {
846
        // Set parent properties
847
        super.loadFromState(state);
848
        // Set own properties
849 34294 fdiaz
850 35329 jpiera
        setShapeType(state.getInt(FIELD_SHAPETYPE));
851
        if (state.getBoolean(FIELD_HAS_ZSORT)) {
852
            setZSort(new ZSort(this));
853
        }
854
        setDefaultSymbol((ISymbol) state.get(FIELD_DEFAULT_SYMBOL));
855
    }
856 34294 fdiaz
857 35329 jpiera
    public void saveToState(PersistentState state) throws PersistenceException {
858
        // Save parent properties
859
        super.saveToState(state);
860
        // Save own properties
861
        state.set(FIELD_SHAPETYPE, getShapeType());
862
        state.set(FIELD_HAS_ZSORT, this.zSort != null);
863
        state.set(FIELD_DEFAULT_SYMBOL, getDefaultSymbol());
864
    }
865 34294 fdiaz
866 35329 jpiera
    public static class RegisterPersistence implements Callable {
867 34294 fdiaz
868 35329 jpiera
        public Object call() throws Exception {
869
            PersistenceManager manager = ToolsLocator.getPersistenceManager();
870
            if (manager
871
                .getDefinition(VECTORIAL_LEGEND_PERSISTENCE_DEFINITION_NAME) == null) {
872
                DynStruct definition = manager.addDefinition(
873
                    AbstractVectorialLegend.class,
874
                    VECTORIAL_LEGEND_PERSISTENCE_DEFINITION_NAME,
875
                    VECTORIAL_LEGEND_PERSISTENCE_DEFINITION_NAME
876
                    + " persistence definition", null, null);
877
                // Extend the Legend base definition
878
                definition.extend(manager
879
                    .getDefinition(LEGEND_PERSISTENCE_DEFINITION_NAME));
880 34935 jjdelcerro
881 35329 jpiera
                // Shapetype
882
                definition.addDynFieldInt(FIELD_SHAPETYPE).setMandatory(true);
883
                // ZSort
884
                definition.addDynFieldBoolean(FIELD_HAS_ZSORT).setMandatory(
885
                    true);
886
                // Default symbol
887
                definition.addDynFieldObject(FIELD_DEFAULT_SYMBOL)
888
                .setClassOfValue(ISymbol.class).setMandatory(true);
889
            }
890
            return Boolean.TRUE;
891
        }
892 34935 jjdelcerro
893 35329 jpiera
    }
894 34935 jjdelcerro
895 35329 jpiera
    /**
896
     * Returns the names of the {@link Feature} attributes required for the
897
     * {@link ILegend} to operate.
898
     *
899
     * @param featureStore
900
     *            the store where the {@link Feature}s belong to
901
     * @return the names of required {@link Feature} attribute names
902
     * @throws DataException
903
     *             if there is an error getting the attribute names
904
     */
905
    protected abstract String[] getRequiredFeatureAttributeNames(
906
        FeatureStore featureStore) throws DataException;
907 38221 cordinyana
}