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

Last change on this file since 12846 was 12846, checked in by bastiK, 17 months ago

see #15229 - use Config.getPref() wherever possible

  • Property svn:eol-style set to native
File size: 13.0 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.util.Collection;
9import java.util.Collections;
10import java.util.HashSet;
11import java.util.Set;
12import java.util.concurrent.Future;
13
14import org.openstreetmap.josm.actions.AutoScaleAction;
15import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTask;
16import org.openstreetmap.josm.actions.downloadtasks.DownloadTask;
17import org.openstreetmap.josm.actions.downloadtasks.PostDownloadHandler;
18import org.openstreetmap.josm.data.Bounds;
19import org.openstreetmap.josm.data.coor.LatLon;
20import org.openstreetmap.josm.data.osm.BBox;
21import org.openstreetmap.josm.data.osm.DataSet;
22import org.openstreetmap.josm.data.osm.OsmPrimitive;
23import org.openstreetmap.josm.data.osm.Relation;
24import org.openstreetmap.josm.data.osm.SimplePrimitiveId;
25import org.openstreetmap.josm.data.osm.search.SearchCompiler;
26import org.openstreetmap.josm.data.osm.search.SearchParseError;
27import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
28import org.openstreetmap.josm.gui.MainApplication;
29import org.openstreetmap.josm.gui.MapFrame;
30import org.openstreetmap.josm.gui.util.GuiHelper;
31import org.openstreetmap.josm.io.remotecontrol.AddTagsDialog;
32import org.openstreetmap.josm.io.remotecontrol.PermissionPrefWithDefault;
33import org.openstreetmap.josm.tools.Logging;
34import org.openstreetmap.josm.tools.SubclassFilteredCollection;
35import org.openstreetmap.josm.tools.Utils;
36
37/**
38 * Handler for {@code load_and_zoom} and {@code zoom} requests.
39 * @since 3707
40 */
41public class LoadAndZoomHandler extends RequestHandler {
42
43    /**
44     * The remote control command name used to load data and zoom.
45     */
46    public static final String command = "load_and_zoom";
47
48    /**
49     * The remote control command name used to zoom.
50     */
51    public static final String command2 = "zoom";
52
53    // Mandatory arguments
54    private double minlat;
55    private double maxlat;
56    private double minlon;
57    private double maxlon;
58
59    // Optional argument 'select'
60    private final Set<SimplePrimitiveId> toSelect = new HashSet<>();
61
62    @Override
63    public String getPermissionMessage() {
64        String msg = tr("Remote Control has been asked to load data from the API.") +
65                "<br>" + tr("Bounding box: ") + new BBox(minlon, minlat, maxlon, maxlat).toStringCSV(", ");
66        if (args.containsKey("select") && !toSelect.isEmpty()) {
67            msg += "<br>" + tr("Selection: {0}", toSelect.size());
68        }
69        return msg;
70    }
71
72    @Override
73    public String[] getMandatoryParams() {
74        return new String[] {"bottom", "top", "left", "right"};
75    }
76
77    @Override
78    public String[] getOptionalParams() {
79        return new String[] {"new_layer", "layer_name", "addtags", "select", "zoom_mode", "changeset_comment", "changeset_source", "search"};
80    }
81
82    @Override
83    public String getUsage() {
84        return "download a bounding box from the API, zoom to the downloaded area and optionally select one or more objects";
85    }
86
87    @Override
88    public String[] getUsageExamples() {
89        return getUsageExamples(myCommand);
90    }
91
92    @Override
93    public String[] getUsageExamples(String cmd) {
94        if (command.equals(cmd)) {
95            return new String[] {
96                    "/load_and_zoom?addtags=wikipedia:de=Wei%C3%9Fe_Gasse|maxspeed=5&select=way23071688,way23076176,way23076177," +
97                            "&left=13.740&right=13.741&top=51.05&bottom=51.049",
98                    "/load_and_zoom?left=8.19&right=8.20&top=48.605&bottom=48.590&select=node413602999&new_layer=true"};
99        } else {
100            return new String[] {
101            "/zoom?left=8.19&right=8.20&top=48.605&bottom=48.590&select=node413602999",
102            "/zoom?left=8.19&right=8.20&top=48.605&bottom=48.590&search=highway+OR+railway",
103            };
104        }
105    }
106
107    @Override
108    protected void handleRequest() throws RequestHandlerErrorException {
109        DownloadTask osmTask = new DownloadOsmTask() {
110            {
111                newLayerName = args.get("layer_name");
112            }
113        };
114        try {
115            boolean newLayer = isLoadInNewLayer();
116
117            if (command.equals(myCommand)) {
118                if (!PermissionPrefWithDefault.LOAD_DATA.isAllowed()) {
119                    Logging.info("RemoteControl: download forbidden by preferences");
120                } else {
121                    Area toDownload = null;
122                    if (!newLayer) {
123                        // find out whether some data has already been downloaded
124                        Area present = null;
125                        DataSet ds = MainApplication.getLayerManager().getEditDataSet();
126                        if (ds != null) {
127                            present = ds.getDataSourceArea();
128                        }
129                        if (present != null && !present.isEmpty()) {
130                            toDownload = new Area(new Rectangle2D.Double(minlon, minlat, maxlon-minlon, maxlat-minlat));
131                            toDownload.subtract(present);
132                            if (!toDownload.isEmpty()) {
133                                // the result might not be a rectangle (L shaped etc)
134                                Rectangle2D downloadBounds = toDownload.getBounds2D();
135                                minlat = downloadBounds.getMinY();
136                                minlon = downloadBounds.getMinX();
137                                maxlat = downloadBounds.getMaxY();
138                                maxlon = downloadBounds.getMaxX();
139                            }
140                        }
141                    }
142                    if (toDownload != null && toDownload.isEmpty()) {
143                        Logging.info("RemoteControl: no download necessary");
144                    } else {
145                        Future<?> future = osmTask.download(newLayer, new Bounds(minlat, minlon, maxlat, maxlon),
146                                null /* let the task manage the progress monitor */);
147                        MainApplication.worker.submit(new PostDownloadHandler(osmTask, future));
148                    }
149                }
150            }
151        } catch (RuntimeException ex) { // NOPMD
152            Logging.warn("RemoteControl: Error parsing load_and_zoom remote control request:");
153            Logging.error(ex);
154            throw new RequestHandlerErrorException(ex);
155        }
156
157        /**
158         * deselect objects if parameter addtags given
159         */
160        if (args.containsKey("addtags")) {
161            GuiHelper.executeByMainWorkerInEDT(() -> {
162                DataSet ds = MainApplication.getLayerManager().getEditDataSet();
163                if (ds == null) // e.g. download failed
164                    return;
165                ds.clearSelection();
166            });
167        }
168
169        final Collection<OsmPrimitive> forTagAdd = new HashSet<>();
170        final Bounds bbox = new Bounds(minlat, minlon, maxlat, maxlon);
171        if (args.containsKey("select") && PermissionPrefWithDefault.CHANGE_SELECTION.isAllowed()) {
172            // select objects after downloading, zoom to selection.
173            GuiHelper.executeByMainWorkerInEDT(() -> {
174                Set<OsmPrimitive> newSel = new HashSet<>();
175                DataSet ds = MainApplication.getLayerManager().getEditDataSet();
176                if (ds == null) // e.g. download failed
177                    return;
178                for (SimplePrimitiveId id : toSelect) {
179                    final OsmPrimitive p = ds.getPrimitiveById(id);
180                    if (p != null) {
181                        newSel.add(p);
182                        forTagAdd.add(p);
183                    }
184                }
185                toSelect.clear();
186                ds.setSelected(newSel);
187                zoom(newSel, bbox);
188                MapFrame map = MainApplication.getMap();
189                if (MainApplication.isDisplayingMapView() && map.relationListDialog != null) {
190                    map.relationListDialog.selectRelations(null); // unselect all relations to fix #7342
191                    map.relationListDialog.dataChanged(null);
192                    map.relationListDialog.selectRelations(Utils.filteredCollection(newSel, Relation.class));
193                }
194            });
195        } else if (args.containsKey("search") && PermissionPrefWithDefault.CHANGE_SELECTION.isAllowed()) {
196            try {
197                final SearchCompiler.Match search = SearchCompiler.compile(args.get("search"));
198                MainApplication.worker.submit(() -> {
199                    final DataSet ds = MainApplication.getLayerManager().getEditDataSet();
200                    final Collection<OsmPrimitive> filteredPrimitives = SubclassFilteredCollection.filter(ds.allPrimitives(), search);
201                    ds.setSelected(filteredPrimitives);
202                    forTagAdd.addAll(filteredPrimitives);
203                    zoom(filteredPrimitives, bbox);
204                });
205            } catch (SearchParseError ex) {
206                Logging.error(ex);
207                throw new RequestHandlerErrorException(ex);
208            }
209        } else {
210            // after downloading, zoom to downloaded area.
211            zoom(Collections.<OsmPrimitive>emptySet(), bbox);
212        }
213
214        // add changeset tags after download if necessary
215        if (args.containsKey("changeset_comment") || args.containsKey("changeset_source")) {
216            MainApplication.worker.submit(() -> {
217                if (MainApplication.getLayerManager().getEditDataSet() != null) {
218                    if (args.containsKey("changeset_comment")) {
219                        MainApplication.getLayerManager().getEditDataSet().addChangeSetTag("comment", args.get("changeset_comment"));
220                    }
221                    if (args.containsKey("changeset_source")) {
222                        MainApplication.getLayerManager().getEditDataSet().addChangeSetTag("source", args.get("changeset_source"));
223                    }
224                }
225            });
226        }
227
228        AddTagsDialog.addTags(args, sender, forTagAdd);
229    }
230
231    protected void zoom(Collection<OsmPrimitive> primitives, final Bounds bbox) {
232        if (!PermissionPrefWithDefault.CHANGE_VIEWPORT.isAllowed()) {
233            return;
234        }
235        // zoom_mode=(download|selection), defaults to selection
236        if (!"download".equals(args.get("zoom_mode")) && !primitives.isEmpty()) {
237            AutoScaleAction.autoScale("selection");
238        } else if (MainApplication.isDisplayingMapView()) {
239            // make sure this isn't called unless there *is* a MapView
240            GuiHelper.executeByMainWorkerInEDT(() -> {
241                BoundingXYVisitor bbox1 = new BoundingXYVisitor();
242                bbox1.visit(bbox);
243                MainApplication.getMap().mapView.zoomTo(bbox1);
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 != null ? args.get("bottom") : ""));
262            maxlat = LatLon.roundToOsmPrecision(Double.parseDouble(args != null ? args.get("top") : ""));
263            minlon = LatLon.roundToOsmPrecision(Double.parseDouble(args != null ? args.get("left") : ""));
264            maxlon = LatLon.roundToOsmPrecision(Double.parseDouble(args != null ? args.get("right") : ""));
265        } catch (NumberFormatException e) {
266            throw new RequestHandlerBadRequestException("NumberFormatException ("+e.getMessage()+')', e);
267        }
268
269        // Current API 0.6 check: "The latitudes must be between -90 and 90"
270        if (!LatLon.isValidLat(minlat) || !LatLon.isValidLat(maxlat)) {
271            throw new RequestHandlerBadRequestException(tr("The latitudes must be between {0} and {1}", -90d, 90d));
272        }
273        // Current API 0.6 check: "longitudes between -180 and 180"
274        if (!LatLon.isValidLon(minlon) || !LatLon.isValidLon(maxlon)) {
275            throw new RequestHandlerBadRequestException(tr("The longitudes must be between {0} and {1}", -180d, 180d));
276        }
277        // Current API 0.6 check: "the minima must be less than the maxima"
278        if (minlat > maxlat || minlon > maxlon) {
279            throw new RequestHandlerBadRequestException(tr("The minima must be less than the maxima"));
280        }
281
282        // Process optional argument 'select'
283        if (args != null && args.containsKey("select")) {
284            toSelect.clear();
285            for (String item : args.get("select").split(",")) {
286                if (!item.isEmpty()) {
287                    try {
288                        toSelect.add(SimplePrimitiveId.fromString(item));
289                    } catch (IllegalArgumentException ex) {
290                        Logging.log(Logging.LEVEL_WARN, "RemoteControl: invalid selection '" + item + "' ignored", ex);
291                    }
292                }
293            }
294        }
295    }
296}
Note: See TracBrowser for help on using the repository browser.