/[schmitzm]/branches/2.0-GP14/src/skrueger/geotools/selection/FeatureMapLayerSelectionSynchronizer.java
ViewVC logotype

Annotation of /branches/2.0-GP14/src/skrueger/geotools/selection/FeatureMapLayerSelectionSynchronizer.java

Parent Directory Parent Directory | Revision Log Revision Log


Revision 560 - (hide annotations)
Tue Nov 24 14:57:05 2009 UTC (15 years, 3 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: 17368 byte(s)
* Selection mit Click funktioniert wieder
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 559 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 559 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 559 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 559 import schmitzm.geotools.GTUtil;
77 alfonx 509 import schmitzm.geotools.gui.SelectableXMapPane;
78 alfonx 97 import schmitzm.geotools.map.event.FeatureSelectedEvent;
79 alfonx 559 import schmitzm.geotools.map.event.JMapPaneListener;
80 alfonx 509 import schmitzm.geotools.map.event.MapPaneEvent;
81 alfonx 97 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 516
107 alfonx 97 /**
108 alfonx 516 * This constant is set as the {@link FeatureTypeStyle#getName()} attribute
109     * in the {@link FeatureTypeStyle}s that only exist to present the selected
110     * features
111     **/
112     public static final String SELECTION_STYLING_FTS_NAME = "SELECTION";
113    
114     /**
115 alfonx 97 * Holds the {@link MapLayer} to keep synchronized with the layer selection
116     * model.
117     */
118     protected final MapLayer mapLayer;
119 alfonx 489 protected final StyledFeaturesInterface<?> styledLayer;
120 alfonx 509 protected final SelectableXMapPane mapPane;
121 alfonx 111 private final MapPaneToolBar toolBar;
122 alfonx 97
123     /**
124     * Creates a new synchronizer
125     *
126     * @param layerSelModel
127     * layer selection model to keep synchronized with the
128     * {@link MapLayer}
129     *
130     * @param mapLayer
131     * {@link MapLayer} to keep synchronized with.
132     */
133     public FeatureMapLayerSelectionSynchronizer(
134     StyledFeatureLayerSelectionModel layerSelModel,
135 alfonx 489 StyledFeaturesInterface<?> styledLayer, MapLayer mapLayer,
136 alfonx 509 SelectableXMapPane mapPane, MapPaneToolBar toolBar) {
137 alfonx 97
138     super(layerSelModel);
139 alfonx 224 this.styledLayer = styledLayer;
140 alfonx 97
141     this.mapLayer = mapLayer;
142     this.mapPane = mapPane;
143 alfonx 111 this.toolBar = toolBar;
144 alfonx 97 }
145    
146     /**
147     * Called by {@link StyledLayerSelectionModel} when a the selection on other
148     * selection components (map, chart, ...) has changed. When calling this
149     *
150     * method changes the {@link MapLayer} selection according to the
151     * {@link StyledLayerSelectionModel} selection.
152     *
153     * @param evt
154     * an event
155     */
156     @Override
157     public void propertyChange(PropertyChangeEvent evt) {
158 alfonx 151
159     if (!isEnabled())
160     return;
161    
162 alfonx 97 if (!(evt instanceof StyledLayerSelectionEvent))
163     return;
164     StyledLayerSelectionEvent selEvt = (StyledLayerSelectionEvent) evt;
165     // Only react on own layer and ignore events invoked by
166     // this component itself
167     if (selEvt.getEmitter() != layerSelModel || selectionChangeCausedByMe)
168     return;
169     // Apply new selection on table (except this event is one of
170     // some more events)
171 alfonx 111 Vector<String> newSelection = layerSelModel.getSelection();
172 alfonx 97 if (newSelection == null)
173     return;
174    
175     // Avoid event circles in valueChanged(..)
176     selectionChangeCausedByMe = true;
177    
178     changeLayerStyle(newSelection);
179    
180     // Danger of event circles in valueChanged(..) banned
181     selectionChangeCausedByMe = false;
182     }
183    
184 alfonx 151 /**
185     * Changes the Style of the {@link MapLayer} to reflect changes of the
186     * selection.
187     *
188     * @param newSelection
189 alfonx 318 * A {@link Vector} of SimpleFeature-IDs that are selected.
190 alfonx 151 */
191 alfonx 111 private void changeLayerStyle(final Vector<String> newSelection) {
192 alfonx 97 try {
193 alfonx 455
194 alfonx 412 Style originalStyle = mapLayer.getStyle();
195 alfonx 500
196 alfonx 97 if (newSelection.isEmpty()) {
197    
198 alfonx 412 // Check if the Style contains a SELECTION FTS
199 alfonx 455
200     FeatureTypeStyle[] clone = originalStyle.featureTypeStyles()
201     .toArray(new FeatureTypeStyle[] {}).clone();
202    
203 alfonx 412 for (FeatureTypeStyle fts : clone) {
204     if (fts.getName() != null
205 alfonx 516 && fts.getName().equals(SELECTION_STYLING_FTS_NAME)) {
206 alfonx 412 originalStyle.featureTypeStyles().remove(fts);
207 alfonx 455
208 alfonx 559 mapLayer.setStyle(originalStyle);
209     // replaceRenderer();
210     // // mapPane.refresh();
211 alfonx 106
212 alfonx 412 return;
213     }
214     }
215    
216 alfonx 97 } else {
217 alfonx 500
218 alfonx 100 LOGGER.debug("SELECTION .. change style");
219 alfonx 106
220 alfonx 500 // Saving a repaint if the selection didn't change
221     if (!selectionChanged(newSelection, originalStyle)) {
222     return;
223     }
224    
225 alfonx 123 // We take Style from the MapLayer that is displayed at the
226 alfonx 224 // moment. We do not use the styledLayer.getStyle, because in
227 alfonx 123 // the atlas, this always return the default style, but
228 alfonx 224 // additional styles might be selected.
229 alfonx 123 // Taking the style from the mapLayer indicated, that we have to
230 alfonx 125 // remove any selection rules first.
231 alfonx 101
232 alfonx 412 FeatureTypeStyle selectionFTStyle = StylingUtil
233 alfonx 224 .createSelectionStyle(styledLayer.getGeoObject());
234 alfonx 106
235 alfonx 516 selectionFTStyle.setName(SELECTION_STYLING_FTS_NAME);
236 alfonx 123
237 alfonx 158 /**
238     *
239     * Add a Filter to the selectionMapStyle, so that it is only
240     * used on objects that are selected. <br/>
241 alfonx 489 * To optimize rendering speed, the filter checks whether more
242     * than half of the features are selected. If so, the filter is
243     * inverted to be shorter.
244 alfonx 158 *
245     */
246 alfonx 489 final FeatureCollection<SimpleFeatureType, SimpleFeature> featureCollectionFiltered = styledLayer
247     .getFeatureCollectionFiltered();
248     if (newSelection.size() > featureCollectionFiltered.size() / 2) {
249     FeatureIterator<SimpleFeature> iterator = featureCollectionFiltered
250     .features();
251     Set<FeatureId> antiFids = new HashSet<FeatureId>();
252     try {
253     while (iterator.hasNext()) {
254     SimpleFeature next = iterator.next();
255     if (!newSelection.contains(next.getID()))
256     antiFids.add(FilterUtil.FILTER_FAC2
257     .featureId(next.getID()));
258     }
259    
260     selectionFTStyle.rules().get(0).setFilter(
261     FilterUtil.FILTER_FAC2
262     .not(FilterUtil.FILTER_FAC2
263     .id(antiFids)));
264    
265     } finally {
266     featureCollectionFiltered.close(iterator);
267     }
268     } else {
269     Set<FeatureId> fids = new HashSet<FeatureId>();
270     for (String fid : newSelection) {
271     fids.add(FilterUtil.FILTER_FAC2.featureId(fid));
272     }
273    
274     selectionFTStyle.rules().get(0).setFilter(
275     FilterUtil.FILTER_FAC2.id(fids));
276 alfonx 158 }
277 alfonx 106
278 alfonx 412 // Maybe there has already been another selection
279     // FeatureTypeStyle... Let's replace it...
280     boolean foundAndReplaced = false;
281     for (FeatureTypeStyle fts : originalStyle.featureTypeStyles()) {
282     if (fts.getName() != null
283 alfonx 516 && fts.getName().equals(SELECTION_STYLING_FTS_NAME)) {
284 alfonx 412 foundAndReplaced = true;
285     fts.rules().clear();
286     fts.rules().addAll(selectionFTStyle.rules());
287     break;
288     }
289 alfonx 123 }
290 alfonx 412 if (!foundAndReplaced) {
291     originalStyle.featureTypeStyles().add(selectionFTStyle);
292     }
293 alfonx 151
294 alfonx 412 // Refresh the map
295 alfonx 455 // mapPane.refresh();
296    
297 alfonx 419 DuplicatingStyleVisitor dsv = new DuplicatingStyleVisitor();
298     dsv.visit(originalStyle);
299 alfonx 455 Style newStyle = (Style) dsv.getCopy();
300 mojays 493
301 alfonx 500 // SK-Debug
302     try {
303     //
304     StylingUtil.saveStyleToSLD(newStyle, new File(
305     "/home/stefan/Desktop/selection.sld"));
306     } catch (Exception e) {
307     }
308 mojays 493
309     // DuplicatingStyleVisitor dsv = new DuplicatingStyleVisitor();
310 alfonx 489 // dsv.visit(originalStyle);
311     // Style newStyle = originalStyle;
312    
313 alfonx 455 // Style newStyle = StylingUtil.STYLE_BUILDER.createStyle();
314     // newStyle.featureTypeStyles().addAll(originalStyle.featureTypeStyles());
315 alfonx 419 mapLayer.setStyle(newStyle);
316 alfonx 455
317 alfonx 419 replaceRenderer();
318 alfonx 97 }
319    
320     } catch (Exception e) {
321     LOGGER.error("Error while trying to create a selection style", e);
322     }
323     }
324    
325 alfonx 500 /**
326 alfonx 560 * Analyzes whether the selection has changed in comparison to the selection
327 alfonx 500 * stored in the mapLayer.Style
328     *
329 alfonx 560 * @param newSelection a List<String> of all newly selected FIDs
330     * @param originalStyle the original {@link Style} that has an earlier selection coded into the {@link Style}
331     * @return <code>true</code> if changed.
332 alfonx 500 */
333     private boolean selectionChanged(Vector<String> newSelection,
334     Style originalStyle) {
335    
336     boolean SELECTION_STYLING_foundInMapStyle = false;
337    
338     /**
339     * testing, whether the selection really changed. If not, we can save
340 alfonx 559 * one rendering!
341 alfonx 500 */
342     for (FeatureTypeStyle fts : originalStyle.featureTypeStyles()) {
343    
344     if (fts.getName() != null
345 alfonx 516 && fts.getName().equals(SELECTION_STYLING_FTS_NAME)) {
346 alfonx 500
347     SELECTION_STYLING_foundInMapStyle = true;
348    
349     Filter filter = fts.rules().get(0).getFilter();
350     if (filter instanceof Not) {
351     FidFilterImpl antiOrigFidsFilter = (FidFilterImpl) ((Not) filter)
352     .getFilter();
353    
354     // Check one way
355     final Set<String> antiFids = antiOrigFidsFilter
356     .getFidsSet();
357    
358     if (antiFids.isEmpty() && !newSelection.isEmpty())
359     return true;
360    
361     for (String fid : antiFids) {
362     if (newSelection.contains(fid)) {
363     return true;
364     }
365     }
366    
367     // Check the other way
368     for (String fid : newSelection) {
369     if (antiFids.contains(fid)) {
370     return true;
371     }
372     }
373    
374 alfonx 559 if (antiFids.size() + newSelection.size() != styledLayer
375     .getFeatureCollectionFiltered().size())
376     return true;
377    
378 alfonx 500 } else {
379     FidFilterImpl origFidsFilter = (FidFilterImpl) filter;
380    
381     // Check one way
382     final Set<String> fids = origFidsFilter.getFidsSet();
383    
384     if (fids.isEmpty() && newSelection.isEmpty())
385     return false;
386     if (fids.size() != newSelection.size())
387     return true;
388    
389     for (String fid : fids) {
390     if (!newSelection.contains(fid)) {
391     return true;
392     }
393     }
394    
395     // Check the other way
396     for (String fid : newSelection) {
397     if (!fids.contains(fid)) {
398     return true;
399     }
400     }
401    
402     }
403    
404     break;
405     }
406     }
407    
408     if (!SELECTION_STYLING_foundInMapStyle && !newSelection.isEmpty())
409     return true;
410    
411     return false;
412     }
413    
414 alfonx 509 /**
415 alfonx 516 * Replaces the local renderer
416 alfonx 509 */
417 alfonx 419 private void replaceRenderer() {
418     }
419    
420 alfonx 151 /**
421     * Used to synchronize {@link FeatureSelectedEvent}s with the
422     * {@link StyledFeatureLayerSelectionModel}
423     */
424 alfonx 97 @Override
425 alfonx 509 public void performMapPaneEvent(MapPaneEvent e) {
426 alfonx 106
427 alfonx 151 // Ignore event if it is caused by us or the synchronizer is disabled.
428     if (!isEnabled() || selectionChangeCausedByMe)
429     return;
430    
431     if (!(e instanceof FeatureSelectedEvent)) {
432     // LOGGER.debug("Ignoring event " + e);
433     return;
434     }
435    
436 alfonx 111 /**
437 alfonx 151 * Only listen to FeatureSelectedEvents if an appropriate tool is
438 alfonx 111 * selected.
439     */
440     final int selectedTool = toolBar.getSelectedTool();
441     if (selectedTool != MapPaneToolBar.TOOL_SELECTION_ADD
442     && selectedTool != MapPaneToolBar.TOOL_SELECTION_REMOVE
443     && selectedTool != MapPaneToolBar.TOOL_SELECTION_SET) {
444     return;
445     }
446    
447 mojays 109 // only listen to events directly coming from JMapPane
448     // selection (ignore selections from FilterDialog)
449 alfonx 111 if (e.getSourceObject() != this.mapPane)
450     return;
451    
452 alfonx 151 /**
453 alfonx 158 * Checking, that the FeatureSelectedEvent actually contains features
454     * from this layer
455 alfonx 151 */
456 alfonx 492 FeatureSelectedEvent fse = (FeatureSelectedEvent) e;
457 alfonx 151 final String sourceID = fse.getSourceLayer().getTitle();
458     final String syncForID = mapLayer.getTitle();
459     if (sourceID != null && syncForID != null
460 alfonx 158 && !sourceID.equals(syncForID)) {
461     LOGGER.debug("Ignoring a FeatureSelectedEvent from " + sourceID);
462 alfonx 97 return;
463 alfonx 151 }
464 alfonx 97
465 alfonx 489 // LOGGER.debug("do event " + fse);
466 alfonx 111
467 alfonx 97 // Avoid event circles in propertyChange(..)
468     selectionChangeCausedByMe = true;
469    
470 alfonx 455 final FeatureCollection<SimpleFeatureType, SimpleFeature> selectionResult = fse
471     .getSelectionResult();
472     Iterator<SimpleFeature> fi = selectionResult.iterator();
473     try {
474 alfonx 97
475 alfonx 455 // reset the selection of the DpLayerSelectionModel
476     // layerSelModel.setValueIsAdjusting(true);
477 alfonx 97
478 alfonx 455 if (selectedTool == MapPaneToolBar.TOOL_SELECTION_ADD) {
479     layerSelModel.setValueIsAdjusting(true);
480 alfonx 409
481 alfonx 455 for (int fIdx = 0; fi.hasNext(); fIdx++) {
482     SimpleFeature f = fi.next();
483     layerSelModel.addSelection(f.getID());
484     }
485     layerSelModel.setValueIsAdjusting(false);
486     } else if (selectedTool == MapPaneToolBar.TOOL_SELECTION_SET) {
487     layerSelModel.setValueIsAdjusting(true);
488     layerSelModel.clearSelection();
489 alfonx 97
490 alfonx 455 for (int fIdx = 0; fi.hasNext(); fIdx++) {
491     SimpleFeature f = fi.next();
492     layerSelModel.addSelection(f.getID());
493     }
494 alfonx 111
495 alfonx 455 // LOGGER.debug("Setting selection to " + fi.());
496 alfonx 409
497 alfonx 455 layerSelModel.setValueIsAdjusting(false);
498     } else if (selectedTool == MapPaneToolBar.TOOL_SELECTION_REMOVE) {
499     layerSelModel.setValueIsAdjusting(true);
500     for (int fIdx = 0; fi.hasNext(); fIdx++) {
501     SimpleFeature f = fi.next();
502     layerSelModel.removeSelection(f.getID());
503     }
504     layerSelModel.setValueIsAdjusting(false);
505 alfonx 111 }
506 alfonx 455
507     } finally {
508     selectionResult.close(fi);
509 alfonx 151 }
510 alfonx 111
511     // Show selected features in the map, which is not automatically done by
512     // the origin: FeatureSelectedEvent
513 alfonx 97 changeLayerStyle(layerSelModel.getSelection());
514 alfonx 106
515 alfonx 97 // Danger of event circles in propertyChange(..) banned
516     selectionChangeCausedByMe = false;
517     }
518 alfonx 151
519 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