/[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 946 - (hide annotations)
Mon Jul 26 17:13:42 2010 UTC (14 years, 7 months ago) by alfonx
File MIME type: text/plain
File size: 17322 byte(s)
Kleine Aufräumarbeiten... 
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 alfonx 862 * Stefan A. Tzeggai - additional utility classes
29 alfonx 244 ******************************************************************************/
30 alfonx 97 /**
31 alfonx 862 Copyright 2008 Stefan Alfons Tzeggai
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 740 import schmitzm.geotools.gui.XMapPaneEvent;
73 alfonx 97 import schmitzm.geotools.map.event.FeatureSelectedEvent;
74 alfonx 559 import schmitzm.geotools.map.event.JMapPaneListener;
75 alfonx 97 import schmitzm.geotools.styling.StylingUtil;
76 alfonx 946 import schmitzm.geotools.styling.StylingUtil.SelectionStylesTypes;
77 alfonx 111 import skrueger.geotools.MapPaneToolBar;
78 alfonx 489 import skrueger.geotools.StyledFeaturesInterface;
79 alfonx 97
80     /**
81     * This class keeps the selection of a (feature) {@link JTable} synchronized
82     * with the {@link StyledLayerSelectionModel} of a layer. This is done by
83     * implementing:
84     * <ul>
85     * <li>a {@link PropertyChangeListener} which listens to the
86     * {@link StyledLayerSelectionModel} and accordingly changes the {@link JTable}
87     * selection</li>
88     * <li>a {@link ListSelectionListener} which listens to the {@link JTable} and
89     * accordingly changes the {@link StyledLayerSelectionModel} selection</li>
90     * </ul>
91     * After creating, the instance of this synchronizer must be added as listener
92     * to both, the {@link StyledLayerSelectionModel} and the table's
93     * {@link ListSelectionModel}.
94     *
95     * @author <a href="mailto:[email protected]">Martin Schmitz</a>
96     * (University of Bonn/Germany)
97     */
98     public class FeatureMapLayerSelectionSynchronizer extends
99 alfonx 106 StyledLayerSelectionModelSynchronizer<StyledFeatureLayerSelectionModel>
100     implements JMapPaneListener {
101 alfonx 516
102 alfonx 97 /**
103 alfonx 516 * This constant is set as the {@link FeatureTypeStyle#getName()} attribute
104     * in the {@link FeatureTypeStyle}s that only exist to present the selected
105     * features
106     **/
107     public static final String SELECTION_STYLING_FTS_NAME = "SELECTION";
108    
109     /**
110 alfonx 97 * Holds the {@link MapLayer} to keep synchronized with the layer selection
111     * model.
112     */
113     protected final MapLayer mapLayer;
114 alfonx 489 protected final StyledFeaturesInterface<?> styledLayer;
115 alfonx 509 protected final SelectableXMapPane mapPane;
116 alfonx 111 private final MapPaneToolBar toolBar;
117 alfonx 97
118     /**
119     * Creates a new synchronizer
120     *
121     * @param layerSelModel
122     * layer selection model to keep synchronized with the
123     * {@link MapLayer}
124     *
125     * @param mapLayer
126     * {@link MapLayer} to keep synchronized with.
127     */
128     public FeatureMapLayerSelectionSynchronizer(
129     StyledFeatureLayerSelectionModel layerSelModel,
130 alfonx 489 StyledFeaturesInterface<?> styledLayer, MapLayer mapLayer,
131 alfonx 509 SelectableXMapPane mapPane, MapPaneToolBar toolBar) {
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 97 }
140    
141     /**
142     * Called by {@link StyledLayerSelectionModel} when a the selection on other
143     * selection components (map, chart, ...) has changed. When calling this
144     *
145     * method changes the {@link MapLayer} selection according to the
146     * {@link StyledLayerSelectionModel} selection.
147     *
148     * @param evt
149     * an event
150     */
151     @Override
152     public void propertyChange(PropertyChangeEvent evt) {
153 alfonx 151
154     if (!isEnabled())
155     return;
156    
157 alfonx 97 if (!(evt instanceof StyledLayerSelectionEvent))
158     return;
159     StyledLayerSelectionEvent selEvt = (StyledLayerSelectionEvent) evt;
160     // Only react on own layer and ignore events invoked by
161     // this component itself
162     if (selEvt.getEmitter() != layerSelModel || selectionChangeCausedByMe)
163     return;
164     // Apply new selection on table (except this event is one of
165     // some more events)
166 alfonx 111 Vector<String> newSelection = layerSelModel.getSelection();
167 alfonx 97 if (newSelection == null)
168     return;
169    
170     // Avoid event circles in valueChanged(..)
171     selectionChangeCausedByMe = true;
172    
173     changeLayerStyle(newSelection);
174    
175     // Danger of event circles in valueChanged(..) banned
176     selectionChangeCausedByMe = false;
177     }
178    
179 alfonx 151 /**
180     * Changes the Style of the {@link MapLayer} to reflect changes of the
181     * selection.
182     *
183     * @param newSelection
184 alfonx 318 * A {@link Vector} of SimpleFeature-IDs that are selected.
185 alfonx 151 */
186 alfonx 111 private void changeLayerStyle(final Vector<String> newSelection) {
187 alfonx 97 try {
188 alfonx 455
189 alfonx 412 Style originalStyle = mapLayer.getStyle();
190 alfonx 500
191 alfonx 97 if (newSelection.isEmpty()) {
192    
193 alfonx 412 // Check if the Style contains a SELECTION FTS
194 alfonx 455
195     FeatureTypeStyle[] clone = originalStyle.featureTypeStyles()
196     .toArray(new FeatureTypeStyle[] {}).clone();
197    
198 alfonx 412 for (FeatureTypeStyle fts : clone) {
199     if (fts.getName() != null
200 alfonx 516 && fts.getName().equals(SELECTION_STYLING_FTS_NAME)) {
201 alfonx 412 originalStyle.featureTypeStyles().remove(fts);
202 alfonx 455
203 alfonx 559 mapLayer.setStyle(originalStyle);
204     // replaceRenderer();
205     // // mapPane.refresh();
206 alfonx 106
207 alfonx 412 return;
208     }
209     }
210    
211 alfonx 97 } else {
212 alfonx 500
213 alfonx 100 LOGGER.debug("SELECTION .. change style");
214 alfonx 106
215 alfonx 500 // Saving a repaint if the selection didn't change
216     if (!selectionChanged(newSelection, originalStyle)) {
217     return;
218     }
219    
220 alfonx 123 // We take Style from the MapLayer that is displayed at the
221 alfonx 224 // moment. We do not use the styledLayer.getStyle, because in
222 alfonx 123 // the atlas, this always return the default style, but
223 alfonx 224 // additional styles might be selected.
224 alfonx 867 // Taking the style from the mapLayer indicates, that we have to
225 alfonx 125 // remove any selection rules first.
226 alfonx 101
227 alfonx 412 FeatureTypeStyle selectionFTStyle = StylingUtil
228 alfonx 946 .createSelectionFTStyle(styledLayer.getGeoObject(),
229     SelectionStylesTypes.LeftTop2RightBottom_lines);
230 alfonx 106
231 alfonx 516 selectionFTStyle.setName(SELECTION_STYLING_FTS_NAME);
232 alfonx 123
233 alfonx 158 /**
234     *
235     * Add a Filter to the selectionMapStyle, so that it is only
236     * used on objects that are selected. <br/>
237 alfonx 489 * To optimize rendering speed, the filter checks whether more
238     * than half of the features are selected. If so, the filter is
239     * inverted to be shorter.
240 alfonx 158 *
241     */
242 alfonx 489 final FeatureCollection<SimpleFeatureType, SimpleFeature> featureCollectionFiltered = styledLayer
243     .getFeatureCollectionFiltered();
244     if (newSelection.size() > featureCollectionFiltered.size() / 2) {
245     FeatureIterator<SimpleFeature> iterator = featureCollectionFiltered
246     .features();
247     Set<FeatureId> antiFids = new HashSet<FeatureId>();
248     try {
249     while (iterator.hasNext()) {
250     SimpleFeature next = iterator.next();
251     if (!newSelection.contains(next.getID()))
252     antiFids.add(FilterUtil.FILTER_FAC2
253     .featureId(next.getID()));
254     }
255    
256     selectionFTStyle.rules().get(0).setFilter(
257     FilterUtil.FILTER_FAC2
258     .not(FilterUtil.FILTER_FAC2
259     .id(antiFids)));
260    
261     } finally {
262     featureCollectionFiltered.close(iterator);
263     }
264     } else {
265     Set<FeatureId> fids = new HashSet<FeatureId>();
266     for (String fid : newSelection) {
267 alfonx 671 if (fid != null)
268     fids.add(FilterUtil.FILTER_FAC2.featureId(fid));
269 alfonx 489 }
270    
271     selectionFTStyle.rules().get(0).setFilter(
272     FilterUtil.FILTER_FAC2.id(fids));
273 alfonx 158 }
274 alfonx 106
275 alfonx 412 // Maybe there has already been another selection
276     // FeatureTypeStyle... Let's replace it...
277     boolean foundAndReplaced = false;
278     for (FeatureTypeStyle fts : originalStyle.featureTypeStyles()) {
279     if (fts.getName() != null
280 alfonx 516 && fts.getName().equals(SELECTION_STYLING_FTS_NAME)) {
281 alfonx 412 foundAndReplaced = true;
282     fts.rules().clear();
283     fts.rules().addAll(selectionFTStyle.rules());
284     break;
285     }
286 alfonx 123 }
287 alfonx 412 if (!foundAndReplaced) {
288     originalStyle.featureTypeStyles().add(selectionFTStyle);
289     }
290 alfonx 151
291 alfonx 412 // Refresh the map
292 alfonx 455 // mapPane.refresh();
293    
294 alfonx 419 DuplicatingStyleVisitor dsv = new DuplicatingStyleVisitor();
295     dsv.visit(originalStyle);
296 alfonx 455 Style newStyle = (Style) dsv.getCopy();
297 mojays 493
298 alfonx 500 // SK-Debug
299     try {
300     //
301     StylingUtil.saveStyleToSLD(newStyle, new File(
302     "/home/stefan/Desktop/selection.sld"));
303     } catch (Exception e) {
304     }
305 mojays 493
306     // DuplicatingStyleVisitor dsv = new DuplicatingStyleVisitor();
307 alfonx 489 // dsv.visit(originalStyle);
308     // Style newStyle = originalStyle;
309    
310 alfonx 455 // Style newStyle = StylingUtil.STYLE_BUILDER.createStyle();
311     // newStyle.featureTypeStyles().addAll(originalStyle.featureTypeStyles());
312 alfonx 419 mapLayer.setStyle(newStyle);
313 alfonx 455
314 alfonx 419 replaceRenderer();
315 alfonx 97 }
316    
317     } catch (Exception e) {
318     LOGGER.error("Error while trying to create a selection style", e);
319     }
320     }
321    
322 alfonx 500 /**
323 alfonx 560 * Analyzes whether the selection has changed in comparison to the selection
324 alfonx 500 * stored in the mapLayer.Style
325     *
326 alfonx 946 * @param newSelection
327     * a List<String> of all newly selected FIDs
328     * @param originalStyle
329     * the original {@link Style} that has an earlier selection coded
330     * into the {@link Style}
331 alfonx 560 * @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 740 public void performMapPaneEvent(XMapPaneEvent 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