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