View Javadoc
1   package fr.ifremer.dali.ui.swing.util.map;
2   
3   /*-
4    * #%L
5    * Dali :: UI
6    * $Id:$
7    * $HeadURL:$
8    * %%
9    * Copyright (C) 2014 - 2017 Ifremer
10   * %%
11   * This program is free software: you can redistribute it and/or modify
12   * it under the terms of the GNU Affero General Public License as published by
13   * the Free Software Foundation, either version 3 of the License, or
14   * (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 Affero General Public License
22   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23   * #L%
24   */
25  
26  import fr.ifremer.dali.map.Maps;
27  import fr.ifremer.dali.ui.swing.util.map.layer.DataFeatureLayer;
28  import fr.ifremer.quadrige3.ui.swing.ApplicationUIUtil;
29  import org.apache.commons.logging.Log;
30  import org.apache.commons.logging.LogFactory;
31  import org.geotools.data.simple.SimpleFeatureCollection;
32  import org.geotools.data.simple.SimpleFeatureIterator;
33  import org.geotools.factory.CommonFactoryFinder;
34  import org.geotools.geometry.jts.ReferencedEnvelope;
35  import org.geotools.map.Layer;
36  import org.geotools.swing.event.MapMouseEvent;
37  import org.geotools.swing.tool.CursorTool;
38  import org.opengis.feature.simple.SimpleFeature;
39  import org.opengis.filter.Filter;
40  import org.opengis.filter.FilterFactory2;
41  import org.opengis.filter.identity.FeatureId;
42  
43  import java.awt.Cursor;
44  import java.awt.Point;
45  import java.awt.geom.Point2D;
46  import java.io.IOException;
47  import java.util.HashSet;
48  import java.util.Set;
49  
50  /**
51   * Extends default ScrollWheelTool to add pan capabilities from PanTool
52   *
53   * @author peck7 on 02/06/2017.
54   */
55  public class MultipleCursorTool extends CursorTool {
56  
57      private Cursor cursor;
58      private boolean panning;
59  
60      private Point startPointInScreen;
61      private Point panningPointInScreen;
62      private Point2D startPointInWorld;
63      private ReferencedEnvelope startDisplayArea;
64  
65      private static final Log log = LogFactory.getLog(MultipleCursorTool.class);
66  
67      public MultipleCursorTool(DataMapPane mapPane) {
68          setMapPane(mapPane);
69          cursor = ApplicationUIUtil.getCustomCursor("select");
70          panning = false;
71      }
72  
73      @Override
74      public DataMapPane getMapPane() {
75          return (DataMapPane) super.getMapPane();
76      }
77  
78      /**
79       * Respond to a mouse button click.
80       * Select the underlying feature
81       *
82       * @param ev event
83       */
84      @Override
85      public void onMouseClicked(MapMouseEvent ev) {
86  
87          // Create a envelope by 10x10 pixels of screen area
88          ReferencedEnvelope envelope = ev.getEnvelopeByPixels(10);
89  
90          /*
91           * Create a Filter to select features that intersect with
92           * the bounding box
93           */
94          FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2(null);
95  
96          // Build feature list across all visible data layers
97          Set<DataFeatureLayer> selectedDataLayers = new HashSet<>();
98  
99          try {
100             for (Layer layer : getMapPane().getMapContent().layers()) {
101                 if (layer instanceof DataFeatureLayer && layer.isVisible()) {
102                     DataFeatureLayer dataLayer = (DataFeatureLayer) layer;
103                     String geometryAttributeName = layer.getFeatureSource().getSchema().getGeometryDescriptor().getLocalName();
104                     Filter filter = ff.intersects(
105                             ff.property(geometryAttributeName),
106                             ff.literal(
107                                     Maps.transformReferencedEnvelope(
108                                             envelope,
109                                             dataLayer.getBounds().getCoordinateReferenceSystem())
110                             )
111                     );
112 
113                     // Use the filter to identify the selected features
114                     SimpleFeatureCollection selectedFeatureCollection = dataLayer.getSimpleFeatureSource().getFeatures(filter);
115 
116                     Set<FeatureId> IDs = new HashSet<>();
117 
118                     try (SimpleFeatureIterator iterator = selectedFeatureCollection.features()) {
119                         while (iterator.hasNext()) {
120                             SimpleFeature feature = iterator.next();
121                             IDs.add(feature.getIdentifier());
122                             if (log.isDebugEnabled()) {
123                                 log.debug(" feature selected: " + feature.getIdentifier());
124                             }
125                         }
126 
127                     }
128 
129                     if (!IDs.isEmpty()) {
130 
131                         // This data layer is selected
132                         selectedDataLayers.add(dataLayer);
133                     } else {
134                         if (log.isDebugEnabled()) {
135                             log.debug("no feature selected");
136                         }
137                     }
138                 }
139             }
140         } catch (IOException ex) {
141             log.error("Unable to retrieve selected features", ex);
142         }
143 
144         if (!selectedDataLayers.isEmpty()) {
145 
146             // Produce an event
147             DataSelectionEvent event = new DataSelectionEvent(getMapPane(), DataSelectionEvent.Type.DATA_LAYER_SELECTED, selectedDataLayers, ev.getPoint(), ev.getWorldPos());
148             getMapPane().publishSelectionEvent(event);
149 
150         } else {
151 
152             // Empty selection event
153             DataSelectionEvent event = new DataSelectionEvent(getMapPane(), DataSelectionEvent.Type.EMPTY_SELECTION);
154             getMapPane().publishSelectionEvent(event);
155 
156         }
157     }
158 
159     /**
160      * Respond to a mouse button press event from the map mapPane. This may
161      * signal the start of a mouse drag. Records the event's window position.
162      *
163      * @param ev the mouse event
164      */
165     @Override
166     public void onMousePressed(MapMouseEvent ev) {
167         startDisplayArea = getMapPane().getDisplayArea();
168         startPointInWorld = ev.getWorldPos();
169         startPointInScreen = panningPointInScreen = ev.getPoint();
170     }
171 
172     /**
173      * Respond to a mouse dragged event. Calls {@link org.geotools.swing.MapPane#moveImage(int, int)}
174      *
175      * @param ev the mouse event
176      */
177     @Override
178     public void onMouseDragged(MapMouseEvent ev) {
179 
180         // Don't process translation if delta < 2 pixels
181         if (getMapPane().isBusy() || ev.getPoint().distance(startPointInScreen) < 2) return;
182 
183         panning = true;
184 
185         // move image
186         Point point = ev.getPoint();
187         if (!point.equals(panningPointInScreen)) {
188             getMapPane().moveImage(point.x - panningPointInScreen.x, point.y - panningPointInScreen.y);
189             panningPointInScreen = point;
190         }
191     }
192 
193     /**
194      * If this button release is the end of a mouse dragged event, requests the
195      * map mapPane to repaint the display
196      *
197      * @param ev the mouse event
198      */
199     @Override
200     public void onMouseReleased(MapMouseEvent ev) {
201 
202         if (!panning) return;
203 
204         panning = false;
205         Point2D endPointInWorld = ev.getWorldPos();
206         double transX = startPointInWorld.getX() - endPointInWorld.getX();
207         double transY = startPointInWorld.getY() - endPointInWorld.getY();
208 
209         ReferencedEnvelope endDisplayArea = new ReferencedEnvelope(startDisplayArea);
210 
211         endDisplayArea.translate(transX, transY);
212 
213         getMapPane().setDisplayArea(endDisplayArea);
214 
215         if (log.isDebugEnabled()) {
216             log.debug(String.format("Translate (x : %s, y : %s)", transX, transY));
217         }
218     }
219 
220     /**
221      * Respond to wheel action (will zoom in or out)
222      * <p>
223      * If allowed by the mapPane, a buffered image (interstitial) will be drawn
224      *
225      * @param ev event
226      */
227     @Override
228     public void onMouseWheelMoved(MapMouseEvent ev) {
229 
230         int notches = ev.getWheelAmount();
231         double zoomRatio = 1 - (DataMapPane.ZOOM_STEP_RATIO * notches);
232         Point zoomCenter = ev.getPoint();
233 
234         if (zoomRatio != 1) {
235 
236             // Apply zoom
237             getMapPane().applyZoom(zoomRatio, zoomCenter);
238 
239         }
240     }
241 
242     /**
243      * Returns false to indicate that this tool does not draw a box
244      * on the map display when the mouse is being dragged
245      */
246     @Override
247     public boolean drawDragBox() {
248         return false;
249     }
250 
251     @Override
252     public Cursor getCursor() {
253         return cursor;
254     }
255 
256 }