/[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 509 - (show annotations)
Thu Nov 5 08:51:33 2009 UTC (15 years, 3 months ago) by alfonx
File MIME type: text/plain
File size: 17700 byte(s)
new XMapPane and SelectableMapPane to replace and restructure JMapPanes
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.beans.PropertyChangeEvent;
46 import java.beans.PropertyChangeListener;
47 import java.io.File;
48 import java.util.HashSet;
49 import java.util.Iterator;
50 import java.util.Set;
51 import java.util.Vector;
52
53 import javax.swing.JTable;
54 import javax.swing.ListSelectionModel;
55 import javax.swing.event.ListSelectionListener;
56
57 import org.geotools.feature.FeatureCollection;
58 import org.geotools.feature.FeatureIterator;
59 import org.geotools.filter.FidFilterImpl;
60 import org.geotools.map.MapLayer;
61 import org.geotools.styling.FeatureTypeStyle;
62 import org.geotools.styling.Style;
63 import org.geotools.styling.visitor.DuplicatingStyleVisitor;
64 import org.opengis.feature.simple.SimpleFeature;
65 import org.opengis.feature.simple.SimpleFeatureType;
66 import org.opengis.filter.Filter;
67 import org.opengis.filter.Not;
68 import org.opengis.filter.identity.FeatureId;
69
70 import schmitzm.geotools.FilterUtil;
71 import schmitzm.geotools.gui.SelectableXMapPane;
72 import schmitzm.geotools.map.event.FeatureSelectedEvent;
73 import schmitzm.geotools.map.event.MapPaneEvent;
74 import schmitzm.geotools.map.event.JMapPaneListener;
75 import schmitzm.geotools.styling.StylingUtil;
76 import skrueger.geotools.MapPaneToolBar;
77 import skrueger.geotools.StyledFeaturesInterface;
78
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 StyledLayerSelectionModelSynchronizer<StyledFeatureLayerSelectionModel>
99 implements JMapPaneListener {
100 public static final String SELECTION_STYLING = "SELECTION";
101 /**
102 * Holds the {@link MapLayer} to keep synchronized with the layer selection
103 * model.
104 */
105 protected final MapLayer mapLayer;
106 protected final StyledFeaturesInterface<?> styledLayer;
107 protected final SelectableXMapPane mapPane;
108 private final MapPaneToolBar toolBar;
109
110 /**
111 * Creates a new synchronizer
112 *
113 * @param layerSelModel
114 * layer selection model to keep synchronized with the
115 * {@link MapLayer}
116 *
117 * @param mapLayer
118 * {@link MapLayer} to keep synchronized with.
119 */
120 public FeatureMapLayerSelectionSynchronizer(
121 StyledFeatureLayerSelectionModel layerSelModel,
122 StyledFeaturesInterface<?> styledLayer, MapLayer mapLayer,
123 SelectableXMapPane mapPane, MapPaneToolBar toolBar) {
124
125 super(layerSelModel);
126 this.styledLayer = styledLayer;
127
128 this.mapLayer = mapLayer;
129 this.mapPane = mapPane;
130 this.toolBar = toolBar;
131 }
132
133 /**
134 * Called by {@link StyledLayerSelectionModel} when a the selection on other
135 * selection components (map, chart, ...) has changed. When calling this
136 *
137 * method changes the {@link MapLayer} selection according to the
138 * {@link StyledLayerSelectionModel} selection.
139 *
140 * @param evt
141 * an event
142 */
143 @Override
144 public void propertyChange(PropertyChangeEvent evt) {
145
146 if (!isEnabled())
147 return;
148
149 if (!(evt instanceof StyledLayerSelectionEvent))
150 return;
151 StyledLayerSelectionEvent selEvt = (StyledLayerSelectionEvent) evt;
152 // Only react on own layer and ignore events invoked by
153 // this component itself
154 if (selEvt.getEmitter() != layerSelModel || selectionChangeCausedByMe)
155 return;
156 // Apply new selection on table (except this event is one of
157 // some more events)
158 Vector<String> newSelection = layerSelModel.getSelection();
159 if (newSelection == null)
160 return;
161
162 // Avoid event circles in valueChanged(..)
163 selectionChangeCausedByMe = true;
164
165 changeLayerStyle(newSelection);
166
167 // Danger of event circles in valueChanged(..) banned
168 selectionChangeCausedByMe = false;
169 }
170
171 /**
172 * Changes the Style of the {@link MapLayer} to reflect changes of the
173 * selection.
174 *
175 * @param newSelection
176 * A {@link Vector} of SimpleFeature-IDs that are selected.
177 */
178 private void changeLayerStyle(final Vector<String> newSelection) {
179 try {
180
181 Style originalStyle = mapLayer.getStyle();
182
183 if (newSelection.isEmpty()) {
184
185 // Check if the Style contains a SELECTION FTS
186
187 FeatureTypeStyle[] clone = originalStyle.featureTypeStyles()
188 .toArray(new FeatureTypeStyle[] {}).clone();
189
190 for (FeatureTypeStyle fts : clone) {
191 if (fts.getName() != null
192 && fts.getName().equals(SELECTION_STYLING)) {
193 originalStyle.featureTypeStyles().remove(fts);
194
195 replaceRenderer();
196 // mapPane.refresh();
197
198 return;
199 }
200 }
201
202 } else {
203
204 LOGGER.debug("SELECTION .. change style");
205
206 // Saving a repaint if the selection didn't change
207 if (!selectionChanged(newSelection, originalStyle)) {
208 return;
209 }
210
211 // We take Style from the MapLayer that is displayed at the
212 // moment. We do not use the styledLayer.getStyle, because in
213 // the atlas, this always return the default style, but
214 // additional styles might be selected.
215 // Taking the style from the mapLayer indicated, that we have to
216 // remove any selection rules first.
217
218 FeatureTypeStyle selectionFTStyle = StylingUtil
219 .createSelectionStyle(styledLayer.getGeoObject());
220
221 selectionFTStyle.setName(SELECTION_STYLING);
222
223 /**
224 *
225 * Add a Filter to the selectionMapStyle, so that it is only
226 * used on objects that are selected. <br/>
227 * To optimize rendering speed, the filter checks whether more
228 * than half of the features are selected. If so, the filter is
229 * inverted to be shorter.
230 *
231 */
232 final FeatureCollection<SimpleFeatureType, SimpleFeature> featureCollectionFiltered = styledLayer
233 .getFeatureCollectionFiltered();
234 if (newSelection.size() > featureCollectionFiltered.size() / 2) {
235 FeatureIterator<SimpleFeature> iterator = featureCollectionFiltered
236 .features();
237 Set<FeatureId> antiFids = new HashSet<FeatureId>();
238 try {
239 while (iterator.hasNext()) {
240 SimpleFeature next = iterator.next();
241 if (!newSelection.contains(next.getID()))
242 antiFids.add(FilterUtil.FILTER_FAC2
243 .featureId(next.getID()));
244 }
245
246 selectionFTStyle.rules().get(0).setFilter(
247 FilterUtil.FILTER_FAC2
248 .not(FilterUtil.FILTER_FAC2
249 .id(antiFids)));
250
251 } finally {
252 featureCollectionFiltered.close(iterator);
253 }
254 } else {
255 Set<FeatureId> fids = new HashSet<FeatureId>();
256 for (String fid : newSelection) {
257 fids.add(FilterUtil.FILTER_FAC2.featureId(fid));
258 }
259
260 selectionFTStyle.rules().get(0).setFilter(
261 FilterUtil.FILTER_FAC2.id(fids));
262 }
263
264 // Maybe there has already been another selection
265 // FeatureTypeStyle... Let's replace it...
266 boolean foundAndReplaced = false;
267 for (FeatureTypeStyle fts : originalStyle.featureTypeStyles()) {
268 if (fts.getName() != null
269 && fts.getName().equals(SELECTION_STYLING)) {
270 foundAndReplaced = true;
271 fts.rules().clear();
272 fts.rules().addAll(selectionFTStyle.rules());
273 break;
274 }
275 }
276 if (!foundAndReplaced) {
277 originalStyle.featureTypeStyles().add(selectionFTStyle);
278 }
279
280 // Refresh the map
281 // mapPane.refresh();
282
283 DuplicatingStyleVisitor dsv = new DuplicatingStyleVisitor();
284 dsv.visit(originalStyle);
285 Style newStyle = (Style) dsv.getCopy();
286
287 // SK-Debug
288 try {
289 //
290 StylingUtil.saveStyleToSLD(newStyle, new File(
291 "/home/stefan/Desktop/selection.sld"));
292 } catch (Exception e) {
293 }
294
295 // DuplicatingStyleVisitor dsv = new DuplicatingStyleVisitor();
296 // dsv.visit(originalStyle);
297 // Style newStyle = originalStyle;
298
299 // Style newStyle = StylingUtil.STYLE_BUILDER.createStyle();
300 // newStyle.featureTypeStyles().addAll(originalStyle.featureTypeStyles());
301 mapLayer.setStyle(newStyle);
302
303 replaceRenderer();
304 }
305
306 } catch (Exception e) {
307 LOGGER.error("Error while trying to create a selection style", e);
308 }
309 }
310
311 /**
312 * Analyses whether the selection has changed in comparison to the selection
313 * stored in the mapLayer.Style
314 *
315 * @param newSelection
316 * @param originalStyle
317 * @return
318 */
319 private boolean selectionChanged(Vector<String> newSelection,
320 Style originalStyle) {
321
322 boolean SELECTION_STYLING_foundInMapStyle = false;
323
324 /**
325 * testing, whether the selection really changed. If not, we can save
326 * one paint!
327 */
328 for (FeatureTypeStyle fts : originalStyle.featureTypeStyles()) {
329
330 if (fts.getName() != null
331 && fts.getName().equals(SELECTION_STYLING)) {
332
333 SELECTION_STYLING_foundInMapStyle = true;
334
335 Filter filter = fts.rules().get(0).getFilter();
336 if (filter instanceof Not) {
337 FidFilterImpl antiOrigFidsFilter = (FidFilterImpl) ((Not) filter)
338 .getFilter();
339
340 // Check one way
341 final Set<String> antiFids = antiOrigFidsFilter
342 .getFidsSet();
343
344 if (antiFids.isEmpty() && !newSelection.isEmpty())
345 return true;
346
347 for (String fid : antiFids) {
348 if (newSelection.contains(fid)) {
349 return true;
350 }
351 }
352
353 // Check the other way
354 for (String fid : newSelection) {
355 if (antiFids.contains(fid)) {
356 return true;
357 }
358 }
359
360 } else {
361 FidFilterImpl origFidsFilter = (FidFilterImpl) filter;
362
363 // Check one way
364 final Set<String> fids = origFidsFilter.getFidsSet();
365
366 if (fids.isEmpty() && newSelection.isEmpty())
367 return false;
368 if (fids.size() != newSelection.size())
369 return true;
370
371 for (String fid : fids) {
372 if (!newSelection.contains(fid)) {
373 return true;
374 }
375 }
376
377 // Check the other way
378 for (String fid : newSelection) {
379 if (!fids.contains(fid)) {
380 return true;
381 }
382 }
383
384 }
385
386 break;
387 }
388 }
389
390 if (!SELECTION_STYLING_foundInMapStyle && !newSelection.isEmpty())
391 return true;
392
393 return false;
394 }
395
396 /**
397 * Replaces the local renderer
398 */
399 private void replaceRenderer() {
400 // //
401 // // // Has to be done before we apply the new Renderer
402 // // mapLayer.setStyle(style);
403 //
404 // GTRenderer oldRenderer = mapPane.getLocalRenderer();
405 //
406 // /**
407 // * Explicitly putting a new instance of LabelCacheDefault into the
408 // * renderer instance, so JMapPane doesn't reuse the old one. This is
409 // * very useful when changing the TextSymbolizer with AtlasStyler<br/>
410 // * SK 9.7.09: It's not enought to user LabelCache.clear(). We can not
411 // * reuse the old Renderer - better to create a new one!
412 // */
413 // final GTRenderer newRenderer = GTUtil.createGTRenderer();
414 //
415 // final HashMap<Object, Object> rendererHints = defaultGTRenderingHints;
416 // rendererHints.put(StreamingRenderer.LABEL_CACHE_KEY,
417 // new LabelCacheImpl());
418 //
419 // newRenderer.setRendererHints(rendererHints);
420 // mapPane.setLocalRenderer(newRenderer);
421 //
422 // if (oldRenderer != null) {
423 //
424 // RenderingHints java2DHints = oldRenderer.getJava2DHints();
425 // if (java2DHints != null) {
426 // newRenderer.setJava2DHints(java2DHints);
427 // }
428 //
429 // oldRenderer.setContext(null);
430 // oldRenderer = null;
431 // }
432
433 mapPane.refresh();
434
435 }
436
437 /**
438 * Used to synchronize {@link FeatureSelectedEvent}s with the
439 * {@link StyledFeatureLayerSelectionModel}
440 */
441 @Override
442 public void performMapPaneEvent(MapPaneEvent e) {
443
444 // Ignore event if it is caused by us or the synchronizer is disabled.
445 if (!isEnabled() || selectionChangeCausedByMe)
446 return;
447
448 if (!(e instanceof FeatureSelectedEvent)) {
449 // LOGGER.debug("Ignoring event " + e);
450 return;
451 }
452
453 /**
454 * Only listen to FeatureSelectedEvents if an appropriate tool is
455 * selected.
456 */
457 final int selectedTool = toolBar.getSelectedTool();
458 if (selectedTool != MapPaneToolBar.TOOL_SELECTION_ADD
459 && selectedTool != MapPaneToolBar.TOOL_SELECTION_REMOVE
460 && selectedTool != MapPaneToolBar.TOOL_SELECTION_SET) {
461 return;
462 }
463
464 // only listen to events directly coming from JMapPane
465 // selection (ignore selections from FilterDialog)
466 if (e.getSourceObject() != this.mapPane)
467 return;
468
469 /**
470 * Checking, that the FeatureSelectedEvent actually contains features
471 * from this layer
472 */
473 FeatureSelectedEvent fse = (FeatureSelectedEvent) e;
474 final String sourceID = fse.getSourceLayer().getTitle();
475 final String syncForID = mapLayer.getTitle();
476 if (sourceID != null && syncForID != null
477 && !sourceID.equals(syncForID)) {
478 LOGGER.debug("Ignoring a FeatureSelectedEvent from " + sourceID);
479 return;
480 }
481
482 // LOGGER.debug("do event " + fse);
483
484 // Avoid event circles in propertyChange(..)
485 selectionChangeCausedByMe = true;
486
487 final FeatureCollection<SimpleFeatureType, SimpleFeature> selectionResult = fse
488 .getSelectionResult();
489 Iterator<SimpleFeature> fi = selectionResult.iterator();
490 try {
491
492 // reset the selection of the DpLayerSelectionModel
493 // layerSelModel.setValueIsAdjusting(true);
494
495 if (selectedTool == MapPaneToolBar.TOOL_SELECTION_ADD) {
496 layerSelModel.setValueIsAdjusting(true);
497
498 for (int fIdx = 0; fi.hasNext(); fIdx++) {
499 SimpleFeature f = fi.next();
500 layerSelModel.addSelection(f.getID());
501 }
502 layerSelModel.setValueIsAdjusting(false);
503 } else if (selectedTool == MapPaneToolBar.TOOL_SELECTION_SET) {
504 layerSelModel.setValueIsAdjusting(true);
505 layerSelModel.clearSelection();
506
507 for (int fIdx = 0; fi.hasNext(); fIdx++) {
508 SimpleFeature f = fi.next();
509 layerSelModel.addSelection(f.getID());
510 }
511
512 // LOGGER.debug("Setting selection to " + fi.());
513
514 layerSelModel.setValueIsAdjusting(false);
515 } else if (selectedTool == MapPaneToolBar.TOOL_SELECTION_REMOVE) {
516 layerSelModel.setValueIsAdjusting(true);
517 for (int fIdx = 0; fi.hasNext(); fIdx++) {
518 SimpleFeature f = fi.next();
519 layerSelModel.removeSelection(f.getID());
520 }
521 layerSelModel.setValueIsAdjusting(false);
522 }
523
524 } finally {
525 selectionResult.close(fi);
526 }
527
528 // Show selected features in the map, which is not automatically done by
529 // the origin: FeatureSelectedEvent
530 changeLayerStyle(layerSelModel.getSelection());
531
532 // Danger of event circles in propertyChange(..) banned
533 selectionChangeCausedByMe = false;
534 }
535
536 }

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