/[schmitzm]/branches/1.0-gt2-2.6/src/skrueger/geotools/selection/FeatureMapLayerSelectionSynchronizer.java
ViewVC logotype

Contents of /branches/1.0-gt2-2.6/src/skrueger/geotools/selection/FeatureMapLayerSelectionSynchronizer.java

Parent Directory Parent Directory | Revision Log Revision Log


Revision 500 - (show annotations)
Sun Oct 25 16:56:21 2009 UTC (15 years, 4 months ago) by alfonx
File MIME type: text/plain
File size: 18077 byte(s)
Der Style wird nur erneuert, wenn die selection sich geändert hat. Sollte man denken, dass das sowieso nur passiert? Ja, aber bei einzelnen clicks auf punkte im chart, wurde das styling zweimal gesetzt...


1 /*******************************************************************************
2 * Copyright (c) 2009 Martin O. J. Schmitz.
3 *
4 * This file is part of the SCHMITZM library - a collection of utility
5 * classes based on Java 1.6, focusing (not only) on Java Swing
6 * and the Geotools library.
7 *
8 * The SCHMITZM project is hosted at:
9 * http://wald.intevation.org/projects/schmitzm/
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public License
13 * as published by the Free Software Foundation; either version 3
14 * of the License, or (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU Lesser General Public License (license.txt)
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24 * or try this link: http://www.gnu.org/licenses/lgpl.html
25 *
26 * Contributors:
27 * Martin O. J. Schmitz - initial API and implementation
28 * Stefan A. Krüger - additional utility classes
29 ******************************************************************************/
30 /**
31 Copyright 2008 Stefan Alfons Krüger
32
33 atlas-framework - This file is part of the Atlas Framework
34
35 This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.
36 This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
37 You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
38
39 Diese Bibliothek ist freie Software; Sie dürfen sie unter den Bedingungen der GNU Lesser General Public License, wie von der Free Software Foundation veröffentlicht, weiterverteilen und/oder modifizieren; entweder gemäß Version 2.1 der Lizenz oder (nach Ihrer Option) jeder späteren Version.
40 Diese Bibliothek wird in der Hoffnung weiterverbreitet, daß sie nützlich sein wird, jedoch OHNE IRGENDEINE GARANTIE, auch ohne die implizierte Garantie der MARKTREIFE oder der VERWENDBARKEIT FÜR EINEN BESTIMMTEN ZWECK. Mehr Details finden Sie in der GNU Lesser General Public License.
41 Sie sollten eine Kopie der GNU Lesser General Public License zusammen mit dieser Bibliothek erhalten haben; falls nicht, schreiben Sie an die Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA.
42 **/
43 package skrueger.geotools.selection;
44
45 import java.awt.RenderingHints;
46 import java.beans.PropertyChangeEvent;
47 import java.beans.PropertyChangeListener;
48 import java.io.File;
49 import java.util.HashMap;
50 import java.util.HashSet;
51 import java.util.Iterator;
52 import java.util.Set;
53 import java.util.Vector;
54
55 import javax.swing.JTable;
56 import javax.swing.ListSelectionModel;
57 import javax.swing.event.ListSelectionListener;
58
59 import org.geotools.feature.FeatureCollection;
60 import org.geotools.feature.FeatureIterator;
61 import org.geotools.filter.FidFilterImpl;
62 import org.geotools.map.MapLayer;
63 import org.geotools.renderer.GTRenderer;
64 import org.geotools.renderer.label.LabelCacheImpl;
65 import org.geotools.renderer.lite.StreamingRenderer;
66 import org.geotools.styling.FeatureTypeStyle;
67 import org.geotools.styling.Style;
68 import org.geotools.styling.visitor.DuplicatingStyleVisitor;
69 import org.opengis.feature.simple.SimpleFeature;
70 import org.opengis.feature.simple.SimpleFeatureType;
71 import org.opengis.filter.Filter;
72 import org.opengis.filter.Not;
73 import org.opengis.filter.identity.FeatureId;
74
75 import schmitzm.geotools.FilterUtil;
76 import schmitzm.geotools.GTUtil;
77 import schmitzm.geotools.gui.JMapPane;
78 import schmitzm.geotools.map.event.FeatureSelectedEvent;
79 import schmitzm.geotools.map.event.JMapPaneEvent;
80 import schmitzm.geotools.map.event.JMapPaneListener;
81 import schmitzm.geotools.styling.StylingUtil;
82 import skrueger.geotools.MapPaneToolBar;
83 import skrueger.geotools.StyledFeaturesInterface;
84
85 /**
86 * This class keeps the selection of a (feature) {@link JTable} synchronized
87 * with the {@link StyledLayerSelectionModel} of a layer. This is done by
88 * implementing:
89 * <ul>
90 * <li>a {@link PropertyChangeListener} which listens to the
91 * {@link StyledLayerSelectionModel} and accordingly changes the {@link JTable}
92 * selection</li>
93 * <li>a {@link ListSelectionListener} which listens to the {@link JTable} and
94 * accordingly changes the {@link StyledLayerSelectionModel} selection</li>
95 * </ul>
96 * After creating, the instance of this synchronizer must be added as listener
97 * to both, the {@link StyledLayerSelectionModel} and the table's
98 * {@link ListSelectionModel}.
99 *
100 * @author <a href="mailto:[email protected]">Martin Schmitz</a>
101 * (University of Bonn/Germany)
102 */
103 public class FeatureMapLayerSelectionSynchronizer extends
104 StyledLayerSelectionModelSynchronizer<StyledFeatureLayerSelectionModel>
105 implements JMapPaneListener {
106 public static final String SELECTION_STYLING = "SELECTION";
107 /**
108 * Holds the {@link MapLayer} to keep synchronized with the layer selection
109 * model.
110 */
111 protected final MapLayer mapLayer;
112 protected final StyledFeaturesInterface<?> styledLayer;
113 protected final JMapPane mapPane;
114 private final MapPaneToolBar toolBar;
115 private final HashMap<Object, Object> defaultGTRenderingHints;
116
117 /**
118 * Creates a new synchronizer
119 *
120 * @param layerSelModel
121 * layer selection model to keep synchronized with the
122 * {@link MapLayer}
123 *
124 * @param mapLayer
125 * {@link MapLayer} to keep synchronized with.
126 */
127 public FeatureMapLayerSelectionSynchronizer(
128 StyledFeatureLayerSelectionModel layerSelModel,
129 StyledFeaturesInterface<?> styledLayer, MapLayer mapLayer,
130 JMapPane mapPane, MapPaneToolBar toolBar,
131 HashMap<Object, Object> defaultGTRenderingHints) {
132
133 super(layerSelModel);
134 this.styledLayer = styledLayer;
135
136 this.mapLayer = mapLayer;
137 this.mapPane = mapPane;
138 this.toolBar = toolBar;
139 if (defaultGTRenderingHints != null)
140 this.defaultGTRenderingHints = defaultGTRenderingHints;
141 else
142 this.defaultGTRenderingHints = new HashMap<Object, Object>();
143 }
144
145 /**
146 * Called by {@link StyledLayerSelectionModel} when a the selection on other
147 * selection components (map, chart, ...) has changed. When calling this
148 *
149 * method changes the {@link MapLayer} selection according to the
150 * {@link StyledLayerSelectionModel} selection.
151 *
152 * @param evt
153 * an event
154 */
155 @Override
156 public void propertyChange(PropertyChangeEvent evt) {
157
158 if (!isEnabled())
159 return;
160
161 if (!(evt instanceof StyledLayerSelectionEvent))
162 return;
163 StyledLayerSelectionEvent selEvt = (StyledLayerSelectionEvent) evt;
164 // Only react on own layer and ignore events invoked by
165 // this component itself
166 if (selEvt.getEmitter() != layerSelModel || selectionChangeCausedByMe)
167 return;
168 // Apply new selection on table (except this event is one of
169 // some more events)
170 Vector<String> newSelection = layerSelModel.getSelection();
171 if (newSelection == null)
172 return;
173
174 // Avoid event circles in valueChanged(..)
175 selectionChangeCausedByMe = true;
176
177 changeLayerStyle(newSelection);
178
179 // Danger of event circles in valueChanged(..) banned
180 selectionChangeCausedByMe = false;
181 }
182
183 /**
184 * Changes the Style of the {@link MapLayer} to reflect changes of the
185 * selection.
186 *
187 * @param newSelection
188 * A {@link Vector} of SimpleFeature-IDs that are selected.
189 */
190 private void changeLayerStyle(final Vector<String> newSelection) {
191 try {
192
193 Style originalStyle = mapLayer.getStyle();
194
195 if (newSelection.isEmpty()) {
196
197 // Check if the Style contains a SELECTION FTS
198
199 FeatureTypeStyle[] clone = originalStyle.featureTypeStyles()
200 .toArray(new FeatureTypeStyle[] {}).clone();
201
202 for (FeatureTypeStyle fts : clone) {
203 if (fts.getName() != null
204 && fts.getName().equals(SELECTION_STYLING)) {
205 originalStyle.featureTypeStyles().remove(fts);
206
207 replaceRenderer();
208 // mapPane.refresh();
209
210 return;
211 }
212 }
213
214 } else {
215
216 LOGGER.debug("SELECTION .. change style");
217
218 // Saving a repaint if the selection didn't change
219 if (!selectionChanged(newSelection, originalStyle)) {
220 return;
221 }
222
223 // We take Style from the MapLayer that is displayed at the
224 // moment. We do not use the styledLayer.getStyle, because in
225 // the atlas, this always return the default style, but
226 // additional styles might be selected.
227 // Taking the style from the mapLayer indicated, that we have to
228 // remove any selection rules first.
229
230 FeatureTypeStyle selectionFTStyle = StylingUtil
231 .createSelectionStyle(styledLayer.getGeoObject());
232
233 selectionFTStyle.setName(SELECTION_STYLING);
234
235 /**
236 *
237 * Add a Filter to the selectionMapStyle, so that it is only
238 * used on objects that are selected. <br/>
239 * To optimize rendering speed, the filter checks whether more
240 * than half of the features are selected. If so, the filter is
241 * inverted to be shorter.
242 *
243 */
244 final FeatureCollection<SimpleFeatureType, SimpleFeature> featureCollectionFiltered = styledLayer
245 .getFeatureCollectionFiltered();
246 if (newSelection.size() > featureCollectionFiltered.size() / 2) {
247 FeatureIterator<SimpleFeature> iterator = featureCollectionFiltered
248 .features();
249 Set<FeatureId> antiFids = new HashSet<FeatureId>();
250 try {
251 while (iterator.hasNext()) {
252 SimpleFeature next = iterator.next();
253 if (!newSelection.contains(next.getID()))
254 antiFids.add(FilterUtil.FILTER_FAC2
255 .featureId(next.getID()));
256 }
257
258 selectionFTStyle.rules().get(0).setFilter(
259 FilterUtil.FILTER_FAC2
260 .not(FilterUtil.FILTER_FAC2
261 .id(antiFids)));
262
263 } finally {
264 featureCollectionFiltered.close(iterator);
265 }
266 } else {
267 Set<FeatureId> fids = new HashSet<FeatureId>();
268 for (String fid : newSelection) {
269 fids.add(FilterUtil.FILTER_FAC2.featureId(fid));
270 }
271
272 selectionFTStyle.rules().get(0).setFilter(
273 FilterUtil.FILTER_FAC2.id(fids));
274 }
275
276 // Maybe there has already been another selection
277 // FeatureTypeStyle... Let's replace it...
278 boolean foundAndReplaced = false;
279 for (FeatureTypeStyle fts : originalStyle.featureTypeStyles()) {
280 if (fts.getName() != null
281 && fts.getName().equals(SELECTION_STYLING)) {
282 foundAndReplaced = true;
283 fts.rules().clear();
284 fts.rules().addAll(selectionFTStyle.rules());
285 break;
286 }
287 }
288 if (!foundAndReplaced) {
289 originalStyle.featureTypeStyles().add(selectionFTStyle);
290 }
291
292 // Refresh the map
293 // mapPane.refresh();
294
295 DuplicatingStyleVisitor dsv = new DuplicatingStyleVisitor();
296 dsv.visit(originalStyle);
297 Style newStyle = (Style) dsv.getCopy();
298
299 // SK-Debug
300 try {
301 //
302 StylingUtil.saveStyleToSLD(newStyle, new File(
303 "/home/stefan/Desktop/selection.sld"));
304 } catch (Exception e) {
305 }
306
307 // DuplicatingStyleVisitor dsv = new DuplicatingStyleVisitor();
308 // dsv.visit(originalStyle);
309 // Style newStyle = originalStyle;
310
311 // Style newStyle = StylingUtil.STYLE_BUILDER.createStyle();
312 // newStyle.featureTypeStyles().addAll(originalStyle.featureTypeStyles());
313 mapLayer.setStyle(newStyle);
314
315 replaceRenderer();
316 }
317
318 } catch (Exception e) {
319 LOGGER.error("Error while trying to create a selection style", e);
320 }
321 }
322
323 /**
324 * Analyses whether the selection has changed in comparison to the selection
325 * stored in the mapLayer.Style
326 *
327 * @param newSelection
328 * @param originalStyle
329 * @return
330 */
331 private boolean selectionChanged(Vector<String> newSelection,
332 Style originalStyle) {
333
334 boolean SELECTION_STYLING_foundInMapStyle = false;
335
336 /**
337 * testing, whether the selection really changed. If not, we can save
338 * one paint!
339 */
340 for (FeatureTypeStyle fts : originalStyle.featureTypeStyles()) {
341
342 if (fts.getName() != null
343 && fts.getName().equals(SELECTION_STYLING)) {
344
345 SELECTION_STYLING_foundInMapStyle = true;
346
347 Filter filter = fts.rules().get(0).getFilter();
348 if (filter instanceof Not) {
349 FidFilterImpl antiOrigFidsFilter = (FidFilterImpl) ((Not) filter)
350 .getFilter();
351
352 // Check one way
353 final Set<String> antiFids = antiOrigFidsFilter
354 .getFidsSet();
355
356 if (antiFids.isEmpty() && !newSelection.isEmpty())
357 return true;
358
359 for (String fid : antiFids) {
360 if (newSelection.contains(fid)) {
361 return true;
362 }
363 }
364
365 // Check the other way
366 for (String fid : newSelection) {
367 if (antiFids.contains(fid)) {
368 return true;
369 }
370 }
371
372 } else {
373 FidFilterImpl origFidsFilter = (FidFilterImpl) filter;
374
375 // Check one way
376 final Set<String> fids = origFidsFilter.getFidsSet();
377
378 if (fids.isEmpty() && newSelection.isEmpty())
379 return false;
380 if (fids.size() != newSelection.size())
381 return true;
382
383 for (String fid : fids) {
384 if (!newSelection.contains(fid)) {
385 return true;
386 }
387 }
388
389 // Check the other way
390 for (String fid : newSelection) {
391 if (!fids.contains(fid)) {
392 return true;
393 }
394 }
395
396 }
397
398 break;
399 }
400 }
401
402 if (!SELECTION_STYLING_foundInMapStyle && !newSelection.isEmpty())
403 return true;
404
405 return false;
406 }
407
408 private void replaceRenderer() {
409 //
410 // // Has to be done before we apply the new Renderer
411 // mapLayer.setStyle(style);
412
413 GTRenderer oldRenderer = mapPane.getRenderer();
414
415 /**
416 * Explicitly putting a new instance of LabelCacheDefault into the
417 * renderer instance, so JMapPane doesn't reuse the old one. This is
418 * very useful when changing the TextSymbolizer with AtlasStyler<br/>
419 * SK 9.7.09: It's not enought to user LabelCache.clear(). We can not
420 * reuse the old Renderer - better to create a new one!
421 */
422 final GTRenderer newRenderer = GTUtil.createGTRenderer();
423
424 final HashMap<Object, Object> rendererHints = defaultGTRenderingHints;
425 rendererHints.put(StreamingRenderer.LABEL_CACHE_KEY,
426 new LabelCacheImpl());
427
428 newRenderer.setRendererHints(rendererHints);
429 mapPane.setRenderer(newRenderer);
430
431 if (oldRenderer != null) {
432
433 RenderingHints java2DHints = oldRenderer.getJava2DHints();
434 if (java2DHints != null) {
435 newRenderer.setJava2DHints(java2DHints);
436 }
437
438 oldRenderer.setContext(null);
439 oldRenderer = null;
440 }
441
442 mapPane.refresh();
443
444 }
445
446 /**
447 * Used to synchronize {@link FeatureSelectedEvent}s with the
448 * {@link StyledFeatureLayerSelectionModel}
449 */
450 @Override
451 public void performMapPaneEvent(JMapPaneEvent e) {
452
453 // Ignore event if it is caused by us or the synchronizer is disabled.
454 if (!isEnabled() || selectionChangeCausedByMe)
455 return;
456
457 if (!(e instanceof FeatureSelectedEvent)) {
458 // LOGGER.debug("Ignoring event " + e);
459 return;
460 }
461
462 /**
463 * Only listen to FeatureSelectedEvents if an appropriate tool is
464 * selected.
465 */
466 final int selectedTool = toolBar.getSelectedTool();
467 if (selectedTool != MapPaneToolBar.TOOL_SELECTION_ADD
468 && selectedTool != MapPaneToolBar.TOOL_SELECTION_REMOVE
469 && selectedTool != MapPaneToolBar.TOOL_SELECTION_SET) {
470 return;
471 }
472
473 // only listen to events directly coming from JMapPane
474 // selection (ignore selections from FilterDialog)
475 if (e.getSourceObject() != this.mapPane)
476 return;
477
478 /**
479 * Checking, that the FeatureSelectedEvent actually contains features
480 * from this layer
481 */
482 FeatureSelectedEvent fse = (FeatureSelectedEvent) e;
483 final String sourceID = fse.getSourceLayer().getTitle();
484 final String syncForID = mapLayer.getTitle();
485 if (sourceID != null && syncForID != null
486 && !sourceID.equals(syncForID)) {
487 LOGGER.debug("Ignoring a FeatureSelectedEvent from " + sourceID);
488 return;
489 }
490
491 // LOGGER.debug("do event " + fse);
492
493 // Avoid event circles in propertyChange(..)
494 selectionChangeCausedByMe = true;
495
496 final FeatureCollection<SimpleFeatureType, SimpleFeature> selectionResult = fse
497 .getSelectionResult();
498 Iterator<SimpleFeature> fi = selectionResult.iterator();
499 try {
500
501 // reset the selection of the DpLayerSelectionModel
502 // layerSelModel.setValueIsAdjusting(true);
503
504 if (selectedTool == MapPaneToolBar.TOOL_SELECTION_ADD) {
505 layerSelModel.setValueIsAdjusting(true);
506
507 for (int fIdx = 0; fi.hasNext(); fIdx++) {
508 SimpleFeature f = fi.next();
509 layerSelModel.addSelection(f.getID());
510 }
511 layerSelModel.setValueIsAdjusting(false);
512 } else if (selectedTool == MapPaneToolBar.TOOL_SELECTION_SET) {
513 layerSelModel.setValueIsAdjusting(true);
514 layerSelModel.clearSelection();
515
516 for (int fIdx = 0; fi.hasNext(); fIdx++) {
517 SimpleFeature f = fi.next();
518 layerSelModel.addSelection(f.getID());
519 }
520
521 // LOGGER.debug("Setting selection to " + fi.());
522
523 layerSelModel.setValueIsAdjusting(false);
524 } else if (selectedTool == MapPaneToolBar.TOOL_SELECTION_REMOVE) {
525 layerSelModel.setValueIsAdjusting(true);
526 for (int fIdx = 0; fi.hasNext(); fIdx++) {
527 SimpleFeature f = fi.next();
528 layerSelModel.removeSelection(f.getID());
529 }
530 layerSelModel.setValueIsAdjusting(false);
531 }
532
533 } finally {
534 selectionResult.close(fi);
535 }
536
537 // Show selected features in the map, which is not automatically done by
538 // the origin: FeatureSelectedEvent
539 changeLayerStyle(layerSelModel.getSelection());
540
541 // Danger of event circles in propertyChange(..) banned
542 selectionChangeCausedByMe = false;
543 }
544
545 }

Properties

Name Value
svn:eol-style native
svn:keywords Id
svn:mime-type text/plain

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26