/[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 607 - (hide annotations)
Wed Dec 9 15:13:42 2009 UTC (15 years, 2 months ago) by alfonx
Original Path: branches/2.0-RC1/src/skrueger/geotools/selection/FeatureMapLayerSelectionSynchronizer.java
File MIME type: text/plain
File size: 17132 byte(s)
Keine Ahnung was er da gebrancht hat.. der stand der dateien war weder trunk, noch der 1.0-gt26 branch... ich hab die dateien jetzt händisch auf den richtigen stand gebracht und comitte

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