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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 491 - (show annotations)
Fri Oct 23 12:57:15 2009 UTC (15 years, 4 months ago) by alfonx
File MIME type: text/plain
File size: 15989 byte(s)
removed the debug sld to file selection.sld

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 // try {
292 ////
293 //// StylingUtil.saveStyleToSLD(newStyle, new File(
294 //// "/home/stefan/Desktop/selection.sld"));
295 // } catch (Exception e) {
296 // }
297 //
298 // // DuplicatingStyleVisitor dsv = new DuplicatingStyleVisitor();
299 // dsv.visit(originalStyle);
300 // Style newStyle = originalStyle;
301
302 // Style newStyle = StylingUtil.STYLE_BUILDER.createStyle();
303 // newStyle.featureTypeStyles().addAll(originalStyle.featureTypeStyles());
304 mapLayer.setStyle(newStyle);
305
306 replaceRenderer();
307 }
308
309 } catch (Exception e) {
310 LOGGER.error("Error while trying to create a selection style", e);
311 }
312 }
313
314 private void replaceRenderer() {
315 //
316 // // Has to be done before we apply the new Renderer
317 // mapLayer.setStyle(style);
318
319 GTRenderer oldRenderer = mapPane.getRenderer();
320
321 /**
322 * Explicitly putting a new instance of LabelCacheDefault into the
323 * renderer instance, so JMapPane doesn't reuse the old one. This is
324 * very useful when changing the TextSymbolizer with AtlasStyler<br/>
325 * SK 9.7.09: It's not enought to user LabelCache.clear(). We can not
326 * reuse the old Renderer - better to create a new one!
327 */
328 final GTRenderer newRenderer = GTUtil.createGTRenderer();
329
330 final HashMap<Object, Object> rendererHints = defaultGTRenderingHints;
331 rendererHints.put(StreamingRenderer.LABEL_CACHE_KEY,
332 new LabelCacheImpl());
333
334 newRenderer.setRendererHints(rendererHints);
335 mapPane.setRenderer(newRenderer);
336
337 if (oldRenderer != null) {
338
339 RenderingHints java2DHints = oldRenderer.getJava2DHints();
340 if (java2DHints != null) {
341 newRenderer.setJava2DHints(java2DHints);
342 }
343
344 oldRenderer.setContext(null);
345 oldRenderer = null;
346 }
347
348 mapPane.refresh();
349
350 }
351
352 /**
353 * Used to synchronize {@link FeatureSelectedEvent}s with the
354 * {@link StyledFeatureLayerSelectionModel}
355 */
356 @Override
357 public void performMapPaneEvent(JMapPaneEvent e) {
358
359 // Ignore event if it is caused by us or the synchronizer is disabled.
360 if (!isEnabled() || selectionChangeCausedByMe)
361 return;
362
363 if (!(e instanceof FeatureSelectedEvent)) {
364 // LOGGER.debug("Ignoring event " + e);
365 return;
366 }
367
368 /**
369 * Only listen to FeatureSelectedEvents if an appropriate tool is
370 * selected.
371 */
372 final int selectedTool = toolBar.getSelectedTool();
373 if (selectedTool != MapPaneToolBar.TOOL_SELECTION_ADD
374 && selectedTool != MapPaneToolBar.TOOL_SELECTION_REMOVE
375 && selectedTool != MapPaneToolBar.TOOL_SELECTION_SET) {
376 return;
377 }
378
379 // only listen to events directly coming from JMapPane
380 // selection (ignore selections from FilterDialog)
381 if (e.getSourceObject() != this.mapPane)
382 return;
383
384 FeatureSelectedEvent fse = (FeatureSelectedEvent) e;
385
386 /**
387 * Checking, that the FeatureSelectedEvent actually contains features
388 * from this layer
389 */
390 final String sourceID = fse.getSourceLayer().getTitle();
391 final String syncForID = mapLayer.getTitle();
392 if (sourceID != null && syncForID != null
393 && !sourceID.equals(syncForID)) {
394 LOGGER.debug("Ignoring a FeatureSelectedEvent from " + sourceID);
395 return;
396 }
397
398 // LOGGER.debug("do event " + fse);
399
400 // Avoid event circles in propertyChange(..)
401 selectionChangeCausedByMe = true;
402
403 final FeatureCollection<SimpleFeatureType, SimpleFeature> selectionResult = fse
404 .getSelectionResult();
405 Iterator<SimpleFeature> fi = selectionResult.iterator();
406 try {
407
408 // reset the selection of the DpLayerSelectionModel
409 // layerSelModel.setValueIsAdjusting(true);
410
411 if (selectedTool == MapPaneToolBar.TOOL_SELECTION_ADD) {
412 layerSelModel.setValueIsAdjusting(true);
413
414 for (int fIdx = 0; fi.hasNext(); fIdx++) {
415 SimpleFeature f = fi.next();
416 layerSelModel.addSelection(f.getID());
417 }
418 layerSelModel.setValueIsAdjusting(false);
419 } else if (selectedTool == MapPaneToolBar.TOOL_SELECTION_SET) {
420 layerSelModel.setValueIsAdjusting(true);
421 layerSelModel.clearSelection();
422
423 for (int fIdx = 0; fi.hasNext(); fIdx++) {
424 SimpleFeature f = fi.next();
425 layerSelModel.addSelection(f.getID());
426 }
427
428 // LOGGER.debug("Setting selection to " + fi.());
429
430 layerSelModel.setValueIsAdjusting(false);
431 } else if (selectedTool == MapPaneToolBar.TOOL_SELECTION_REMOVE) {
432 layerSelModel.setValueIsAdjusting(true);
433 for (int fIdx = 0; fi.hasNext(); fIdx++) {
434 SimpleFeature f = fi.next();
435 layerSelModel.removeSelection(f.getID());
436 }
437 layerSelModel.setValueIsAdjusting(false);
438 }
439
440 } finally {
441 selectionResult.close(fi);
442 }
443
444 // Show selected features in the map, which is not automatically done by
445 // the origin: FeatureSelectedEvent
446 changeLayerStyle(layerSelModel.getSelection());
447
448 // Danger of event circles in propertyChange(..) banned
449 selectionChangeCausedByMe = false;
450 }
451
452 }

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