/[schmitzm]/branches/2.1/src/skrueger/geotools/selection/FeatureMapLayerSelectionSynchronizer.java
ViewVC logotype

Contents of /branches/2.1/src/skrueger/geotools/selection/FeatureMapLayerSelectionSynchronizer.java

Parent Directory Parent Directory | Revision Log Revision Log


Revision 493 - (show 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 /*******************************************************************************
2 * Copyright (c) 2009 Martin O. J. Schmitz.
3 *
4 * This file is part of the SCHMITZM library - a collection of utility
5 * classes based on Java 1.6, focusing (not only) on Java Swing
6 * 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 /**
31 Copyright 2008 Stefan Alfons Krüger
32
33 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.awt.RenderingHints;
46 import java.beans.PropertyChangeEvent;
47 import java.beans.PropertyChangeListener;
48 import java.io.File;
49 import java.util.HashMap;
50 import java.util.HashSet;
51 import java.util.Iterator;
52 import java.util.Set;
53 import java.util.Vector;
54
55 import javax.swing.JTable;
56 import javax.swing.ListSelectionModel;
57 import javax.swing.event.ListSelectionListener;
58
59 import org.geotools.feature.FeatureCollection;
60 import org.geotools.feature.FeatureIterator;
61 import org.geotools.map.MapLayer;
62 import org.geotools.renderer.GTRenderer;
63 import org.geotools.renderer.label.LabelCacheImpl;
64 import org.geotools.renderer.lite.StreamingRenderer;
65 import org.geotools.styling.FeatureTypeStyle;
66 import org.geotools.styling.Style;
67 import org.geotools.styling.visitor.DuplicatingStyleVisitor;
68 import org.opengis.feature.simple.SimpleFeature;
69 import org.opengis.feature.simple.SimpleFeatureType;
70 import org.opengis.filter.identity.FeatureId;
71
72 import schmitzm.geotools.FilterUtil;
73 import schmitzm.geotools.GTUtil;
74 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 import skrueger.geotools.MapPaneToolBar;
80 import skrueger.geotools.StyledFeaturesInterface;
81 import skrueger.geotools.StyledLayerInterface;
82
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 StyledLayerSelectionModelSynchronizer<StyledFeatureLayerSelectionModel>
103 implements JMapPaneListener {
104 public static final String SELECTION_STYLING = "SELECTION";
105 /**
106 * Holds the {@link MapLayer} to keep synchronized with the layer selection
107 * model.
108 */
109 protected final MapLayer mapLayer;
110 protected final StyledFeaturesInterface<?> styledLayer;
111 protected final JMapPane mapPane;
112 private final MapPaneToolBar toolBar;
113 private final HashMap<Object, Object> defaultGTRenderingHints;
114
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 StyledFeaturesInterface<?> styledLayer, MapLayer mapLayer,
128 JMapPane mapPane, MapPaneToolBar toolBar,
129 HashMap<Object, Object> defaultGTRenderingHints) {
130
131 super(layerSelModel);
132 this.styledLayer = styledLayer;
133
134 this.mapLayer = mapLayer;
135 this.mapPane = mapPane;
136 this.toolBar = toolBar;
137 if (defaultGTRenderingHints != null)
138 this.defaultGTRenderingHints = defaultGTRenderingHints;
139 else
140 this.defaultGTRenderingHints = new HashMap<Object, Object>();
141 }
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
156 if (!isEnabled())
157 return;
158
159 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 Vector<String> newSelection = layerSelModel.getSelection();
169 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 /**
182 * Changes the Style of the {@link MapLayer} to reflect changes of the
183 * selection.
184 *
185 * @param newSelection
186 * A {@link Vector} of SimpleFeature-IDs that are selected.
187 */
188 private void changeLayerStyle(final Vector<String> newSelection) {
189 try {
190
191 Style originalStyle = mapLayer.getStyle();
192 if (newSelection.isEmpty()) {
193
194 // Check if the Style contains a SELECTION FTS
195
196 FeatureTypeStyle[] clone = originalStyle.featureTypeStyles()
197 .toArray(new FeatureTypeStyle[] {}).clone();
198
199 for (FeatureTypeStyle fts : clone) {
200 if (fts.getName() != null
201 && fts.getName().equals(SELECTION_STYLING)) {
202 originalStyle.featureTypeStyles().remove(fts);
203
204 replaceRenderer();
205 // mapPane.refresh();
206
207 return;
208 }
209 }
210
211 } else {
212 LOGGER.debug("SELECTION .. change style");
213
214 // We take Style from the MapLayer that is displayed at the
215 // moment. We do not use the styledLayer.getStyle, because in
216 // the atlas, this always return the default style, but
217 // additional styles might be selected.
218 // Taking the style from the mapLayer indicated, that we have to
219 // remove any selection rules first.
220
221 FeatureTypeStyle selectionFTStyle = StylingUtil
222 .createSelectionStyle(styledLayer.getGeoObject());
223
224 selectionFTStyle.setName(SELECTION_STYLING);
225
226 /**
227 *
228 * Add a Filter to the selectionMapStyle, so that it is only
229 * used on objects that are selected. <br/>
230 * 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 *
234 */
235
236 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 }
267
268 // 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 }
280 if (!foundAndReplaced) {
281 originalStyle.featureTypeStyles().add(selectionFTStyle);
282 }
283
284 // Refresh the map
285 // mapPane.refresh();
286
287 DuplicatingStyleVisitor dsv = new DuplicatingStyleVisitor();
288 dsv.visit(originalStyle);
289 Style newStyle = (Style) dsv.getCopy();
290
291 //SK-Debug
292 // try {
293 ////
294 //// StylingUtil.saveStyleToSLD(newStyle, new File(
295 //// "/home/stefan/Desktop/selection.sld"));
296 // } catch (Exception e) {
297 // }
298
299 // DuplicatingStyleVisitor dsv = new DuplicatingStyleVisitor();
300 // dsv.visit(originalStyle);
301 // Style newStyle = originalStyle;
302
303 // Style newStyle = StylingUtil.STYLE_BUILDER.createStyle();
304 // newStyle.featureTypeStyles().addAll(originalStyle.featureTypeStyles());
305 mapLayer.setStyle(newStyle);
306
307 replaceRenderer();
308 }
309
310 } catch (Exception e) {
311 LOGGER.error("Error while trying to create a selection style", e);
312 }
313 }
314
315 private void replaceRenderer() {
316 //
317 // // Has to be done before we apply the new Renderer
318 // mapLayer.setStyle(style);
319
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 final GTRenderer newRenderer = GTUtil.createGTRenderer();
330
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
340 RenderingHints java2DHints = oldRenderer.getJava2DHints();
341 if (java2DHints != null) {
342 newRenderer.setJava2DHints(java2DHints);
343 }
344
345 oldRenderer.setContext(null);
346 oldRenderer = null;
347 }
348
349 mapPane.refresh();
350
351 }
352
353 /**
354 * Used to synchronize {@link FeatureSelectedEvent}s with the
355 * {@link StyledFeatureLayerSelectionModel}
356 */
357 @Override
358 public void performMapPaneEvent(JMapPaneEvent e) {
359
360 // 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 /**
370 * Only listen to FeatureSelectedEvents if an appropriate tool is
371 * 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 // only listen to events directly coming from JMapPane
381 // selection (ignore selections from FilterDialog)
382 if (e.getSourceObject() != this.mapPane)
383 return;
384
385
386 /**
387 * Checking, that the FeatureSelectedEvent actually contains features
388 * from this layer
389 */
390 FeatureSelectedEvent fse = (FeatureSelectedEvent) e;
391 final String sourceID = fse.getSourceLayer().getTitle();
392 final String syncForID = mapLayer.getTitle();
393 if (sourceID != null && syncForID != null
394 && !sourceID.equals(syncForID)) {
395 LOGGER.debug("Ignoring a FeatureSelectedEvent from " + sourceID);
396 return;
397 }
398
399 // LOGGER.debug("do event " + fse);
400
401 // Avoid event circles in propertyChange(..)
402 selectionChangeCausedByMe = true;
403
404 final FeatureCollection<SimpleFeatureType, SimpleFeature> selectionResult = fse
405 .getSelectionResult();
406 Iterator<SimpleFeature> fi = selectionResult.iterator();
407 try {
408
409 // reset the selection of the DpLayerSelectionModel
410 // layerSelModel.setValueIsAdjusting(true);
411
412 if (selectedTool == MapPaneToolBar.TOOL_SELECTION_ADD) {
413 layerSelModel.setValueIsAdjusting(true);
414
415 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
424 for (int fIdx = 0; fi.hasNext(); fIdx++) {
425 SimpleFeature f = fi.next();
426 layerSelModel.addSelection(f.getID());
427 }
428
429 // LOGGER.debug("Setting selection to " + fi.());
430
431 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 }
440
441 } finally {
442 selectionResult.close(fi);
443 }
444
445 // Show selected features in the map, which is not automatically done by
446 // the origin: FeatureSelectedEvent
447 changeLayerStyle(layerSelModel.getSelection());
448
449 // Danger of event circles in propertyChange(..) banned
450 selectionChangeCausedByMe = false;
451 }
452
453 }

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