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

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