source: josm/trunk/src/org/openstreetmap/josm/io/remotecontrol/handler/LoadAndZoomHandler.java@ 5790

Last change on this file since 5790 was 5790, checked in by akks, 11 years ago

fix #8426: add_tags in remote control: better changed tags detection,
fix EDT violations and NPE when layer is closed while adding tags
added GuiHelper.executeByMainWorkerInEDT to execute GUI-related tasks sequentially from non-EDT threads

  • Property svn:eol-style set to native
File size: 11.9 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.io.remotecontrol.handler;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.geom.Area;
7import java.awt.geom.Rectangle2D;
8import java.io.UnsupportedEncodingException;
9import java.net.URLDecoder;
10import java.util.HashSet;
11import java.util.Map;
12import java.util.Set;
13import java.util.concurrent.Future;
14
15import org.openstreetmap.josm.Main;
16import org.openstreetmap.josm.actions.AutoScaleAction;
17import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTask;
18import org.openstreetmap.josm.actions.downloadtasks.DownloadTask;
19import org.openstreetmap.josm.actions.downloadtasks.PostDownloadHandler;
20import org.openstreetmap.josm.data.Bounds;
21import org.openstreetmap.josm.data.coor.LatLon;
22import org.openstreetmap.josm.data.osm.BBox;
23import org.openstreetmap.josm.data.osm.DataSet;
24import org.openstreetmap.josm.data.osm.Node;
25import org.openstreetmap.josm.data.osm.OsmPrimitive;
26import org.openstreetmap.josm.data.osm.Relation;
27import org.openstreetmap.josm.data.osm.Way;
28import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
29import org.openstreetmap.josm.gui.util.GuiHelper;
30import org.openstreetmap.josm.io.remotecontrol.AddTagsDialog;
31import org.openstreetmap.josm.io.remotecontrol.PermissionPrefWithDefault;
32import org.openstreetmap.josm.tools.Utils;
33
34/**
35 * Handler for load_and_zoom request.
36 */
37public class LoadAndZoomHandler extends RequestHandler
38{
39 /**
40 * The remote control command name used to load data and zoom.
41 */
42 public static final String command = "load_and_zoom";
43
44 /**
45 * The remote control command name used to zoom.
46 */
47 public static final String command2 = "zoom";
48
49 // Mandatory arguments
50 private double minlat;
51 private double maxlat;
52 private double minlon;
53 private double maxlon;
54
55 // Optional argument 'select'
56 private final Set<Long> ways = new HashSet<Long>();
57 private final Set<Long> nodes = new HashSet<Long>();
58 private final Set<Long> relations = new HashSet<Long>();
59
60 @Override
61 public String getPermissionMessage()
62 {
63 String msg = tr("Remote Control has been asked to load data from the API.") +
64 "<br>" + tr("Bounding box: ") + new BBox(minlon, minlat, maxlon, maxlat).toStringCSV(", ");
65 if (args.containsKey("select") && ways.size()+nodes.size()+relations.size() > 0) {
66 msg += "<br>" + tr("Sel.: Rel.:{0} / Ways:{1} / Nodes:{2}", relations.size(), ways.size(), nodes.size());
67 }
68 return msg;
69 }
70
71 @Override
72 public String[] getMandatoryParams()
73 {
74 return new String[] { "bottom", "top", "left", "right" };
75 }
76
77 @Override
78 protected void handleRequest() throws RequestHandlerErrorException
79 {
80 DownloadTask osmTask = new DownloadOsmTask();
81 try {
82 boolean newLayer = isLoadInNewLayer();
83
84 if(command.equals(myCommand))
85 {
86 if (!PermissionPrefWithDefault.LOAD_DATA.isAllowed())
87 {
88 System.out.println("RemoteControl: download forbidden by preferences");
89 }
90 else
91 {
92 Area toDownload = null;
93 if (!newLayer) {
94 // find out whether some data has already been downloaded
95 Area present = null;
96 DataSet ds = Main.main.getCurrentDataSet();
97 if (ds != null) {
98 present = ds.getDataSourceArea();
99 }
100 if (present != null && !present.isEmpty()) {
101 toDownload = new Area(new Rectangle2D.Double(minlon,minlat,maxlon-minlon,maxlat-minlat));
102 toDownload.subtract(present);
103 if (!toDownload.isEmpty())
104 {
105 // the result might not be a rectangle (L shaped etc)
106 Rectangle2D downloadBounds = toDownload.getBounds2D();
107 minlat = downloadBounds.getMinY();
108 minlon = downloadBounds.getMinX();
109 maxlat = downloadBounds.getMaxY();
110 maxlon = downloadBounds.getMaxX();
111 }
112 }
113 }
114 if (toDownload != null && toDownload.isEmpty())
115 {
116 System.out.println("RemoteControl: no download necessary");
117 }
118 else
119 {
120 Future<?> future = osmTask.download(newLayer, new Bounds(minlat,minlon,maxlat,maxlon), null /* let the task manage the progress monitor */);
121 Main.worker.submit(new PostDownloadHandler(osmTask, future));
122 }
123 }
124 }
125 } catch (Exception ex) {
126 System.out.println("RemoteControl: Error parsing load_and_zoom remote control request:");
127 ex.printStackTrace();
128 throw new RequestHandlerErrorException();
129 }
130
131 /**
132 * deselect objects if parameter addtags given
133 */
134 if (args.containsKey("addtags")) {
135 GuiHelper.executeByMainWorkerInEDT(new Runnable() {
136 public void run() {
137 DataSet ds = Main.main.getCurrentDataSet();
138 if(ds == null) // e.g. download failed
139 return;
140 ds.clearSelection();
141 }
142 });
143 }
144
145 final Bounds bbox = new Bounds(new LatLon(minlat, minlon), new LatLon(maxlat, maxlon));
146 if (args.containsKey("select") && PermissionPrefWithDefault.CHANGE_SELECTION.isAllowed()) {
147 // select objects after downloading, zoom to selection.
148 GuiHelper.executeByMainWorkerInEDT(new Runnable() {
149 public void run() {
150 HashSet<OsmPrimitive> newSel = new HashSet<OsmPrimitive>();
151 DataSet ds = Main.main.getCurrentDataSet();
152 if(ds == null) // e.g. download failed
153 return;
154 for (Way w : ds.getWays()) {
155 if (ways.contains(w.getId())) {
156 newSel.add(w);
157 }
158 }
159 ways.clear();
160 for (Node n : ds.getNodes()) {
161 if (nodes.contains(n.getId())) {
162 newSel.add(n);
163 }
164 }
165 nodes.clear();
166 for (Relation r : ds.getRelations()) {
167 if (relations.contains(r.getId())) {
168 newSel.add(r);
169 }
170 }
171 relations.clear();
172 ds.setSelected(newSel);
173 if (PermissionPrefWithDefault.CHANGE_VIEWPORT.isAllowed()) {
174 // zoom_mode=(download|selection), defaults to selection
175 if (!"download".equals(args.get("zoom_mode")) && !newSel.isEmpty()) {
176 AutoScaleAction.autoScale("selection");
177 } else {
178 zoom(bbox);
179 }
180 }
181 if (Main.isDisplayingMapView() && Main.map.relationListDialog != null) {
182 Main.map.relationListDialog.selectRelations(null); // unselect all relations to fix #7342
183 Main.map.relationListDialog.dataChanged(null);
184 Main.map.relationListDialog.selectRelations(Utils.filteredCollection(newSel, Relation.class));
185 }
186 }
187 });
188 } else if (PermissionPrefWithDefault.CHANGE_VIEWPORT.isAllowed()) {
189 // after downloading, zoom to downloaded area.
190 zoom(bbox);
191 }
192
193 addTags(args);
194 }
195
196 /*
197 * parse addtags parameters Example URL (part):
198 * addtags=wikipedia:de%3DResidenzschloss Dresden|name:en%3DDresden Castle
199 */
200 static void addTags(final Map<String, String> args) {
201 if (args.containsKey("addtags")) {
202 GuiHelper.executeByMainWorkerInEDT(new Runnable() {
203
204 public void run() {
205 String[] tags = null;
206 try {
207 tags = URLDecoder.decode(args.get("addtags"), "UTF-8").split("\\|");
208 } catch (UnsupportedEncodingException e) {
209 throw new RuntimeException();
210 }
211 Set<String> tagSet = new HashSet<String>();
212 for (String tag : tags) {
213 if (!tag.trim().isEmpty() && tag.contains("=")) {
214 tagSet.add(tag.trim());
215 }
216 }
217 if (!tagSet.isEmpty()) {
218 String[][] keyValue = new String[tagSet.size()][2];
219 int i = 0;
220 for (String tag : tagSet) {
221 // support a = b===c as "a"="b===c"
222 String [] pair = tag.split("\\s*=\\s*",2);
223 keyValue[i][0] = pair[0];
224 keyValue[i][1] = pair.length<2 ? "": pair[1];
225 i++;
226 }
227
228 new AddTagsDialog(keyValue).showDialog();
229 }
230 }
231 });
232 }
233 }
234
235 protected void zoom(final Bounds bounds) {
236 // make sure this isn't called unless there *is* a MapView
237 if (Main.isDisplayingMapView()) {
238 GuiHelper.executeByMainWorkerInEDT(new Runnable() {
239 public void run() {
240 BoundingXYVisitor bbox = new BoundingXYVisitor();
241 bbox.visit(bounds);
242 Main.map.mapView.recalculateCenterScale(bbox);
243 }
244 });
245 }
246 }
247
248 @Override
249 public PermissionPrefWithDefault getPermissionPref() {
250 return null;
251 }
252
253 @Override
254 protected void validateRequest() throws RequestHandlerBadRequestException {
255 // Process mandatory arguments
256 minlat = 0;
257 maxlat = 0;
258 minlon = 0;
259 maxlon = 0;
260 try {
261 minlat = LatLon.roundToOsmPrecision(Double.parseDouble(args.get("bottom")));
262 maxlat = LatLon.roundToOsmPrecision(Double.parseDouble(args.get("top")));
263 minlon = LatLon.roundToOsmPrecision(Double.parseDouble(args.get("left")));
264 maxlon = LatLon.roundToOsmPrecision(Double.parseDouble(args.get("right")));
265 } catch (NumberFormatException e) {
266 throw new RequestHandlerBadRequestException("NumberFormatException ("+e.getMessage()+")");
267 }
268
269 // Process optional argument 'select'
270 if (args.containsKey("select")) {
271 ways.clear();
272 nodes.clear();
273 relations.clear();
274 for (String item : args.get("select").split(",")) {
275 try {
276 if (item.startsWith("way")) {
277 ways.add(Long.parseLong(item.substring(3)));
278 } else if (item.startsWith("node")) {
279 nodes.add(Long.parseLong(item.substring(4)));
280 } else if (item.startsWith("relation")) {
281 relations.add(Long.parseLong(item.substring(8)));
282 } else if (item.startsWith("rel")) {
283 relations.add(Long.parseLong(item.substring(3)));
284 } else {
285 System.out.println("RemoteControl: invalid selection '"+item+"' ignored");
286 }
287 } catch (NumberFormatException e) {
288 System.out.println("RemoteControl: invalid selection '"+item+"' ignored");
289 }
290 }
291 }
292 }
293}
Note: See TracBrowser for help on using the repository browser.