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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 516 - (hide annotations)
Wed Nov 11 21:17:33 2009 UTC (15 years, 3 months ago) by alfonx
File MIME type: text/plain
File size: 17953 byte(s)
* Extended schmitzm.JPanel to generally set its background to white when printed, by overwriting the print() method.
* Simplified the creation and update logic of ((Design)Atlas)MapLegend and ((Design)Atlas)MapLayerLegend. Besides other things there now is a LegendHelper class in AtlasProject... 
* Orginized Imports
* Removed a lot of DEBUG messages...
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 509 import schmitzm.geotools.map.event.MapPaneEvent;
74 alfonx 97 import schmitzm.geotools.map.event.JMapPaneListener;
75     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 419 replaceRenderer();
203 alfonx 455 // mapPane.refresh();
204 alfonx 106
205 alfonx 412 return;
206     }
207     }
208    
209 alfonx 97 } else {
210 alfonx 500
211 alfonx 100 LOGGER.debug("SELECTION .. change style");
212 alfonx 106
213 alfonx 500 // Saving a repaint if the selection didn't change
214     if (!selectionChanged(newSelection, originalStyle)) {
215     return;
216     }
217    
218 alfonx 123 // We take Style from the MapLayer that is displayed at the
219 alfonx 224 // moment. We do not use the styledLayer.getStyle, because in
220 alfonx 123 // the atlas, this always return the default style, but
221 alfonx 224 // additional styles might be selected.
222 alfonx 123 // Taking the style from the mapLayer indicated, that we have to
223 alfonx 125 // remove any selection rules first.
224 alfonx 101
225 alfonx 412 FeatureTypeStyle selectionFTStyle = StylingUtil
226 alfonx 224 .createSelectionStyle(styledLayer.getGeoObject());
227 alfonx 106
228 alfonx 516 selectionFTStyle.setName(SELECTION_STYLING_FTS_NAME);
229 alfonx 123
230 alfonx 158 /**
231     *
232     * Add a Filter to the selectionMapStyle, so that it is only
233     * used on objects that are selected. <br/>
234 alfonx 489 * To optimize rendering speed, the filter checks whether more
235     * than half of the features are selected. If so, the filter is
236     * inverted to be shorter.
237 alfonx 158 *
238     */
239 alfonx 489 final FeatureCollection<SimpleFeatureType, SimpleFeature> featureCollectionFiltered = styledLayer
240     .getFeatureCollectionFiltered();
241     if (newSelection.size() > featureCollectionFiltered.size() / 2) {
242     FeatureIterator<SimpleFeature> iterator = featureCollectionFiltered
243     .features();
244     Set<FeatureId> antiFids = new HashSet<FeatureId>();
245     try {
246     while (iterator.hasNext()) {
247     SimpleFeature next = iterator.next();
248     if (!newSelection.contains(next.getID()))
249     antiFids.add(FilterUtil.FILTER_FAC2
250     .featureId(next.getID()));
251     }
252    
253     selectionFTStyle.rules().get(0).setFilter(
254     FilterUtil.FILTER_FAC2
255     .not(FilterUtil.FILTER_FAC2
256     .id(antiFids)));
257    
258     } finally {
259     featureCollectionFiltered.close(iterator);
260     }
261     } else {
262     Set<FeatureId> fids = new HashSet<FeatureId>();
263     for (String fid : newSelection) {
264     fids.add(FilterUtil.FILTER_FAC2.featureId(fid));
265     }
266    
267     selectionFTStyle.rules().get(0).setFilter(
268     FilterUtil.FILTER_FAC2.id(fids));
269 alfonx 158 }
270 alfonx 106
271 alfonx 412 // Maybe there has already been another selection
272     // FeatureTypeStyle... Let's replace it...
273     boolean foundAndReplaced = false;
274     for (FeatureTypeStyle fts : originalStyle.featureTypeStyles()) {
275     if (fts.getName() != null
276 alfonx 516 && fts.getName().equals(SELECTION_STYLING_FTS_NAME)) {
277 alfonx 412 foundAndReplaced = true;
278     fts.rules().clear();
279     fts.rules().addAll(selectionFTStyle.rules());
280     break;
281     }
282 alfonx 123 }
283 alfonx 412 if (!foundAndReplaced) {
284     originalStyle.featureTypeStyles().add(selectionFTStyle);
285     }
286 alfonx 151
287 alfonx 412 // Refresh the map
288 alfonx 455 // mapPane.refresh();
289    
290 alfonx 419 DuplicatingStyleVisitor dsv = new DuplicatingStyleVisitor();
291     dsv.visit(originalStyle);
292 alfonx 455 Style newStyle = (Style) dsv.getCopy();
293 mojays 493
294 alfonx 500 // SK-Debug
295     try {
296     //
297     StylingUtil.saveStyleToSLD(newStyle, new File(
298     "/home/stefan/Desktop/selection.sld"));
299     } catch (Exception e) {
300     }
301 mojays 493
302     // DuplicatingStyleVisitor dsv = new DuplicatingStyleVisitor();
303 alfonx 489 // dsv.visit(originalStyle);
304     // Style newStyle = originalStyle;
305    
306 alfonx 455 // Style newStyle = StylingUtil.STYLE_BUILDER.createStyle();
307     // newStyle.featureTypeStyles().addAll(originalStyle.featureTypeStyles());
308 alfonx 419 mapLayer.setStyle(newStyle);
309 alfonx 455
310 alfonx 419 replaceRenderer();
311 alfonx 97 }
312    
313     } catch (Exception e) {
314     LOGGER.error("Error while trying to create a selection style", e);
315     }
316     }
317    
318 alfonx 500 /**
319     * Analyses whether the selection has changed in comparison to the selection
320     * stored in the mapLayer.Style
321     *
322     * @param newSelection
323     * @param originalStyle
324     * @return
325     */
326     private boolean selectionChanged(Vector<String> newSelection,
327     Style originalStyle) {
328    
329     boolean SELECTION_STYLING_foundInMapStyle = false;
330    
331     /**
332     * testing, whether the selection really changed. If not, we can save
333     * one paint!
334     */
335     for (FeatureTypeStyle fts : originalStyle.featureTypeStyles()) {
336    
337     if (fts.getName() != null
338 alfonx 516 && fts.getName().equals(SELECTION_STYLING_FTS_NAME)) {
339 alfonx 500
340     SELECTION_STYLING_foundInMapStyle = true;
341    
342     Filter filter = fts.rules().get(0).getFilter();
343     if (filter instanceof Not) {
344     FidFilterImpl antiOrigFidsFilter = (FidFilterImpl) ((Not) filter)
345     .getFilter();
346    
347     // Check one way
348     final Set<String> antiFids = antiOrigFidsFilter
349     .getFidsSet();
350    
351     if (antiFids.isEmpty() && !newSelection.isEmpty())
352     return true;
353    
354     for (String fid : antiFids) {
355     if (newSelection.contains(fid)) {
356     return true;
357     }
358     }
359    
360     // Check the other way
361     for (String fid : newSelection) {
362     if (antiFids.contains(fid)) {
363     return true;
364     }
365     }
366    
367     } else {
368     FidFilterImpl origFidsFilter = (FidFilterImpl) filter;
369    
370     // Check one way
371     final Set<String> fids = origFidsFilter.getFidsSet();
372    
373     if (fids.isEmpty() && newSelection.isEmpty())
374     return false;
375     if (fids.size() != newSelection.size())
376     return true;
377    
378     for (String fid : fids) {
379     if (!newSelection.contains(fid)) {
380     return true;
381     }
382     }
383    
384     // Check the other way
385     for (String fid : newSelection) {
386     if (!fids.contains(fid)) {
387     return true;
388     }
389     }
390    
391     }
392    
393     break;
394     }
395     }
396    
397     if (!SELECTION_STYLING_foundInMapStyle && !newSelection.isEmpty())
398     return true;
399    
400     return false;
401     }
402    
403 alfonx 509 /**
404 alfonx 516 * Replaces the local renderer
405 alfonx 509 */
406 alfonx 419 private void replaceRenderer() {
407 alfonx 516 // //
408     // // // Has to be done before we apply the new Renderer
409     // // mapLayer.setStyle(style);
410     //
411     // GTRenderer oldRenderer = mapPane.getLocalRenderer();
412     //
413     // /**
414     // * Explicitly putting a new instance of LabelCacheDefault into the
415     // * renderer instance, so JMapPane doesn't reuse the old one. This is
416     // * very useful when changing the TextSymbolizer with AtlasStyler<br/>
417     // * SK 9.7.09: It's not enought to user LabelCache.clear(). We can not
418     // * reuse the old Renderer - better to create a new one!
419     // */
420     // final GTRenderer newRenderer = GTUtil.createGTRenderer();
421     //
422     // final HashMap<Object, Object> rendererHints =
423     // defaultGTRenderingHints;
424     // rendererHints.put(StreamingRenderer.LABEL_CACHE_KEY,
425     // new LabelCacheImpl());
426     //
427     // newRenderer.setRendererHints(rendererHints);
428     // mapPane.setLocalRenderer(newRenderer);
429     //
430     // if (oldRenderer != null) {
431     //
432     // RenderingHints java2DHints = oldRenderer.getJava2DHints();
433     // if (java2DHints != null) {
434     // newRenderer.setJava2DHints(java2DHints);
435     // }
436     //
437     // oldRenderer.setContext(null);
438     // oldRenderer = null;
439     // }
440 alfonx 419
441     mapPane.refresh();
442    
443     }
444    
445 alfonx 151 /**
446     * Used to synchronize {@link FeatureSelectedEvent}s with the
447     * {@link StyledFeatureLayerSelectionModel}
448     */
449 alfonx 97 @Override
450 alfonx 509 public void performMapPaneEvent(MapPaneEvent e) {
451 alfonx 106
452 alfonx 151 // Ignore event if it is caused by us or the synchronizer is disabled.
453     if (!isEnabled() || selectionChangeCausedByMe)
454     return;
455    
456     if (!(e instanceof FeatureSelectedEvent)) {
457     // LOGGER.debug("Ignoring event " + e);
458     return;
459     }
460    
461 alfonx 111 /**
462 alfonx 151 * Only listen to FeatureSelectedEvents if an appropriate tool is
463 alfonx 111 * selected.
464     */
465     final int selectedTool = toolBar.getSelectedTool();
466     if (selectedTool != MapPaneToolBar.TOOL_SELECTION_ADD
467     && selectedTool != MapPaneToolBar.TOOL_SELECTION_REMOVE
468     && selectedTool != MapPaneToolBar.TOOL_SELECTION_SET) {
469     return;
470     }
471    
472 mojays 109 // only listen to events directly coming from JMapPane
473     // selection (ignore selections from FilterDialog)
474 alfonx 111 if (e.getSourceObject() != this.mapPane)
475     return;
476    
477 alfonx 151 /**
478 alfonx 158 * Checking, that the FeatureSelectedEvent actually contains features
479     * from this layer
480 alfonx 151 */
481 alfonx 492 FeatureSelectedEvent fse = (FeatureSelectedEvent) e;
482 alfonx 151 final String sourceID = fse.getSourceLayer().getTitle();
483     final String syncForID = mapLayer.getTitle();
484     if (sourceID != null && syncForID != null
485 alfonx 158 && !sourceID.equals(syncForID)) {
486     LOGGER.debug("Ignoring a FeatureSelectedEvent from " + sourceID);
487 alfonx 97 return;
488 alfonx 151 }
489 alfonx 97
490 alfonx 489 // LOGGER.debug("do event " + fse);
491 alfonx 111
492 alfonx 97 // Avoid event circles in propertyChange(..)
493     selectionChangeCausedByMe = true;
494    
495 alfonx 455 final FeatureCollection<SimpleFeatureType, SimpleFeature> selectionResult = fse
496     .getSelectionResult();
497     Iterator<SimpleFeature> fi = selectionResult.iterator();
498     try {
499 alfonx 97
500 alfonx 455 // reset the selection of the DpLayerSelectionModel
501     // layerSelModel.setValueIsAdjusting(true);
502 alfonx 97
503 alfonx 455 if (selectedTool == MapPaneToolBar.TOOL_SELECTION_ADD) {
504     layerSelModel.setValueIsAdjusting(true);
505 alfonx 409
506 alfonx 455 for (int fIdx = 0; fi.hasNext(); fIdx++) {
507     SimpleFeature f = fi.next();
508     layerSelModel.addSelection(f.getID());
509     }
510     layerSelModel.setValueIsAdjusting(false);
511     } else if (selectedTool == MapPaneToolBar.TOOL_SELECTION_SET) {
512     layerSelModel.setValueIsAdjusting(true);
513     layerSelModel.clearSelection();
514 alfonx 97
515 alfonx 455 for (int fIdx = 0; fi.hasNext(); fIdx++) {
516     SimpleFeature f = fi.next();
517     layerSelModel.addSelection(f.getID());
518     }
519 alfonx 111
520 alfonx 455 // LOGGER.debug("Setting selection to " + fi.());
521 alfonx 409
522 alfonx 455 layerSelModel.setValueIsAdjusting(false);
523     } else if (selectedTool == MapPaneToolBar.TOOL_SELECTION_REMOVE) {
524     layerSelModel.setValueIsAdjusting(true);
525     for (int fIdx = 0; fi.hasNext(); fIdx++) {
526     SimpleFeature f = fi.next();
527     layerSelModel.removeSelection(f.getID());
528     }
529     layerSelModel.setValueIsAdjusting(false);
530 alfonx 111 }
531 alfonx 455
532     } finally {
533     selectionResult.close(fi);
534 alfonx 151 }
535 alfonx 111
536     // Show selected features in the map, which is not automatically done by
537     // the origin: FeatureSelectedEvent
538 alfonx 97 changeLayerStyle(layerSelModel.getSelection());
539 alfonx 106
540 alfonx 97 // Danger of event circles in propertyChange(..) banned
541     selectionChangeCausedByMe = false;
542     }
543 alfonx 151
544 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