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

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