Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.library / 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 @ 47790

History | View | Annotate | Download (37.2 KB)

1 40560 jjdelcerro
/**
2
 * gvSIG. Desktop Geographic Information System.
3 40435 jjdelcerro
 *
4 40560 jjdelcerro
 * Copyright (C) 2007-2013 gvSIG Association.
5
 *
6 40435 jjdelcerro
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8 40560 jjdelcerro
 * as published by the Free Software Foundation; either version 3
9 40435 jjdelcerro
 * of the License, or (at your option) any later version.
10 40560 jjdelcerro
 *
11 40435 jjdelcerro
 * 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 40560 jjdelcerro
 *
16 40435 jjdelcerro
 * 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 40560 jjdelcerro
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 40435 jjdelcerro
 * MA  02110-1301, USA.
20 40560 jjdelcerro
 *
21
 * For any additional information, do not hesitate to contact us
22
 * at info AT gvsig.com, or visit our website www.gvsig.com.
23 40435 jjdelcerro
 */
24
/*
25
 * AUTHORS (In addition to CIT):
26
 * 2009 {DiSiD Technologies}  {{Task}}
27
 */
28
package org.gvsig.symbology.fmap.mapcontext.rendering.legend.impl;
29
30 47790 fdiaz
import java.awt.Color;
31 40435 jjdelcerro
import java.awt.Graphics2D;
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 45887 jjdelcerro
import org.apache.commons.lang3.StringUtils;
38 40435 jjdelcerro
import org.cresques.cts.ICoordTrans;
39
import org.cresques.cts.IProjection;
40
import org.gvsig.compat.CompatLocator;
41
import org.gvsig.compat.print.PrintAttributes;
42 44361 jjdelcerro
import org.gvsig.expressionevaluator.ExpressionBuilder;
43
import org.gvsig.expressionevaluator.ExpressionUtils;
44 40435 jjdelcerro
import org.gvsig.fmap.dal.exception.DataException;
45
import org.gvsig.fmap.dal.feature.Feature;
46
import org.gvsig.fmap.dal.feature.FeatureQuery;
47 43206 jjdelcerro
import org.gvsig.fmap.dal.feature.FeatureReference;
48 40435 jjdelcerro
import org.gvsig.fmap.dal.feature.FeatureSelection;
49
import org.gvsig.fmap.dal.feature.FeatureSet;
50
import org.gvsig.fmap.dal.feature.FeatureStore;
51 44361 jjdelcerro
import org.gvsig.fmap.dal.feature.FeatureType;
52 40435 jjdelcerro
import org.gvsig.fmap.dal.feature.exception.ConcurrentDataModificationException;
53
import org.gvsig.fmap.geom.Geometry;
54
import org.gvsig.fmap.geom.GeometryLocator;
55
import org.gvsig.fmap.geom.GeometryManager;
56
import org.gvsig.fmap.geom.aggregate.Aggregate;
57
import org.gvsig.fmap.geom.exception.CreateGeometryException;
58
import org.gvsig.fmap.geom.exception.ReprojectionRuntimeException;
59
import org.gvsig.fmap.geom.primitive.Envelope;
60
import org.gvsig.fmap.mapcontext.MapContext;
61
import org.gvsig.fmap.mapcontext.MapContextException;
62
import org.gvsig.fmap.mapcontext.ViewPort;
63 43020 jjdelcerro
import org.gvsig.fmap.mapcontext.layers.vectorial.SpatialEvaluatorsFactory;
64 40435 jjdelcerro
import org.gvsig.fmap.mapcontext.rendering.legend.ILegend;
65
import org.gvsig.fmap.mapcontext.rendering.legend.IVectorLegend;
66
import org.gvsig.fmap.mapcontext.rendering.legend.LegendException;
67
import org.gvsig.fmap.mapcontext.rendering.legend.ZSort;
68
import org.gvsig.fmap.mapcontext.rendering.symbols.CartographicSupport;
69
import org.gvsig.fmap.mapcontext.rendering.symbols.IMultiLayerSymbol;
70
import org.gvsig.fmap.mapcontext.rendering.symbols.ISymbol;
71
import org.gvsig.tools.ToolsLocator;
72
import org.gvsig.tools.dispose.DisposableIterator;
73 43326 jjdelcerro
import org.gvsig.tools.dispose.DisposeUtils;
74 40435 jjdelcerro
import org.gvsig.tools.dynobject.DynStruct;
75 43020 jjdelcerro
import org.gvsig.tools.evaluator.Evaluator;
76 40435 jjdelcerro
import org.gvsig.tools.exception.BaseException;
77 43206 jjdelcerro
import org.gvsig.tools.logger.FilteredLogger;
78 40435 jjdelcerro
import org.gvsig.tools.persistence.PersistenceManager;
79
import org.gvsig.tools.persistence.PersistentState;
80
import org.gvsig.tools.persistence.exception.PersistenceException;
81
import org.gvsig.tools.task.Cancellable;
82
import org.gvsig.tools.task.SimpleTaskStatus;
83
import org.gvsig.tools.util.Callable;
84
import org.gvsig.tools.visitor.VisitCanceledException;
85 47476 fdiaz
import org.slf4j.Logger;
86
import org.slf4j.LoggerFactory;
87 40435 jjdelcerro
88
/**
89
 * Base implementation for Vectorial data Legends.
90 42464 fdiaz
 *
91 40435 jjdelcerro
 * 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 42464 fdiaz
 *
94 40435 jjdelcerro
 */
95
public abstract class AbstractVectorialLegend extends AbstractLegend implements
96
IVectorLegend {
97 42464 fdiaz
98 43437 jjdelcerro
        protected static final Logger LOG = LoggerFactory
99 40435 jjdelcerro
                        .getLogger(AbstractVectorialLegend.class);
100
101
    private static final int DRAW_MAX_ATTEMPTS = 5;
102
103
        public static final String VECTORIAL_LEGEND_PERSISTENCE_DEFINITION_NAME = "VectorialLegend";
104
105
    private static final String FIELD_HAS_ZSORT = "hasZSort";
106
    private static final String FIELD_SHAPETYPE = "shapeType";
107
    private static final String FIELD_DEFAULT_SYMBOL = "defaultSymbol";
108
109 46451 fdiaz
    private boolean drawSymbolInEeachPrimitive;
110 40435 jjdelcerro
111
    protected ZSort zSort;
112
113 46451 fdiaz
    protected AbstractVectorialLegend() {
114
        this.drawSymbolInEeachPrimitive = true;
115
    }
116
117
118 40435 jjdelcerro
    public ZSort getZSort() {
119
        return zSort;
120
    }
121
122
    public void setZSort(ZSort zSort) {
123
        if (zSort == null) {
124
            removeLegendListener(this.zSort);
125
        }
126
        this.zSort = zSort;
127
        addLegendListener(zSort);
128
    }
129 42464 fdiaz
130 40435 jjdelcerro
    @SuppressWarnings("unchecked")
131 43326 jjdelcerro
    @Override
132 40435 jjdelcerro
    public void draw(BufferedImage image, Graphics2D g, ViewPort viewPort,
133
        Cancellable cancel, double scale, Map queryParameters,
134
        ICoordTrans coordTrans, FeatureStore featureStore)
135
    throws LegendException {
136 41416 jjdelcerro
        double dpi = viewPort.getDPI();
137 40435 jjdelcerro
        draw(image, g, viewPort, cancel, scale, queryParameters, coordTrans,
138
            featureStore, null, dpi);
139
    }
140 42464 fdiaz
141 40435 jjdelcerro
    @SuppressWarnings("unchecked")
142 43326 jjdelcerro
    @Override
143 40435 jjdelcerro
    public void draw(BufferedImage image, Graphics2D g, ViewPort viewPort,
144
        Cancellable cancel, double scale, Map queryParameters,
145
        ICoordTrans coordTrans, FeatureStore featureStore, FeatureQuery featureQuery)
146
    throws LegendException {
147 41416 jjdelcerro
        double dpi = viewPort.getDPI();
148 40435 jjdelcerro
        draw(image, g, viewPort, cancel, scale, queryParameters, coordTrans,
149
            featureStore, featureQuery, dpi);
150
    }
151 43326 jjdelcerro
152 40435 jjdelcerro
    @SuppressWarnings("unchecked")
153 43326 jjdelcerro
    @Override
154 40435 jjdelcerro
    public void print(Graphics2D g, ViewPort viewPort, Cancellable cancel,
155
        double scale, Map queryParameters, ICoordTrans coordTrans,
156
        FeatureStore featureStore, PrintAttributes properties)
157
    throws LegendException {
158
        print(g, viewPort, cancel, scale, queryParameters, coordTrans,
159
            featureStore, null, properties);
160
    }
161 42464 fdiaz
162 40435 jjdelcerro
    @SuppressWarnings("unchecked")
163 43326 jjdelcerro
    @Override
164 40435 jjdelcerro
    public void print(Graphics2D g, ViewPort viewPort, Cancellable cancel,
165
        double scale, Map queryParameters, ICoordTrans coordTrans,
166 40491 jldominguez
        FeatureStore featureStore, FeatureQuery fquery, PrintAttributes properties)
167 40435 jjdelcerro
    throws LegendException {
168 44249 omartinez
169 40435 jjdelcerro
        double dpi = 72;
170
171
        int resolution = properties.getPrintQuality();
172
173
        if (resolution == PrintAttributes.PRINT_QUALITY_DRAFT
174
            || resolution == PrintAttributes.PRINT_QUALITY_NORMAL
175
            || resolution == PrintAttributes.PRINT_QUALITY_HIGH) {
176
            dpi = PrintAttributes.PRINT_QUALITY_DPI[resolution];
177
        }
178
179
        FeatureSet featureSet = null;
180
        DisposableIterator it = null;
181
        try {
182 43510 jjdelcerro
            GeometryManager geomManager = GeometryLocator.getGeometryManager();
183 47476 fdiaz
            ZSort theZSort = getZSort();
184 43326 jjdelcerro
            boolean useZSort = false;
185
            int mapLevelCount = 1;
186
187 47476 fdiaz
            if( theZSort != null ) {
188
                useZSort = theZSort.isUsingZSort();
189 43326 jjdelcerro
                if( useZSort ) {
190 47476 fdiaz
                    mapLevelCount = theZSort.getLevelCount();
191 43326 jjdelcerro
                }
192
            }
193 40435 jjdelcerro
            for (int mapPass = 0; mapPass < mapLevelCount; mapPass++) {
194 42464 fdiaz
195 43326 jjdelcerro
                Envelope vp_env_in_store_crs;
196
                IProjection store_crs;
197 40491 jldominguez
                if (coordTrans != null) {
198
                    // 'coordTrans' is from store crs to vp crs
199
                    ICoordTrans inv = coordTrans.getInverted();
200
                    Envelope aux = viewPort.getAdjustedEnvelope();
201
                    vp_env_in_store_crs = aux.convert(inv);
202
                    store_crs = coordTrans.getPOrig();
203
                } else {
204
                    vp_env_in_store_crs = viewPort.getAdjustedEnvelope();
205
                    store_crs = viewPort.getProjection();
206 40435 jjdelcerro
                }
207 42464 fdiaz
208 40491 jldominguez
                FeatureQuery feat_query = fquery;
209
                Envelope store_env = featureStore.getEnvelope();
210 43326 jjdelcerro
                boolean use_intersection_cond;
211 40491 jldominguez
                if (store_env == null) {
212
                    // Store does not know its envelope, so we must:
213
                    use_intersection_cond = true;
214
                } else {
215
                    if (vp_env_in_store_crs.contains(store_env)) {
216
                        use_intersection_cond = false;
217
                    } else {
218
                        use_intersection_cond = true;
219
                    }
220
                }
221 42464 fdiaz
222 40491 jldominguez
                if (use_intersection_cond) {
223 43020 jjdelcerro
                    Evaluator iee = SpatialEvaluatorsFactory.getInstance().intersects(
224 43295 fdiaz
                            vp_env_in_store_crs,
225
                            store_crs,
226 43020 jjdelcerro
                            featureStore
227
                    );
228 40491 jldominguez
                    if (feat_query == null) {
229
                        feat_query = featureStore.createFeatureQuery();
230
                    }
231 43003 fdiaz
                    String[] fns = getRequiredFeatureAttributeNames(featureStore);
232 47476 fdiaz
                    for (String fn : fns) {
233
                        feat_query.addAttributeName(fn);
234 43003 fdiaz
                    }
235 40491 jldominguez
                    feat_query.addFilter(iee);
236
                }
237 42464 fdiaz
238 40491 jldominguez
                // 'feat_query' can still be NULL here, so we only filter
239
                // the featureStore if it's not null
240
                if (feat_query == null) {
241
                    featureSet = featureStore.getFeatureSet();
242
                } else {
243
                    featureSet = featureStore.getFeatureSet(feat_query);
244
                }
245 40435 jjdelcerro
                it = featureSet.fastIterator();
246
                // Iteration over each feature
247 43206 jjdelcerro
                FilteredLogger logger = new FilteredLogger(LOG,"Drawing "+featureStore.getName(), 10);
248 40435 jjdelcerro
                while (!cancel.isCanceled() && it.hasNext()) {
249
                    Feature feat = (Feature) it.next();
250 43206 jjdelcerro
                    try {
251
                        Geometry geom = feat.getDefaultGeometry();
252
                        if (geom==null) {
253
                            continue;
254
                        }
255
                        // Reprojection if needed
256
                        if (coordTrans != null) {
257
                            geom = geom.cloneGeometry();
258
                            geom.reProject(coordTrans);
259
                        }
260 40435 jjdelcerro
261 43206 jjdelcerro
                        // retrieve the symbol associated to such feature
262
                        ISymbol sym = getSymbolByFeature(feat);
263
                        if (sym == null) {
264
                            continue;
265
                        }
266 47476 fdiaz
                        if (useZSort && theZSort != null) {
267
                            int[] symLevels = theZSort.getLevels(sym);
268 43206 jjdelcerro
                            if (symLevels != null) {
269 40435 jjdelcerro
270 43206 jjdelcerro
                                // Check if this symbol is a multilayer
271
                                if (sym instanceof IMultiLayerSymbol) {
272
                                    // if so, get the layer corresponding to the
273
                                    // current level. If none, continue to next
274
                                    // iteration
275
                                    IMultiLayerSymbol mlSym = (IMultiLayerSymbol) sym;
276
                                    for (int i = 0; i < mlSym.getLayerCount(); i++) {
277
                                        ISymbol mySym = mlSym.getLayer(i);
278
                                        if (symLevels[i] == mapPass) {
279
                                            sym = mySym;
280
                                            break;
281
                                        }
282 40435 jjdelcerro
                                    }
283
284 43206 jjdelcerro
                                } else {
285
                                    // else, just draw the symbol in its level
286
                                    if (symLevels[0] != mapPass) {
287
                                        continue;
288
                                    }
289 40435 jjdelcerro
                                }
290
                            }
291
                        }
292
293 43206 jjdelcerro
                        // Check if this symbol is sized with CartographicSupport
294
                        CartographicSupport csSym = null;
295
                        int symbolType = sym.getSymbolType();
296 40435 jjdelcerro
297 43206 jjdelcerro
                        if (symbolType == Geometry.TYPES.POINT
298 43510 jjdelcerro
                            ||  geomManager.isSubtype(symbolType, Geometry.TYPES.CURVE)
299 43206 jjdelcerro
                            || sym instanceof CartographicSupport) {
300 40435 jjdelcerro
301 43206 jjdelcerro
                            csSym = (CartographicSupport) sym;
302
                        }
303 40435 jjdelcerro
304 43206 jjdelcerro
                        if (csSym == null) {
305 43510 jjdelcerro
                            DrawUtils.drawInts(g, viewPort, sym, feat, geom);
306 43206 jjdelcerro
                        } else {
307 43510 jjdelcerro
                            DrawUtils.drawInts(g, viewPort, cancel, dpi, sym, feat, geom);
308 43206 jjdelcerro
                        }
309 43510 jjdelcerro
310 43206 jjdelcerro
                    } catch(Exception ex) {
311
                        FeatureReference ref = null;
312
                        if( feat!=null ) {
313
                            ref = feat.getReference();
314
                        }
315
                        logger.warn("Can't draw feature ("+ref+").", ex);
316 40435 jjdelcerro
                    }
317
                }
318
            }
319
        } catch (DataException e) {
320
            throw new LegendDrawingException(e);
321
        } finally {
322
            if (it != null) {
323
                it.dispose();
324
            }
325
            if (featureSet != null) {
326
                featureSet.dispose();
327
            }
328
        }
329
    }
330
331
    /**
332
     * Draws the features from the {@link FeatureStore}, filtered with the scale
333
     * and the query parameters, with the symbols of the legend.
334 43326 jjdelcerro
     * @param image
335
     * @param g
336
     * @param viewPort
337
     * @param cancel
338
     * @param scale
339
     * @param queryParameters
340
     * @param coordTrans
341
     * @param featureStore
342
     * @param featureQuery
343
     * @param dpi
344
     * @throws org.gvsig.fmap.mapcontext.rendering.legend.LegendException
345 40435 jjdelcerro
     */
346
    @SuppressWarnings("unchecked")
347
        protected void draw(BufferedImage image, Graphics2D g, ViewPort viewPort,
348
                        Cancellable cancel, double scale, Map queryParameters,
349
                        ICoordTrans coordTrans, FeatureStore featureStore,
350
                        FeatureQuery featureQuery, double dpi) throws LegendException {
351
352
                SimpleTaskStatus taskStatus = ToolsLocator.getTaskStatusManager()
353
                                .createDefaultSimpleTaskStatus(featureStore.getName());
354
                taskStatus.add();
355
                try {
356
                        // Avoid ConcurrentModificationException errors if
357
                        // while drawing another thread edits the store data.
358
                        synchronized (featureStore) {
359
                                internalDraw(image, g, viewPort, cancel, scale,
360
                                                queryParameters, coordTrans, featureStore,
361
                                                featureQuery, dpi, taskStatus);
362
                        }
363
                } finally {
364 43326 jjdelcerro
            taskStatus.terminate();
365
            taskStatus.remove();
366 40435 jjdelcerro
                }
367
        }
368
369
        protected void internalDraw(BufferedImage image, Graphics2D g,
370
                        ViewPort viewPort, Cancellable cancel, double scale,
371
                        Map queryParameters, ICoordTrans coordTrans,
372
                        FeatureStore featureStore, FeatureQuery featureQuery, double dpi,
373
                        SimpleTaskStatus taskStatus) throws LegendDrawingException {
374
375
                if (!getDefaultSymbol().isShapeVisible()) {
376
                        return;
377
                }
378
379
                if (cancel.isCanceled()) {
380
                        return;
381
                }
382
383
                IProjection dataProjection;
384 42464 fdiaz
                Envelope dataEnvelope;
385
                Envelope reprojectedDataEnvelope;
386
                // Gets the view envelope
387
                Envelope viewPortEnvelope = viewPort.getAdjustedEnvelope();
388
        Envelope reprojectedViewPortEnvelope;
389 40435 jjdelcerro
                try {
390 42464 fdiaz
                    dataEnvelope = featureStore.getEnvelope();
391 40435 jjdelcerro
                        if (coordTrans == null) {
392
                                dataProjection = featureStore.getDefaultFeatureType()
393
                                                .getDefaultSRS();
394
395
                                // If the data does not provide a projection, use the
396
                                // current view one
397
                                if (dataProjection == null) {
398
                                        dataProjection = viewPort.getProjection();
399
                                }
400
401 42464 fdiaz
                                reprojectedDataEnvelope = dataEnvelope;
402
                reprojectedViewPortEnvelope = viewPortEnvelope;
403 40435 jjdelcerro
                        } else {
404
                                dataProjection = coordTrans.getPOrig();
405
406 42464 fdiaz
                                if ( dataEnvelope!=null && !dataEnvelope.isEmpty()) {
407
                                        reprojectedDataEnvelope = dataEnvelope.convert(coordTrans);
408 40435 jjdelcerro
                                } else {
409 42464 fdiaz
                                        reprojectedDataEnvelope = dataEnvelope;
410 40435 jjdelcerro
                                }
411 42464 fdiaz
                if ( viewPortEnvelope!=null && !viewPortEnvelope.isEmpty()) {
412
                    reprojectedViewPortEnvelope = viewPortEnvelope.convert(coordTrans.getInverted());
413
                } else {
414
                    reprojectedViewPortEnvelope = viewPortEnvelope;
415
                }
416 40435 jjdelcerro
                        }
417
                } catch (DataException e) {
418
                        throw new LegendDrawingException(e);
419
                }
420
421
422
                // Gets the data envelope with the viewport SRS
423 42464 fdiaz
//                Envelope myEnvelope = reprojectedDataEnvelope;
424 40435 jjdelcerro
425
                // TODO: in some cases, the legend may need a different check to
426
                // decide if the data must be drawn or not
427
                // Checks if the viewport envelope intersects with the data envelope
428 42464 fdiaz
                // This condition may seem redundant, but sometimes the transformations may fail and cause false negatives.
429 43295 fdiaz
                if ((viewPortEnvelope==null || !viewPortEnvelope.intersects(reprojectedDataEnvelope)) && !(reprojectedViewPortEnvelope!=null && reprojectedViewPortEnvelope.intersects(dataEnvelope))) {
430 40435 jjdelcerro
                        // The data is not visible in the current viewport, do nothing.
431
                        return;
432
                }
433
434
                // Check if all the data is contained into the viewport envelope
435 42464 fdiaz
        // This condition may seem redundant, but sometimes the transformations may fail and cause false negatives.
436 47476 fdiaz
        //The Envelope.contains method checks that the parameter is null or not
437
                boolean containsAll = viewPortEnvelope.contains(reprojectedDataEnvelope)
438
                        || (reprojectedViewPortEnvelope!=null && reprojectedViewPortEnvelope.contains(dataEnvelope));
439 40435 jjdelcerro
440
                // Create the drawing notification to be reused on each iteration
441
                DefaultFeatureDrawnNotification drawnNotification = new DefaultFeatureDrawnNotification();
442
443
                if (cancel.isCanceled()) {
444
                        return;
445
                }
446
447
                FeatureSet featureSet = null;
448 46504 fdiaz
                FeatureSelection selection = null;
449
                boolean mustDisposeSelection = false;
450 40435 jjdelcerro
                try {
451
                        taskStatus.message("Retrieve selection");
452 46277 jjdelcerro
                        if( featureStore.isFeatureSelectionEmpty() ) {
453
                            // No hay seleccion, asi que creamos una vacia que consuma pocos recursos.
454
                            selection = featureStore.createMemoryFeatureSelection();
455 46504 fdiaz
                            mustDisposeSelection = true;
456 46277 jjdelcerro
                        } else {
457
                            // Ojo, que esta seleccion puede acabar haciendo un count(*) sobre la tabla.
458
                            selection = featureStore.getFeatureSelection();
459 46504 fdiaz
                            mustDisposeSelection = false;
460 46277 jjdelcerro
                        }
461 40435 jjdelcerro
462
                        if (featureQuery == null) {
463
                                featureQuery = featureStore.createFeatureQuery();
464
                        }
465
466
                        completeQuery(featureStore, featureQuery, scale, queryParameters,
467
                                        coordTrans, dataProjection, viewPortEnvelope, containsAll);
468
469
                        taskStatus.message("Retrieve data");
470
                        featureSet = featureStore.getFeatureSet(featureQuery);
471
472
                        if (cancel.isCanceled()) {
473
                                return;
474
                        }
475
476
                        taskStatus.message("Drawing");
477
                        drawFeatures(image, g, viewPort, cancel, coordTrans, dpi,
478
                                        drawnNotification, featureSet, selection);
479
480 43326 jjdelcerro
        } catch (Throwable e) {
481 40435 jjdelcerro
            /*
482
             * Probably a reprojection exception (for example,
483
             * trying to reproject Canada to EPSG:23030)
484
             */
485
            throw new LegendDrawingException(e);
486 46504 fdiaz
        } finally {
487
                if (featureSet != null) {
488
                        featureSet.dispose();
489
                }
490
                if(mustDisposeSelection) {
491
                    DisposeUtils.disposeQuietly(selection);
492
                }
493
        }
494
    }
495 40435 jjdelcerro
496
    /**
497
     * Complete a {@link FeatureQuery} to load the {@link Feature}s to draw.
498
     */
499
    @SuppressWarnings("unchecked")
500
    private FeatureQuery completeQuery(FeatureStore featureStore, FeatureQuery featureQuery, double scale,
501
        Map queryParameters, ICoordTrans coordTrans,
502
        IProjection dataProjection, Envelope viewPortEnvelope,
503
        boolean containsAll) throws DataException {
504 47505 fdiaz
        boolean retrievesAllAttributes = ((featureQuery.hasFilter() && !featureQuery.hasAttributeNames()));
505 40435 jjdelcerro
506
        featureQuery.setScale(scale);
507 42464 fdiaz
508 40435 jjdelcerro
        //Adds the attributes
509
        String[] fieldNames = getRequiredFeatureAttributeNames(featureStore);
510 47476 fdiaz
            for (String fieldName : fieldNames) {
511
                if( StringUtils.isNotBlank(fieldName) ) {
512
                    featureQuery.addAttributeName(fieldName);
513
                }
514 45887 jjdelcerro
            }
515 42464 fdiaz
516 40435 jjdelcerro
        if (!containsAll) {
517
            // Gets the viewport envelope with the data SRS
518
            Envelope viewPortEnvelopeInMyProj = viewPortEnvelope;
519
            if (coordTrans != null) {
520
                viewPortEnvelopeInMyProj = viewPortEnvelope.convert(coordTrans
521
                        .getInverted());
522
            }
523
524
            if (dataProjection == null) {
525
                throw new IllegalArgumentException(
526
                "Error, the projection parameter value is null");
527
            }
528
529 43020 jjdelcerro
            Evaluator iee = SpatialEvaluatorsFactory.getInstance().intersects(
530 43295 fdiaz
                    viewPortEnvelopeInMyProj,
531
                    dataProjection,
532 43020 jjdelcerro
                    featureStore
533
            );
534 42464 fdiaz
            featureQuery.addFilter(iee);
535 44361 jjdelcerro
        } else {
536
            FeatureType ft = featureStore.getDefaultFeatureType();
537
            ExpressionBuilder expbuilder = ExpressionUtils.createExpressionBuilder();
538
            featureQuery.addFilter(
539
                expbuilder.not_is_null(
540
                    expbuilder.column(
541
                            ft.getDefaultGeometryAttributeName()
542
                    )
543
                ).toString()
544
            );
545 40435 jjdelcerro
        }
546 44361 jjdelcerro
547 40435 jjdelcerro
        if (queryParameters != null) {
548
            Iterator iterEntry = queryParameters.entrySet().iterator();
549
            Entry entry;
550
            while (iterEntry.hasNext()) {
551
                entry = (Entry) iterEntry.next();
552
                featureQuery.setQueryParameter((String) entry.getKey(),
553
                    entry.getValue());
554
            }
555 42464 fdiaz
        }
556 47505 fdiaz
//        featureStore.addRequiredAttributes(featureQuery);
557 45355 omartinez
        if( retrievesAllAttributes ) {
558
            featureQuery.retrievesAllAttributes();
559
        }
560 40435 jjdelcerro
        return featureQuery;
561
    }
562
563
    /**
564
     * Draws the features from the {@link FeatureSet}, with the symbols of the
565
     * legend.
566
     */
567 43436 jjdelcerro
    protected void drawFeatures(BufferedImage image, Graphics2D g,
568 40435 jjdelcerro
        ViewPort viewPort, Cancellable cancel, ICoordTrans coordTrans,
569
        double dpi, DefaultFeatureDrawnNotification drawnNotification,
570
        FeatureSet featureSet, FeatureSelection selection)
571
    throws BaseException {
572
        if (isUseZSort()) {
573
            drawFeaturesMultiLayer(image, g, viewPort, cancel, coordTrans, dpi,
574
                drawnNotification, featureSet, selection);
575
        } else {
576
            drawFeaturesSingleLayer(image, g, viewPort, cancel, coordTrans,
577
                dpi, drawnNotification, featureSet, selection);
578
        }
579
    }
580
581
    /**
582
     * Draws the features from the {@link FeatureSet}, with the symbols of the
583
     * legend, using a single drawing layer.
584
     */
585
    private void drawFeaturesSingleLayer(final BufferedImage image,
586
        final Graphics2D g, final ViewPort viewPort,
587
        final Cancellable cancel, final ICoordTrans coordTrans,
588
        final double dpi,
589
        final DefaultFeatureDrawnNotification drawnNotification,
590
        FeatureSet featureSet, final FeatureSelection selection)
591
    throws BaseException {
592
593
        try {
594 47476 fdiaz
            featureSet.accept((Object obj) -> {
595
                if( cancel.isCanceled() )  {
596
                    throw new VisitCanceledException();
597 40435 jjdelcerro
                }
598 47476 fdiaz
                Feature feat = (Feature) obj;
599
                drawFeatureSingleLayer(image, g, viewPort, cancel,
600
                        coordTrans, dpi, drawnNotification, feat, selection);
601 40435 jjdelcerro
            });
602
603
        } catch (ConcurrentDataModificationException e) {
604
            cancel.setCanceled(true);
605
        }
606
    }
607
608
    /**
609
     * Draws a Feature with the symbols of the legend, using a single drawing
610
     * layer.
611
     */
612
    private void drawFeatureSingleLayer(BufferedImage image, Graphics2D g,
613
        ViewPort viewPort, Cancellable cancel, ICoordTrans coordTrans,
614
        double dpi, DefaultFeatureDrawnNotification drawnNotification,
615
        Feature feat, FeatureSelection selection)
616
    throws MapContextException, CreateGeometryException {
617
618
        Geometry geom = feat.getDefaultGeometry();
619
        if (geom == null) {
620
            return;
621
        }
622
623
        if (geom.getType() == Geometry.TYPES.NULL) {
624
            return;
625
        }
626
627 47790 fdiaz
        ISymbol sym = getSymbol(feat, selection, viewPort.getSelectionColor());
628 40435 jjdelcerro
        if (sym == null) {
629
            return;
630
        }
631
632
        if (coordTrans != null) {
633
            geom = geom.cloneGeometry();
634
            try {
635
                geom.reProject(coordTrans);
636
            } catch (ReprojectionRuntimeException re) {
637 42464 fdiaz
                LOG.warn("Can't reproject geometry "+geom.toString(), re);
638
                return;
639 40435 jjdelcerro
            }
640
        }
641
642
        if (cancel.isCanceled()) {
643
            return;
644
        }
645
646
        drawGeometry(geom, image, feat, sym, viewPort, g, dpi, cancel);
647
648
        // Notify the drawing observers
649
        drawnNotification.setFeature(feat);
650
        drawnNotification.setDrawnGeometry(geom);
651
        notifyObservers(drawnNotification);
652
    }
653
654
    /**
655
     * Draws the features from the {@link FeatureSet}, with the symbols of the
656
     * legend, using a multiple drawing layer.
657
     */
658
    private void drawFeaturesMultiLayer(BufferedImage image, Graphics2D g,
659
        ViewPort viewPort, Cancellable cancel, ICoordTrans coordTrans,
660
        double dpi, DefaultFeatureDrawnNotification drawnNotification,
661
        FeatureSet featureSet, FeatureSelection selection)
662
    throws MapContextException, CreateGeometryException, DataException {
663
664
        // -- visual FX stuff
665
        long time = System.currentTimeMillis();
666
667
        boolean bSymbolLevelError = false;
668
        int screenRefreshDelay = (int) ((1D / MapContext.getDrawFrameRate()) * 3 * 1000);
669 43326 jjdelcerro
        BufferedImage[] imageLevels;
670
        Graphics2D[] graphics;
671 40435 jjdelcerro
672
        imageLevels = new BufferedImage[getZSort().getLevelCount()];
673
        graphics = new Graphics2D[imageLevels.length];
674
        for (int i = 0; !cancel.isCanceled() && i < imageLevels.length; i++) {
675
676
            imageLevels[i] = CompatLocator.getGraphicsUtils()
677
            .createBufferedImage(image.getWidth(), image.getHeight(),
678
                image.getType());
679
680
            graphics[i] = imageLevels[i].createGraphics();
681
            graphics[i].setTransform(g.getTransform());
682
            graphics[i].setRenderingHints(g.getRenderingHints());
683
        }
684
685
        DisposableIterator it = null;
686
        try {
687
            it = featureSet.fastIterator();
688
            // Iteration over each feature
689
            while (it.hasNext()) {
690
                if (cancel.isCanceled()) {
691
                    return;
692
                }
693
                Feature feat = (Feature) it.next();
694
695
                bSymbolLevelError |= drawFeatureMultiLayer(image, g, viewPort,
696
                    cancel, coordTrans, dpi, drawnNotification, selection,
697
                    time, screenRefreshDelay, imageLevels, graphics, feat);
698
699
            }
700
        } catch (ConcurrentDataModificationException e) {
701
            cancel.setCanceled(true);
702
            return;
703
        } finally {
704 43326 jjdelcerro
            DisposeUtils.dispose(it);
705 40435 jjdelcerro
        }
706
707
        g.drawImage(image, 0, 0, null);
708
709
        Point2D offset = viewPort.getOffset();
710
        CompatLocator.getGraphicsUtils().translate(g, offset.getX(),
711
            offset.getY());
712
713
        for (int i = 0; !cancel.isCanceled() && i < imageLevels.length; i++) {
714
            g.drawImage(imageLevels[i], 0, 0, null);
715
            imageLevels[i] = null;
716
            graphics[i] = null;
717
        }
718
719
        CompatLocator.getGraphicsUtils().translate(g, -offset.getX(),
720
            -offset.getY());
721
722
        if (bSymbolLevelError) {
723
            setZSort(null);
724
        }
725
    }
726
727
    /**
728
     * Draws a Feature with the symbols of the legend, using a multiple drawing
729
     * layer.
730
     */
731
    private boolean drawFeatureMultiLayer(BufferedImage image, Graphics2D g,
732
        ViewPort viewPort, Cancellable cancel, ICoordTrans coordTrans,
733
        double dpi, DefaultFeatureDrawnNotification drawnNotification,
734
        FeatureSelection selection, long time, int screenRefreshDelay,
735
        BufferedImage[] imageLevels, Graphics2D[] graphics, Feature feat)
736
    throws MapContextException, CreateGeometryException {
737
738
        Geometry geom = feat.getDefaultGeometry();
739
        boolean bSymbolLevelError = false;
740
        long drawingTime = time;
741
742 43326 jjdelcerro
        if (geom==null || geom.getType() == Geometry.TYPES.NULL) {
743 40435 jjdelcerro
            return false;
744
        }
745
746 47790 fdiaz
        ISymbol sym = getSymbol(feat, selection, viewPort.getSelectionColor());
747 40435 jjdelcerro
748
        if (sym == null) {
749
            return false;
750
        }
751
752
        if (coordTrans != null) {
753
            geom = geom.cloneGeometry();
754
            geom.reProject(coordTrans);
755
        }
756
757
        if (cancel.isCanceled()) {
758
            return false;
759
        }
760
761
        // Check if this symbol is a multilayer
762
        int[] symLevels = getZSort().getLevels(sym);
763
        if (sym instanceof IMultiLayerSymbol) {
764
            // if so, treat each of its layers as a single
765
            // symbol
766
            // in its corresponding map level
767
            IMultiLayerSymbol mlSym = (IMultiLayerSymbol) sym;
768
            for (int i = 0; !cancel.isCanceled() && i < mlSym.getLayerCount(); i++) {
769
                ISymbol mySym = mlSym.getLayer(i);
770
                int symbolLevel = 0;
771
                if (symLevels != null) {
772
                    symbolLevel = symLevels[i];
773
                } else {
774
                    /*
775
                     * an error occured when managing symbol levels some of the
776
                     * legend changed events regarding the symbols did not
777
                     * finish satisfactory and the legend is now inconsistent.
778
                     * For this drawing, it will finish as it was at the bottom
779
                     * (level 0) but, when done, the ZSort will be reset to
780
                     * avoid app crashes. This is a bug that has to be fixed.
781
                     */
782
                    bSymbolLevelError = true;
783
                }
784
                drawGeometry(geom, imageLevels[symbolLevel], feat, mySym,
785
                    viewPort, graphics[symbolLevel], dpi, cancel);
786
            }
787
        } else {
788
            // else, just draw the symbol in its level
789
            int symbolLevel = 0;
790
            if (symLevels != null) {
791
                symbolLevel = symLevels[0];
792
            }
793
            drawGeometry(geom, imageLevels[symbolLevel], feat, sym, viewPort,
794
                graphics[symbolLevel], dpi, cancel);
795
        }
796
797
        // -- visual FX stuff
798
        // Cuando el offset!=0 se est? dibujando sobre el
799
        // Layout y por tanto no tiene que ejecutar el
800
        // siguiente c?digo.
801
        Point2D offset = viewPort.getOffset();
802
        if (offset.getX() == 0 && offset.getY() == 0) {
803
            if ((System.currentTimeMillis() - drawingTime) > screenRefreshDelay) {
804
805
                BufferedImage virtualBim = CompatLocator.getGraphicsUtils()
806
                .createBufferedImage(image.getWidth(),
807
                    image.getHeight(), BufferedImage.TYPE_INT_ARGB);
808
809
                Graphics2D virtualGraphics = virtualBim.createGraphics();
810
                virtualGraphics.drawImage(image, 0, 0, null);
811
                for (int i = 0; !cancel.isCanceled() && i < imageLevels.length; i++) {
812
                    virtualGraphics.drawImage(imageLevels[i], 0, 0, null);
813
                }
814
                g.clearRect(0, 0, image.getWidth(), image.getHeight());
815
                g.drawImage(virtualBim, 0, 0, null);
816
                drawingTime = System.currentTimeMillis();
817
            }
818
            // -- end visual FX stuff
819
820
        }
821
        // Notify the drawing observers
822
        drawnNotification.setFeature(feat);
823
        drawnNotification.setDrawnGeometry(geom);
824
        notifyObservers(drawnNotification);
825
        return bSymbolLevelError;
826
    }
827
828
    /**
829
     * Returns the symbol to use to draw a {@link Feature} taking into account
830
     * if it is selected.
831
     */
832 47790 fdiaz
    private ISymbol getSymbol(Feature feat, FeatureSelection selection, Color selectionColor)
833 40435 jjdelcerro
    throws MapContextException {
834
        // retrieve the symbol associated to such feature
835
        ISymbol sym = getSymbolByFeature(feat);
836
837
        if (sym != null && selection.isSelected(feat)) {
838 47790 fdiaz
            sym = sym.getSymbolForSelection(selectionColor);
839 40435 jjdelcerro
        }
840
        return sym;
841
    }
842
843
    /**
844
     * Returns if the legend is using a ZSort.
845
     */
846
    private boolean isUseZSort() {
847
        return getZSort() != null && getZSort().isUsingZSort();
848
    }
849
850
    /**
851
     * Draws a {@link Geometry} using the given {@link ISymbol}, over the
852
     * {@link Graphics2D} object.
853
     */
854
    private void drawGeometry(Geometry geom, BufferedImage image,
855
        Feature feature, ISymbol symbol, ViewPort viewPort,
856
        Graphics2D graphics, double dpi, Cancellable cancellable)
857
    throws CreateGeometryException {
858
859 46451 fdiaz
        if (geom instanceof Aggregate && this.isDrawSymbolInEeachPrimitive()) {
860 40435 jjdelcerro
            drawAggregate((Aggregate) geom, image, feature, symbol, viewPort,
861
                graphics, dpi, cancellable);
862
        } else {
863
            if (symbol instanceof CartographicSupport) {
864 47476 fdiaz
//                if(((CartographicSupport) symbol).getUnit() == -1){
865
//                    ((CartographicSupport) symbol).setCartographicContext((CartographicContext)null);
866
//                } else {
867
                    ((CartographicSupport) symbol).setCartographicContext(viewPort, dpi, geom);
868
//                }
869 40435 jjdelcerro
            }
870 47476 fdiaz
            symbol.draw(graphics, viewPort.getAffineTransform(), geom, feature, cancellable);
871 40435 jjdelcerro
        }
872
    }
873
874
    /**
875
     * Draws an {@link Aggregate} using the given {@link ISymbol}, over the
876
     * {@link Graphics2D} object.
877
     */
878
    private void drawAggregate(Aggregate aggregate, BufferedImage image,
879
        Feature feature, ISymbol symbol, ViewPort viewPort,
880
        Graphics2D graphics, double dpi, Cancellable cancellable)
881
    throws CreateGeometryException {
882
        for (int i = 0; i < aggregate.getPrimitivesNumber(); i++) {
883
            Geometry prim = aggregate.getPrimitiveAt(i);
884
            drawGeometry(prim, image, feature, symbol, viewPort, graphics, dpi,
885
                cancellable);
886
        }
887
    }
888
889 43326 jjdelcerro
    @Override
890 40435 jjdelcerro
    public Object clone() throws CloneNotSupportedException {
891
        AbstractVectorialLegend clone = (AbstractVectorialLegend) super.clone();
892
893
        // Clone zSort
894 47476 fdiaz
        ZSort theZSort = getZSort();
895
        if (theZSort != null) {
896 40435 jjdelcerro
            clone.setZSort(new ZSort(clone));
897
        }
898
        return clone;
899
    }
900
901 43326 jjdelcerro
    @Override
902 40435 jjdelcerro
    public void loadFromState(PersistentState state)
903
    throws PersistenceException {
904
        // Set parent properties
905
        super.loadFromState(state);
906
        // Set own properties
907
908
        setShapeType(state.getInt(FIELD_SHAPETYPE));
909
        if (state.getBoolean(FIELD_HAS_ZSORT)) {
910
            setZSort(new ZSort(this));
911
        }
912
        setDefaultSymbol((ISymbol) state.get(FIELD_DEFAULT_SYMBOL));
913
    }
914
915 43326 jjdelcerro
    @Override
916 40435 jjdelcerro
    public void saveToState(PersistentState state) throws PersistenceException {
917
        // Save parent properties
918
        super.saveToState(state);
919
        // Save own properties
920
        state.set(FIELD_SHAPETYPE, getShapeType());
921
        state.set(FIELD_HAS_ZSORT, this.zSort != null);
922
        state.set(FIELD_DEFAULT_SYMBOL, getDefaultSymbol());
923
    }
924
925
    public static class RegisterPersistence implements Callable {
926
927 43326 jjdelcerro
        @Override
928 40435 jjdelcerro
        public Object call() throws Exception {
929
            PersistenceManager manager = ToolsLocator.getPersistenceManager();
930
            if (manager
931
                .getDefinition(VECTORIAL_LEGEND_PERSISTENCE_DEFINITION_NAME) == null) {
932
                DynStruct definition = manager.addDefinition(
933
                    AbstractVectorialLegend.class,
934
                    VECTORIAL_LEGEND_PERSISTENCE_DEFINITION_NAME,
935
                    VECTORIAL_LEGEND_PERSISTENCE_DEFINITION_NAME
936
                    + " persistence definition", null, null);
937
                // Extend the Legend base definition
938 43326 jjdelcerro
                definition.extend(manager.getDefinition(LEGEND_PERSISTENCE_DEFINITION_NAME));
939 40435 jjdelcerro
940
                // Shapetype
941
                definition.addDynFieldInt(FIELD_SHAPETYPE).setMandatory(true);
942
                // ZSort
943 43326 jjdelcerro
                definition.addDynFieldBoolean(FIELD_HAS_ZSORT).setMandatory(true);
944 40435 jjdelcerro
                // Default symbol
945
                definition.addDynFieldObject(FIELD_DEFAULT_SYMBOL)
946 43326 jjdelcerro
                    .setClassOfValue(ISymbol.class).setMandatory(true);
947 40435 jjdelcerro
            }
948
            return Boolean.TRUE;
949
        }
950
951
    }
952
953
    /**
954
     * Returns the names of the {@link Feature} attributes required for the
955
     * {@link ILegend} to operate.
956 42464 fdiaz
     *
957 40435 jjdelcerro
     * @param featureStore
958
     *            the store where the {@link Feature}s belong to
959
     * @return the names of required {@link Feature} attribute names
960
     * @throws DataException
961
     *             if there is an error getting the attribute names
962
     */
963
    protected abstract String[] getRequiredFeatureAttributeNames(
964
        FeatureStore featureStore) throws DataException;
965 46451 fdiaz
966
    /**
967
     * @return the drawSymbolInEeachPrimitive
968
     */
969
    public boolean isDrawSymbolInEeachPrimitive() {
970
        return drawSymbolInEeachPrimitive;
971
    }
972
973
    /**
974
     * @param drawSymbolInEeachPrimitive the drawSymbolInEeachPrimitive to set
975
     */
976
    public void setDrawSymbolInEeachPrimitive(boolean drawSymbolInEeachPrimitive) {
977
        this.drawSymbolInEeachPrimitive = drawSymbolInEeachPrimitive;
978
    }
979 40435 jjdelcerro
}