001    // License: GPL. For details, see LICENSE file.
002    package org.openstreetmap.josm.io.remotecontrol.handler;
003    
004    import static org.openstreetmap.josm.tools.I18n.tr;
005    
006    import java.awt.geom.Area;
007    import java.awt.geom.Rectangle2D;
008    import java.io.UnsupportedEncodingException;
009    import java.net.URLDecoder;
010    import java.util.HashSet;
011    import java.util.Map;
012    import java.util.concurrent.Future;
013    
014    import org.openstreetmap.josm.Main;
015    import org.openstreetmap.josm.actions.AutoScaleAction;
016    import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTask;
017    import org.openstreetmap.josm.actions.downloadtasks.DownloadTask;
018    import org.openstreetmap.josm.actions.downloadtasks.PostDownloadHandler;
019    import org.openstreetmap.josm.data.Bounds;
020    import org.openstreetmap.josm.data.coor.LatLon;
021    import org.openstreetmap.josm.data.osm.DataSet;
022    import org.openstreetmap.josm.data.osm.Node;
023    import org.openstreetmap.josm.data.osm.OsmPrimitive;
024    import org.openstreetmap.josm.data.osm.Relation;
025    import org.openstreetmap.josm.data.osm.Way;
026    import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
027    import org.openstreetmap.josm.io.remotecontrol.AddTagsDialog;
028    import org.openstreetmap.josm.io.remotecontrol.PermissionPrefWithDefault;
029    import org.openstreetmap.josm.tools.Utils;
030    
031    /**
032     * Handler for load_and_zoom request.
033     */
034    public class LoadAndZoomHandler extends RequestHandler
035    {
036        public static final String command = "load_and_zoom";
037        public static final String command2 = "zoom";
038    
039        @Override
040        public String getPermissionMessage()
041        {
042            return tr("Remote Control has been asked to load data from the API.") +
043                    "<br>" + tr("Request details: {0}", request);
044        }
045    
046        @Override
047        public String[] getMandatoryParams()
048        {
049            return new String[] { "bottom", "top", "left", "right" };
050        }
051    
052        @Override
053        protected void handleRequest() throws RequestHandlerErrorException
054        {
055            DownloadTask osmTask = new DownloadOsmTask();
056            double minlat = 0;
057            double maxlat = 0;
058            double minlon = 0;
059            double maxlon = 0;
060            try {
061                minlat = LatLon.roundToOsmPrecision(Double.parseDouble(args.get("bottom")));
062                maxlat = LatLon.roundToOsmPrecision(Double.parseDouble(args.get("top")));
063                minlon = LatLon.roundToOsmPrecision(Double.parseDouble(args.get("left")));
064                maxlon = LatLon.roundToOsmPrecision(Double.parseDouble(args.get("right")));
065                boolean newLayer = isLoadInNewLayer();
066    
067                if(command.equals(myCommand))
068                {
069                    if (!PermissionPrefWithDefault.LOAD_DATA.isAllowed())
070                    {
071                        System.out.println("RemoteControl: download forbidden by preferences");
072                    }
073                    else
074                    {
075                        Area toDownload = null;
076                        if (!newLayer) {
077                            // find out whether some data has already been downloaded
078                            Area present = null;
079                            DataSet ds = Main.main.getCurrentDataSet();
080                            if (ds != null) {
081                                present = ds.getDataSourceArea();
082                            }
083                            if (present != null && !present.isEmpty()) {
084                                toDownload = new Area(new Rectangle2D.Double(minlon,minlat,maxlon-minlon,maxlat-minlat));
085                                toDownload.subtract(present);
086                                if (!toDownload.isEmpty())
087                                {
088                                    // the result might not be a rectangle (L shaped etc)
089                                    Rectangle2D downloadBounds = toDownload.getBounds2D();
090                                    minlat = downloadBounds.getMinY();
091                                    minlon = downloadBounds.getMinX();
092                                    maxlat = downloadBounds.getMaxY();
093                                    maxlon = downloadBounds.getMaxX();
094                                }
095                            }
096                        }
097                        if (toDownload != null && toDownload.isEmpty())
098                        {
099                            System.out.println("RemoteControl: no download necessary");
100                        }
101                        else
102                        {
103                            Future<?> future = osmTask.download(newLayer, new Bounds(minlat,minlon,maxlat,maxlon), null /* let the task manage the progress monitor */);
104                            Main.worker.submit(new PostDownloadHandler(osmTask, future));
105                        }
106                    }
107                }
108            } catch (Exception ex) {
109                System.out.println("RemoteControl: Error parsing load_and_zoom remote control request:");
110                ex.printStackTrace();
111                throw new RequestHandlerErrorException();
112            }
113    
114            /**
115             * deselect objects if parameter addtags given
116             */
117            if (args.containsKey("addtags")) {
118                Main.worker.execute(new Runnable() {
119                    public void run() {
120                        DataSet ds = Main.main.getCurrentDataSet();
121                        if(ds == null) // e.g. download failed
122                            return;
123                        ds.clearSelection();
124                    }
125                });
126            }
127    
128            if (args.containsKey("select") && PermissionPrefWithDefault.CHANGE_SELECTION.isAllowed()) {
129                // select objects after downloading, zoom to selection.
130                final String selection = args.get("select");
131                Main.worker.execute(new Runnable() {
132                    public void run() {
133                        HashSet<Long> ways = new HashSet<Long>();
134                        HashSet<Long> nodes = new HashSet<Long>();
135                        HashSet<Long> relations = new HashSet<Long>();
136                        HashSet<OsmPrimitive> newSel = new HashSet<OsmPrimitive>();
137                        for (String item : selection.split(",")) {
138                            if (item.startsWith("way")) {
139                                ways.add(Long.parseLong(item.substring(3)));
140                            } else if (item.startsWith("node")) {
141                                nodes.add(Long.parseLong(item.substring(4)));
142                            } else if (item.startsWith("relation")) {
143                                relations.add(Long.parseLong(item.substring(8)));
144                            } else if (item.startsWith("rel")) {
145                                relations.add(Long.parseLong(item.substring(3)));
146                            } else {
147                                System.out.println("RemoteControl: invalid selection '"+item+"' ignored");
148                            }
149                        }
150                        DataSet ds = Main.main.getCurrentDataSet();
151                        if(ds == null) // e.g. download failed
152                            return;
153                        for (Way w : ds.getWays()) {
154                            if (ways.contains(w.getId())) {
155                                newSel.add(w);
156                            }
157                        }
158                        for (Node n : ds.getNodes()) {
159                            if (nodes.contains(n.getId())) {
160                                newSel.add(n);
161                            }
162                        }
163                        for (Relation r : ds.getRelations()) {
164                            if (relations.contains(r.getId())) {
165                                newSel.add(r);
166                            }
167                        }
168                        ds.setSelected(newSel);
169                        if (PermissionPrefWithDefault.CHANGE_VIEWPORT.isAllowed()) {
170                            AutoScaleAction.autoScale("selection");
171                        }
172                        if (Main.isDisplayingMapView() && Main.map.relationListDialog != null) {
173                            Main.map.relationListDialog.selectRelations(null); // unselect all relations to fix #7342
174                            Main.map.relationListDialog.dataChanged(null);
175                            Main.map.relationListDialog.selectRelations(Utils.filteredCollection(newSel, Relation.class));
176                        }
177                    }
178                });
179            } else if (PermissionPrefWithDefault.CHANGE_VIEWPORT.isAllowed()) {
180                // after downloading, zoom to downloaded area.
181                zoom(minlat, maxlat, minlon, maxlon);
182            }
183    
184            addTags(args);
185    
186        }
187    
188        /*
189         * parse addtags parameters Example URL (part):
190         * addtags=wikipedia:de%3DResidenzschloss Dresden|name:en%3DDresden Castle
191         */
192        static void addTags(final Map<String, String> args) {
193            if (args.containsKey("addtags")) {
194                Main.worker.execute(new Runnable() {
195    
196                    public void run() {
197                        String[] tags = null;
198                        try {
199                            tags = URLDecoder.decode(args.get("addtags"), "UTF-8").split("\\|");
200                        } catch (UnsupportedEncodingException e) {
201                            throw new RuntimeException();
202                        }
203                        String[][] keyValue = new String[tags.length][2];
204                        for (int i = 0; i < tags.length; i++) {
205                            keyValue[i] = tags[i].split("=");
206    
207                            keyValue[i][0] = keyValue[i][0];
208                            keyValue[i][1] = keyValue[i][1];
209                        }
210    
211                        new AddTagsDialog(keyValue);
212                    }
213                });
214            }
215        }
216    
217        protected void zoom(double minlat, double maxlat, double minlon, double maxlon) {
218            final Bounds bounds = new Bounds(new LatLon(minlat, minlon),
219                    new LatLon(maxlat, maxlon));
220    
221            // make sure this isn't called unless there *is* a MapView
222            //
223            if (Main.isDisplayingMapView()) {
224                Main.worker.execute(new Runnable() {
225                    public void run() {
226                        BoundingXYVisitor bbox = new BoundingXYVisitor();
227                        bbox.visit(bounds);
228                        Main.map.mapView.recalculateCenterScale(bbox);
229                    }
230                });
231            }
232        }
233    
234        @Override
235        public PermissionPrefWithDefault getPermissionPref() {
236            return null;
237        }
238    }