/[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 493 - (hide annotations)
Fri Oct 23 14:09:27 2009 UTC (15 years, 4 months ago) by mojays
Original Path: branches/1.0-gt2-2.6/src/skrueger/geotools/selection/FeatureMapLayerSelectionSynchronizer.java
File MIME type: text/plain
File size: 15994 byte(s)
BugFix: Handle empty Rectangle in JMapPane#fixAspectRatio(..)
FeatureTablePane: check for "valueAdjusting" removed (because this must be done in event firing, not in event handling!)
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 97 import org.geotools.map.MapLayer;
62 alfonx 419 import org.geotools.renderer.GTRenderer;
63     import org.geotools.renderer.label.LabelCacheImpl;
64     import org.geotools.renderer.lite.StreamingRenderer;
65 alfonx 123 import org.geotools.styling.FeatureTypeStyle;
66 alfonx 97 import org.geotools.styling.Style;
67 alfonx 419 import org.geotools.styling.visitor.DuplicatingStyleVisitor;
68 mojays 325 import org.opengis.feature.simple.SimpleFeature;
69 alfonx 455 import org.opengis.feature.simple.SimpleFeatureType;
70 alfonx 158 import org.opengis.filter.identity.FeatureId;
71 alfonx 97
72 alfonx 158 import schmitzm.geotools.FilterUtil;
73 alfonx 438 import schmitzm.geotools.GTUtil;
74 alfonx 97 import schmitzm.geotools.gui.JMapPane;
75     import schmitzm.geotools.map.event.FeatureSelectedEvent;
76     import schmitzm.geotools.map.event.JMapPaneEvent;
77     import schmitzm.geotools.map.event.JMapPaneListener;
78     import schmitzm.geotools.styling.StylingUtil;
79 alfonx 111 import skrueger.geotools.MapPaneToolBar;
80 alfonx 489 import skrueger.geotools.StyledFeaturesInterface;
81 alfonx 224 import skrueger.geotools.StyledLayerInterface;
82 alfonx 97
83     /**
84     * This class keeps the selection of a (feature) {@link JTable} synchronized
85     * with the {@link StyledLayerSelectionModel} of a layer. This is done by
86     * implementing:
87     * <ul>
88     * <li>a {@link PropertyChangeListener} which listens to the
89     * {@link StyledLayerSelectionModel} and accordingly changes the {@link JTable}
90     * selection</li>
91     * <li>a {@link ListSelectionListener} which listens to the {@link JTable} and
92     * accordingly changes the {@link StyledLayerSelectionModel} selection</li>
93     * </ul>
94     * After creating, the instance of this synchronizer must be added as listener
95     * to both, the {@link StyledLayerSelectionModel} and the table's
96     * {@link ListSelectionModel}.
97     *
98     * @author <a href="mailto:[email protected]">Martin Schmitz</a>
99     * (University of Bonn/Germany)
100     */
101     public class FeatureMapLayerSelectionSynchronizer extends
102 alfonx 106 StyledLayerSelectionModelSynchronizer<StyledFeatureLayerSelectionModel>
103     implements JMapPaneListener {
104 alfonx 111 public static final String SELECTION_STYLING = "SELECTION";
105 alfonx 97 /**
106     * Holds the {@link MapLayer} to keep synchronized with the layer selection
107     * model.
108     */
109     protected final MapLayer mapLayer;
110 alfonx 489 protected final StyledFeaturesInterface<?> styledLayer;
111 alfonx 97 protected final JMapPane mapPane;
112 alfonx 111 private final MapPaneToolBar toolBar;
113 alfonx 419 private final HashMap<Object, Object> defaultGTRenderingHints;
114 alfonx 97
115     /**
116     * Creates a new synchronizer
117     *
118     * @param layerSelModel
119     * layer selection model to keep synchronized with the
120     * {@link MapLayer}
121     *
122     * @param mapLayer
123     * {@link MapLayer} to keep synchronized with.
124     */
125     public FeatureMapLayerSelectionSynchronizer(
126     StyledFeatureLayerSelectionModel layerSelModel,
127 alfonx 489 StyledFeaturesInterface<?> styledLayer, MapLayer mapLayer,
128 alfonx 455 JMapPane mapPane, MapPaneToolBar toolBar,
129     HashMap<Object, Object> defaultGTRenderingHints) {
130 alfonx 97
131     super(layerSelModel);
132 alfonx 224 this.styledLayer = styledLayer;
133 alfonx 97
134     this.mapLayer = mapLayer;
135     this.mapPane = mapPane;
136 alfonx 111 this.toolBar = toolBar;
137 alfonx 419 if (defaultGTRenderingHints != null)
138     this.defaultGTRenderingHints = defaultGTRenderingHints;
139 alfonx 455 else
140 alfonx 419 this.defaultGTRenderingHints = new HashMap<Object, Object>();
141 alfonx 97 }
142    
143     /**
144     * Called by {@link StyledLayerSelectionModel} when a the selection on other
145     * selection components (map, chart, ...) has changed. When calling this
146     *
147     * method changes the {@link MapLayer} selection according to the
148     * {@link StyledLayerSelectionModel} selection.
149     *
150     * @param evt
151     * an event
152     */
153     @Override
154     public void propertyChange(PropertyChangeEvent evt) {
155 alfonx 151
156     if (!isEnabled())
157     return;
158    
159 alfonx 97 if (!(evt instanceof StyledLayerSelectionEvent))
160     return;
161     StyledLayerSelectionEvent selEvt = (StyledLayerSelectionEvent) evt;
162     // Only react on own layer and ignore events invoked by
163     // this component itself
164     if (selEvt.getEmitter() != layerSelModel || selectionChangeCausedByMe)
165     return;
166     // Apply new selection on table (except this event is one of
167     // some more events)
168 alfonx 111 Vector<String> newSelection = layerSelModel.getSelection();
169 alfonx 97 if (newSelection == null)
170     return;
171    
172     // Avoid event circles in valueChanged(..)
173     selectionChangeCausedByMe = true;
174    
175     changeLayerStyle(newSelection);
176    
177     // Danger of event circles in valueChanged(..) banned
178     selectionChangeCausedByMe = false;
179     }
180    
181 alfonx 151 /**
182     * Changes the Style of the {@link MapLayer} to reflect changes of the
183     * selection.
184     *
185     * @param newSelection
186 alfonx 318 * A {@link Vector} of SimpleFeature-IDs that are selected.
187 alfonx 151 */
188 alfonx 111 private void changeLayerStyle(final Vector<String> newSelection) {
189 alfonx 97 try {
190 alfonx 455
191 alfonx 412 Style originalStyle = mapLayer.getStyle();
192 alfonx 97 if (newSelection.isEmpty()) {
193    
194 alfonx 412 // Check if the Style contains a SELECTION FTS
195 alfonx 455
196     FeatureTypeStyle[] clone = originalStyle.featureTypeStyles()
197     .toArray(new FeatureTypeStyle[] {}).clone();
198    
199 alfonx 412 for (FeatureTypeStyle fts : clone) {
200     if (fts.getName() != null
201     && fts.getName().equals(SELECTION_STYLING)) {
202     originalStyle.featureTypeStyles().remove(fts);
203 alfonx 455
204 alfonx 419 replaceRenderer();
205 alfonx 455 // mapPane.refresh();
206 alfonx 106
207 alfonx 412 return;
208     }
209     }
210    
211 alfonx 97 } else {
212 alfonx 100 LOGGER.debug("SELECTION .. change style");
213 alfonx 106
214 alfonx 123 // We take Style from the MapLayer that is displayed at the
215 alfonx 224 // moment. We do not use the styledLayer.getStyle, because in
216 alfonx 123 // the atlas, this always return the default style, but
217 alfonx 224 // additional styles might be selected.
218 alfonx 123 // Taking the style from the mapLayer indicated, that we have to
219 alfonx 125 // remove any selection rules first.
220 alfonx 101
221 alfonx 412 FeatureTypeStyle selectionFTStyle = StylingUtil
222 alfonx 224 .createSelectionStyle(styledLayer.getGeoObject());
223 alfonx 106
224 alfonx 412 selectionFTStyle.setName(SELECTION_STYLING);
225 alfonx 123
226 alfonx 158 /**
227     *
228     * Add a Filter to the selectionMapStyle, so that it is only
229     * used on objects that are selected. <br/>
230 alfonx 489 * To optimize rendering speed, the filter checks whether more
231     * than half of the features are selected. If so, the filter is
232     * inverted to be shorter.
233 alfonx 158 *
234     */
235 alfonx 106
236 alfonx 489 final FeatureCollection<SimpleFeatureType, SimpleFeature> featureCollectionFiltered = styledLayer
237     .getFeatureCollectionFiltered();
238     if (newSelection.size() > featureCollectionFiltered.size() / 2) {
239     FeatureIterator<SimpleFeature> iterator = featureCollectionFiltered
240     .features();
241     Set<FeatureId> antiFids = new HashSet<FeatureId>();
242     try {
243     while (iterator.hasNext()) {
244     SimpleFeature next = iterator.next();
245     if (!newSelection.contains(next.getID()))
246     antiFids.add(FilterUtil.FILTER_FAC2
247     .featureId(next.getID()));
248     }
249    
250     selectionFTStyle.rules().get(0).setFilter(
251     FilterUtil.FILTER_FAC2
252     .not(FilterUtil.FILTER_FAC2
253     .id(antiFids)));
254    
255     } finally {
256     featureCollectionFiltered.close(iterator);
257     }
258     } else {
259     Set<FeatureId> fids = new HashSet<FeatureId>();
260     for (String fid : newSelection) {
261     fids.add(FilterUtil.FILTER_FAC2.featureId(fid));
262     }
263    
264     selectionFTStyle.rules().get(0).setFilter(
265     FilterUtil.FILTER_FAC2.id(fids));
266 alfonx 158 }
267 alfonx 106
268 alfonx 412 // Maybe there has already been another selection
269     // FeatureTypeStyle... Let's replace it...
270     boolean foundAndReplaced = false;
271     for (FeatureTypeStyle fts : originalStyle.featureTypeStyles()) {
272     if (fts.getName() != null
273     && fts.getName().equals(SELECTION_STYLING)) {
274     foundAndReplaced = true;
275     fts.rules().clear();
276     fts.rules().addAll(selectionFTStyle.rules());
277     break;
278     }
279 alfonx 123 }
280 alfonx 412 if (!foundAndReplaced) {
281     originalStyle.featureTypeStyles().add(selectionFTStyle);
282     }
283 alfonx 151
284 alfonx 412 // Refresh the map
285 alfonx 455 // mapPane.refresh();
286    
287 alfonx 419 DuplicatingStyleVisitor dsv = new DuplicatingStyleVisitor();
288     dsv.visit(originalStyle);
289 alfonx 455 Style newStyle = (Style) dsv.getCopy();
290 mojays 493
291     //SK-Debug
292 alfonx 491 // try {
293     ////
294     //// StylingUtil.saveStyleToSLD(newStyle, new File(
295     //// "/home/stefan/Desktop/selection.sld"));
296     // } catch (Exception e) {
297     // }
298 mojays 493
299     // DuplicatingStyleVisitor dsv = new DuplicatingStyleVisitor();
300 alfonx 489 // dsv.visit(originalStyle);
301     // Style newStyle = originalStyle;
302    
303 alfonx 455 // Style newStyle = StylingUtil.STYLE_BUILDER.createStyle();
304     // newStyle.featureTypeStyles().addAll(originalStyle.featureTypeStyles());
305 alfonx 419 mapLayer.setStyle(newStyle);
306 alfonx 455
307 alfonx 419 replaceRenderer();
308 alfonx 97 }
309    
310     } catch (Exception e) {
311     LOGGER.error("Error while trying to create a selection style", e);
312     }
313     }
314    
315 alfonx 419 private void replaceRenderer() {
316 alfonx 455 //
317     // // Has to be done before we apply the new Renderer
318     // mapLayer.setStyle(style);
319 alfonx 419
320     GTRenderer oldRenderer = mapPane.getRenderer();
321    
322     /**
323     * Explicitly putting a new instance of LabelCacheDefault into the
324     * renderer instance, so JMapPane doesn't reuse the old one. This is
325     * very useful when changing the TextSymbolizer with AtlasStyler<br/>
326     * SK 9.7.09: It's not enought to user LabelCache.clear(). We can not
327     * reuse the old Renderer - better to create a new one!
328     */
329 alfonx 440 final GTRenderer newRenderer = GTUtil.createGTRenderer();
330 alfonx 419
331     final HashMap<Object, Object> rendererHints = defaultGTRenderingHints;
332     rendererHints.put(StreamingRenderer.LABEL_CACHE_KEY,
333     new LabelCacheImpl());
334    
335     newRenderer.setRendererHints(rendererHints);
336     mapPane.setRenderer(newRenderer);
337    
338     if (oldRenderer != null) {
339 alfonx 455
340 alfonx 419 RenderingHints java2DHints = oldRenderer.getJava2DHints();
341     if (java2DHints != null) {
342 alfonx 427 newRenderer.setJava2DHints(java2DHints);
343 alfonx 419 }
344 alfonx 455
345 alfonx 419 oldRenderer.setContext(null);
346     oldRenderer = null;
347     }
348 alfonx 455
349 alfonx 419 mapPane.refresh();
350    
351     }
352    
353 alfonx 151 /**
354     * Used to synchronize {@link FeatureSelectedEvent}s with the
355     * {@link StyledFeatureLayerSelectionModel}
356     */
357 alfonx 97 @Override
358     public void performMapPaneEvent(JMapPaneEvent e) {
359 alfonx 106
360 alfonx 151 // Ignore event if it is caused by us or the synchronizer is disabled.
361     if (!isEnabled() || selectionChangeCausedByMe)
362     return;
363    
364     if (!(e instanceof FeatureSelectedEvent)) {
365     // LOGGER.debug("Ignoring event " + e);
366     return;
367     }
368    
369 alfonx 111 /**
370 alfonx 151 * Only listen to FeatureSelectedEvents if an appropriate tool is
371 alfonx 111 * selected.
372     */
373     final int selectedTool = toolBar.getSelectedTool();
374     if (selectedTool != MapPaneToolBar.TOOL_SELECTION_ADD
375     && selectedTool != MapPaneToolBar.TOOL_SELECTION_REMOVE
376     && selectedTool != MapPaneToolBar.TOOL_SELECTION_SET) {
377     return;
378     }
379    
380 mojays 109 // only listen to events directly coming from JMapPane
381     // selection (ignore selections from FilterDialog)
382 alfonx 111 if (e.getSourceObject() != this.mapPane)
383     return;
384    
385 alfonx 97
386 alfonx 151 /**
387 alfonx 158 * Checking, that the FeatureSelectedEvent actually contains features
388     * from this layer
389 alfonx 151 */
390 alfonx 492 FeatureSelectedEvent fse = (FeatureSelectedEvent) e;
391 alfonx 151 final String sourceID = fse.getSourceLayer().getTitle();
392     final String syncForID = mapLayer.getTitle();
393     if (sourceID != null && syncForID != null
394 alfonx 158 && !sourceID.equals(syncForID)) {
395     LOGGER.debug("Ignoring a FeatureSelectedEvent from " + sourceID);
396 alfonx 97 return;
397 alfonx 151 }
398 alfonx 97
399 alfonx 489 // LOGGER.debug("do event " + fse);
400 alfonx 111
401 alfonx 97 // Avoid event circles in propertyChange(..)
402     selectionChangeCausedByMe = true;
403    
404 alfonx 455 final FeatureCollection<SimpleFeatureType, SimpleFeature> selectionResult = fse
405     .getSelectionResult();
406     Iterator<SimpleFeature> fi = selectionResult.iterator();
407     try {
408 alfonx 97
409 alfonx 455 // reset the selection of the DpLayerSelectionModel
410     // layerSelModel.setValueIsAdjusting(true);
411 alfonx 97
412 alfonx 455 if (selectedTool == MapPaneToolBar.TOOL_SELECTION_ADD) {
413     layerSelModel.setValueIsAdjusting(true);
414 alfonx 409
415 alfonx 455 for (int fIdx = 0; fi.hasNext(); fIdx++) {
416     SimpleFeature f = fi.next();
417     layerSelModel.addSelection(f.getID());
418     }
419     layerSelModel.setValueIsAdjusting(false);
420     } else if (selectedTool == MapPaneToolBar.TOOL_SELECTION_SET) {
421     layerSelModel.setValueIsAdjusting(true);
422     layerSelModel.clearSelection();
423 alfonx 97
424 alfonx 455 for (int fIdx = 0; fi.hasNext(); fIdx++) {
425     SimpleFeature f = fi.next();
426     layerSelModel.addSelection(f.getID());
427     }
428 alfonx 111
429 alfonx 455 // LOGGER.debug("Setting selection to " + fi.());
430 alfonx 409
431 alfonx 455 layerSelModel.setValueIsAdjusting(false);
432     } else if (selectedTool == MapPaneToolBar.TOOL_SELECTION_REMOVE) {
433     layerSelModel.setValueIsAdjusting(true);
434     for (int fIdx = 0; fi.hasNext(); fIdx++) {
435     SimpleFeature f = fi.next();
436     layerSelModel.removeSelection(f.getID());
437     }
438     layerSelModel.setValueIsAdjusting(false);
439 alfonx 111 }
440 alfonx 455
441     } finally {
442     selectionResult.close(fi);
443 alfonx 151 }
444 alfonx 111
445     // Show selected features in the map, which is not automatically done by
446     // the origin: FeatureSelectedEvent
447 alfonx 97 changeLayerStyle(layerSelModel.getSelection());
448 alfonx 106
449 alfonx 97 // Danger of event circles in propertyChange(..) banned
450     selectionChangeCausedByMe = false;
451     }
452 alfonx 151
453 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