/[schmitzm]/trunk/src/skrueger/geotools/selection/FeatureMapLayerSelectionSynchronizer.java
ViewVC logotype

Annotation of /trunk/src/skrueger/geotools/selection/FeatureMapLayerSelectionSynchronizer.java

Parent Directory Parent Directory | Revision Log Revision Log


Revision 500 - (hide annotations)
Sun Oct 25 16:56:21 2009 UTC (15 years, 4 months ago) by alfonx
Original Path: branches/1.0-gt2-2.6/src/skrueger/geotools/selection/FeatureMapLayerSelectionSynchronizer.java
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 alfonx 244 /*******************************************************************************
2     * Copyright (c) 2009 Martin O. J. Schmitz.
3     *
4     * This file is part of the SCHMITZM library - a collection of utility
5 alfonx 256 * classes based on Java 1.6, focusing (not only) on Java Swing
6 alfonx 244 * 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 alfonx 97 /**
31     Copyright 2008 Stefan Alfons Krüger
32 alfonx 409
33 alfonx 97 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 alfonx 419 import java.awt.RenderingHints;
46 alfonx 97 import java.beans.PropertyChangeEvent;
47     import java.beans.PropertyChangeListener;
48 alfonx 489 import java.io.File;
49 alfonx 419 import java.util.HashMap;
50 alfonx 158 import java.util.HashSet;
51 alfonx 409 import java.util.Iterator;
52 alfonx 158 import java.util.Set;
53 alfonx 97 import java.util.Vector;
54    
55     import javax.swing.JTable;
56     import javax.swing.ListSelectionModel;
57     import javax.swing.event.ListSelectionListener;
58    
59 alfonx 455 import org.geotools.feature.FeatureCollection;
60 alfonx 489 import org.geotools.feature.FeatureIterator;
61 alfonx 500 import org.geotools.filter.FidFilterImpl;
62 alfonx 97 import org.geotools.map.MapLayer;
63 alfonx 419 import org.geotools.renderer.GTRenderer;
64     import org.geotools.renderer.label.LabelCacheImpl;
65     import org.geotools.renderer.lite.StreamingRenderer;
66 alfonx 123 import org.geotools.styling.FeatureTypeStyle;
67 alfonx 97 import org.geotools.styling.Style;
68 alfonx 419 import org.geotools.styling.visitor.DuplicatingStyleVisitor;
69 mojays 325 import org.opengis.feature.simple.SimpleFeature;
70 alfonx 455 import org.opengis.feature.simple.SimpleFeatureType;
71 alfonx 500 import org.opengis.filter.Filter;
72     import org.opengis.filter.Not;
73 alfonx 158 import org.opengis.filter.identity.FeatureId;
74 alfonx 97
75 alfonx 158 import schmitzm.geotools.FilterUtil;
76 alfonx 438 import schmitzm.geotools.GTUtil;
77 alfonx 97 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 alfonx 111 import skrueger.geotools.MapPaneToolBar;
83 alfonx 489 import skrueger.geotools.StyledFeaturesInterface;
84 alfonx 97
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 alfonx 106 StyledLayerSelectionModelSynchronizer<StyledFeatureLayerSelectionModel>
105     implements JMapPaneListener {
106 alfonx 111 public static final String SELECTION_STYLING = "SELECTION";
107 alfonx 97 /**
108     * Holds the {@link MapLayer} to keep synchronized with the layer selection
109     * model.
110     */
111     protected final MapLayer mapLayer;
112 alfonx 489 protected final StyledFeaturesInterface<?> styledLayer;
113 alfonx 97 protected final JMapPane mapPane;
114 alfonx 111 private final MapPaneToolBar toolBar;
115 alfonx 419 private final HashMap<Object, Object> defaultGTRenderingHints;
116 alfonx 97
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 alfonx 489 StyledFeaturesInterface<?> styledLayer, MapLayer mapLayer,
130 alfonx 455 JMapPane mapPane, MapPaneToolBar toolBar,
131     HashMap<Object, Object> defaultGTRenderingHints) {
132 alfonx 97
133     super(layerSelModel);
134 alfonx 224 this.styledLayer = styledLayer;
135 alfonx 97
136     this.mapLayer = mapLayer;
137     this.mapPane = mapPane;
138 alfonx 111 this.toolBar = toolBar;
139 alfonx 419 if (defaultGTRenderingHints != null)
140     this.defaultGTRenderingHints = defaultGTRenderingHints;
141 alfonx 455 else
142 alfonx 419 this.defaultGTRenderingHints = new HashMap<Object, Object>();
143 alfonx 97 }
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 alfonx 151
158     if (!isEnabled())
159     return;
160    
161 alfonx 97 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 alfonx 111 Vector<String> newSelection = layerSelModel.getSelection();
171 alfonx 97 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 alfonx 151 /**
184     * Changes the Style of the {@link MapLayer} to reflect changes of the
185     * selection.
186     *
187     * @param newSelection
188 alfonx 318 * A {@link Vector} of SimpleFeature-IDs that are selected.
189 alfonx 151 */
190 alfonx 111 private void changeLayerStyle(final Vector<String> newSelection) {
191 alfonx 97 try {
192 alfonx 455
193 alfonx 412 Style originalStyle = mapLayer.getStyle();
194 alfonx 500
195 alfonx 97 if (newSelection.isEmpty()) {
196    
197 alfonx 412 // Check if the Style contains a SELECTION FTS
198 alfonx 455
199     FeatureTypeStyle[] clone = originalStyle.featureTypeStyles()
200     .toArray(new FeatureTypeStyle[] {}).clone();
201    
202 alfonx 412 for (FeatureTypeStyle fts : clone) {
203     if (fts.getName() != null
204     && fts.getName().equals(SELECTION_STYLING)) {
205     originalStyle.featureTypeStyles().remove(fts);
206 alfonx 455
207 alfonx 419 replaceRenderer();
208 alfonx 455 // mapPane.refresh();
209 alfonx 106
210 alfonx 412 return;
211     }
212     }
213    
214 alfonx 97 } else {
215 alfonx 500
216 alfonx 100 LOGGER.debug("SELECTION .. change style");
217 alfonx 106
218 alfonx 500 // Saving a repaint if the selection didn't change
219     if (!selectionChanged(newSelection, originalStyle)) {
220     return;
221     }
222    
223 alfonx 123 // We take Style from the MapLayer that is displayed at the
224 alfonx 224 // moment. We do not use the styledLayer.getStyle, because in
225 alfonx 123 // the atlas, this always return the default style, but
226 alfonx 224 // additional styles might be selected.
227 alfonx 123 // Taking the style from the mapLayer indicated, that we have to
228 alfonx 125 // remove any selection rules first.
229 alfonx 101
230 alfonx 412 FeatureTypeStyle selectionFTStyle = StylingUtil
231 alfonx 224 .createSelectionStyle(styledLayer.getGeoObject());
232 alfonx 106
233 alfonx 412 selectionFTStyle.setName(SELECTION_STYLING);
234 alfonx 123
235 alfonx 158 /**
236     *
237     * Add a Filter to the selectionMapStyle, so that it is only
238     * used on objects that are selected. <br/>
239 alfonx 489 * 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 alfonx 158 *
243     */
244 alfonx 489 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 alfonx 158 }
275 alfonx 106
276 alfonx 412 // 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 alfonx 123 }
288 alfonx 412 if (!foundAndReplaced) {
289     originalStyle.featureTypeStyles().add(selectionFTStyle);
290     }
291 alfonx 151
292 alfonx 412 // Refresh the map
293 alfonx 455 // mapPane.refresh();
294    
295 alfonx 419 DuplicatingStyleVisitor dsv = new DuplicatingStyleVisitor();
296     dsv.visit(originalStyle);
297 alfonx 455 Style newStyle = (Style) dsv.getCopy();
298 mojays 493
299 alfonx 500 // SK-Debug
300     try {
301     //
302     StylingUtil.saveStyleToSLD(newStyle, new File(
303     "/home/stefan/Desktop/selection.sld"));
304     } catch (Exception e) {
305     }
306 mojays 493
307     // DuplicatingStyleVisitor dsv = new DuplicatingStyleVisitor();
308 alfonx 489 // dsv.visit(originalStyle);
309     // Style newStyle = originalStyle;
310    
311 alfonx 455 // Style newStyle = StylingUtil.STYLE_BUILDER.createStyle();
312     // newStyle.featureTypeStyles().addAll(originalStyle.featureTypeStyles());
313 alfonx 419 mapLayer.setStyle(newStyle);
314 alfonx 455
315 alfonx 419 replaceRenderer();
316 alfonx 97 }
317    
318     } catch (Exception e) {
319     LOGGER.error("Error while trying to create a selection style", e);
320     }
321     }
322    
323 alfonx 500 /**
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 alfonx 419 private void replaceRenderer() {
409 alfonx 455 //
410     // // Has to be done before we apply the new Renderer
411     // mapLayer.setStyle(style);
412 alfonx 419
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 alfonx 440 final GTRenderer newRenderer = GTUtil.createGTRenderer();
423 alfonx 419
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 alfonx 455
433 alfonx 419 RenderingHints java2DHints = oldRenderer.getJava2DHints();
434     if (java2DHints != null) {
435 alfonx 427 newRenderer.setJava2DHints(java2DHints);
436 alfonx 419 }
437 alfonx 455
438 alfonx 419 oldRenderer.setContext(null);
439     oldRenderer = null;
440     }
441 alfonx 455
442 alfonx 419 mapPane.refresh();
443    
444     }
445    
446 alfonx 151 /**
447     * Used to synchronize {@link FeatureSelectedEvent}s with the
448     * {@link StyledFeatureLayerSelectionModel}
449     */
450 alfonx 97 @Override
451     public void performMapPaneEvent(JMapPaneEvent e) {
452 alfonx 106
453 alfonx 151 // 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 alfonx 111 /**
463 alfonx 151 * Only listen to FeatureSelectedEvents if an appropriate tool is
464 alfonx 111 * 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 mojays 109 // only listen to events directly coming from JMapPane
474     // selection (ignore selections from FilterDialog)
475 alfonx 111 if (e.getSourceObject() != this.mapPane)
476     return;
477    
478 alfonx 151 /**
479 alfonx 158 * Checking, that the FeatureSelectedEvent actually contains features
480     * from this layer
481 alfonx 151 */
482 alfonx 492 FeatureSelectedEvent fse = (FeatureSelectedEvent) e;
483 alfonx 151 final String sourceID = fse.getSourceLayer().getTitle();
484     final String syncForID = mapLayer.getTitle();
485     if (sourceID != null && syncForID != null
486 alfonx 158 && !sourceID.equals(syncForID)) {
487     LOGGER.debug("Ignoring a FeatureSelectedEvent from " + sourceID);
488 alfonx 97 return;
489 alfonx 151 }
490 alfonx 97
491 alfonx 489 // LOGGER.debug("do event " + fse);
492 alfonx 111
493 alfonx 97 // Avoid event circles in propertyChange(..)
494     selectionChangeCausedByMe = true;
495    
496 alfonx 455 final FeatureCollection<SimpleFeatureType, SimpleFeature> selectionResult = fse
497     .getSelectionResult();
498     Iterator<SimpleFeature> fi = selectionResult.iterator();
499     try {
500 alfonx 97
501 alfonx 455 // reset the selection of the DpLayerSelectionModel
502     // layerSelModel.setValueIsAdjusting(true);
503 alfonx 97
504 alfonx 455 if (selectedTool == MapPaneToolBar.TOOL_SELECTION_ADD) {
505     layerSelModel.setValueIsAdjusting(true);
506 alfonx 409
507 alfonx 455 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 alfonx 97
516 alfonx 455 for (int fIdx = 0; fi.hasNext(); fIdx++) {
517     SimpleFeature f = fi.next();
518     layerSelModel.addSelection(f.getID());
519     }
520 alfonx 111
521 alfonx 455 // LOGGER.debug("Setting selection to " + fi.());
522 alfonx 409
523 alfonx 455 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 alfonx 111 }
532 alfonx 455
533     } finally {
534     selectionResult.close(fi);
535 alfonx 151 }
536 alfonx 111
537     // Show selected features in the map, which is not automatically done by
538     // the origin: FeatureSelectedEvent
539 alfonx 97 changeLayerStyle(layerSelModel.getSelection());
540 alfonx 106
541 alfonx 97 // Danger of event circles in propertyChange(..) banned
542     selectionChangeCausedByMe = false;
543     }
544 alfonx 151
545 alfonx 97 }

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