source: subversion/applications/editors/josm/plugins/waydownloader/src/org/openstreetmap/josm/plugins/waydownloader/WayDownloaderPlugin.java

Last change on this file was 34976, checked in by donvip, 11 days ago

see #josm17598 #josm17599 #josm17600 #josm17601 #josm17602 - remove deprecated api (patches by taylor.smock)

File size: 14.2 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.plugins.waydownloader;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.event.ActionEvent;
7import java.awt.event.KeyEvent;
8import java.util.ArrayList;
9import java.util.Collection;
10import java.util.Collections;
11import java.util.List;
12import java.util.concurrent.Future;
13
14import javax.swing.JOptionPane;
15
16import org.openstreetmap.josm.actions.JosmAction;
17import org.openstreetmap.josm.actions.MergeNodesAction;
18import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTask;
19import org.openstreetmap.josm.actions.downloadtasks.DownloadParams;
20import org.openstreetmap.josm.command.Command;
21import org.openstreetmap.josm.data.Bounds;
22import org.openstreetmap.josm.data.DataSource;
23import org.openstreetmap.josm.data.UndoRedoHandler;
24import org.openstreetmap.josm.data.coor.LatLon;
25import org.openstreetmap.josm.data.osm.DataSet;
26import org.openstreetmap.josm.data.osm.DefaultNameFormatter;
27import org.openstreetmap.josm.data.osm.Node;
28import org.openstreetmap.josm.data.osm.OsmPrimitive;
29import org.openstreetmap.josm.data.osm.Way;
30import org.openstreetmap.josm.gui.MainApplication;
31import org.openstreetmap.josm.gui.MainMenu;
32import org.openstreetmap.josm.gui.Notification;
33import org.openstreetmap.josm.gui.progress.swing.PleaseWaitProgressMonitor;
34import org.openstreetmap.josm.gui.util.GuiHelper;
35import org.openstreetmap.josm.plugins.Plugin;
36import org.openstreetmap.josm.plugins.PluginInformation;
37import org.openstreetmap.josm.spi.preferences.Config;
38import org.openstreetmap.josm.tools.Logging;
39import org.openstreetmap.josm.tools.Shortcut;
40import org.openstreetmap.josm.tools.Utils;
41
42/**
43 * Plugin class for the Way Downloader plugin
44 *
45 * @author Harry Wood
46 */
47public class WayDownloaderPlugin extends Plugin {
48
49    private Way priorConnectedWay = null;
50    private Node selectedNode = null;
51
52    /**
53     * Plugin constructor called at JOSM startup
54     * @param info plugin info
55     */
56    public WayDownloaderPlugin(PluginInformation info) {
57        super(info);
58        //add WayDownloadAction to tools menu
59        MainMenu.add(MainApplication.getMenu().moreToolsMenu, new WayDownloadAction());
60    }
61
62    private class WayDownloadAction extends JosmAction implements Runnable {
63
64        /** Set up the action (text appearing on the menu, keyboard shortcut etc */
65        public WayDownloadAction() {
66            super( tr("Way Download") ,
67                    "way-download",
68                    tr("Download map data on the end of selected way"),
69                    Shortcut.registerShortcut("waydownloader:waydownload", tr("Way Download"), KeyEvent.VK_W, Shortcut.CTRL_SHIFT),
70                    true);
71        }
72
73        /** Called when the WayDownloadAction action is triggered (e.g. user clicked the menu option) */
74        @Override
75        public void actionPerformed(ActionEvent e) {
76            selectedNode = null;
77            DataSet ds = MainApplication.getLayerManager().getEditDataSet();
78            Collection<Node> selection = ds.getSelectedNodes();
79            if (selection.isEmpty()) {
80                Collection<Way> selWays = ds.getSelectedWays();
81                if (!workFromWaySelection(selWays)) {
82                    showWarningMessage(tr("<html>Neither a node nor a way with an endpoint outside of the<br>current download areas is selected.<br>Select a node on the start or end of a way or an entire way first.</html>"));
83                    return;
84                }
85                selection = ds.getSelectedNodes();
86            }
87
88            if ( selection.isEmpty() || selection.size()>1 || ! (selection.iterator().next() instanceof Node)) {
89                showWarningMessage(tr("<html>Could not find a unique node to start downloading from.</html>"));
90                return;
91            }
92
93            selectedNode = (Node) selection.iterator().next();
94            MainApplication.getMap().mapView.zoomTo(selectedNode.getEastNorth());
95
96            //Before downloading. Figure a few things out.
97            //Find connected way
98            List<Way> connectedWays = findConnectedWays(selectedNode);
99            if (connectedWays.isEmpty()) {
100                showWarningMessage(
101                        tr("<html>There are no ways connected to node ''{0}''. Aborting.</html>",
102                        selectedNode.getDisplayName(DefaultNameFormatter.getInstance()))
103                );
104                return;
105            }
106            priorConnectedWay = connectedWays.get(0);
107
108            //Download a little rectangle around the selected node
109            double latbuffer = Config.getPref().getDouble("waydownloader.latbuffer", 0.00001);
110            double lonbuffer = Config.getPref().getDouble("waydownloader.latbuffer", 0.00002);
111            DownloadOsmTask downloadTask = new DownloadOsmTask();
112            final PleaseWaitProgressMonitor monitor = new PleaseWaitProgressMonitor();
113            LatLon ll = selectedNode.getCoor();
114            final Future<?> future = downloadTask.download(
115                    new DownloadParams(),
116                    new Bounds(
117                            ll.lat()- latbuffer,
118                            ll.lon()- lonbuffer,
119                            ll.lat()+ latbuffer,
120                            ll.lon()+ lonbuffer
121                    ),
122                    monitor
123            );
124            // schedule closing of the progress monitor after the download
125            // job has finished
126            MainApplication.worker.submit(
127                    () -> {
128                                            try {
129                                                future.get();
130                                            } catch(Exception e1) {
131                                                Logging.error(e1);
132                                                return;
133                                            }
134                                            monitor.close();
135                                        }
136            );
137            //The download is scheduled to be executed.
138            //Now schedule the run() method (below) to be executed once that's completed.
139            MainApplication.worker.execute(this);
140        }
141
142        /**
143         * Logic to excute after the download has happened
144         */
145        @Override
146        public void run() {
147            //Find ways connected to the node after the download
148            List<Way> connectedWays = findConnectedWays(selectedNode);
149
150            if (connectedWays.isEmpty()) {
151                String msg = tr("Way downloader data inconsistency. Prior connected way ''{0}'' wasn''t discovered after download",
152                                priorConnectedWay.getDisplayName(DefaultNameFormatter.getInstance())
153                        );
154                showErrorMessage(msg);
155                return;
156            }
157
158            if (connectedWays.size()==1) {
159                //Just one way connecting still to the node . Presumably the one which was there before
160                //Check if it's just a duplicate node
161
162                Node dupeNode = findDuplicateNode(selectedNode);
163                if (dupeNode!=null) {
164                    String msg = tr("<html>There aren''t further connected ways to download.<br>"
165                            + "A potential duplicate node of the currently selected node was found, though.<br><br>"
166                            + "The currently selected node is ''{0}''<br>"
167                            + "The potential duplicate node is ''{1}''<br>"
168                            + "Merge the duplicate node onto the currently selected node and continue way downloading?"
169                            + "</html>",
170                            selectedNode.getDisplayName(DefaultNameFormatter.getInstance()),
171                            dupeNode.getDisplayName(DefaultNameFormatter.getInstance())
172                    );
173
174                    int ret = JOptionPane.showConfirmDialog(
175                            MainApplication.getMainFrame(),
176                            msg,
177                            tr("Merge duplicate node?"),
178                            JOptionPane.YES_NO_OPTION,
179                            JOptionPane.QUESTION_MESSAGE
180                    );
181                    if (ret != JOptionPane.YES_OPTION)
182                        return;
183                    Command cmd = MergeNodesAction.mergeNodes(
184                            Collections.singletonList(dupeNode),
185                            selectedNode
186                    );
187                    if (cmd != null) {
188                        UndoRedoHandler.getInstance().add(cmd);
189                        MainApplication.getLayerManager().getEditLayer().data.setSelected(selectedNode);
190                    }
191                    connectedWays = findConnectedWays(selectedNode);
192                } else {
193                    showInfoMessage(tr("<html>No more connected ways to download.</html>"));
194                    return;
195                }
196                return;
197            }
198
199            if (connectedWays.size()>2) {
200                //Three or more ways meeting at this node. Means we have a junction.
201                String msg = tr(
202                        "Node ''{0}'' is a junction with more than 2 connected ways.",
203                        selectedNode.getDisplayName(DefaultNameFormatter.getInstance())
204                );
205                showWarningMessage(msg);
206                return;
207            }
208
209            if (connectedWays.size()==2) {
210                //Two connected ways (The "normal" way downloading case)
211                //Figure out which of the two is new.
212                Way wayA = connectedWays.get(0);
213                Way wayB = connectedWays.get(1);
214                Way nextWay = wayA;
215                if (priorConnectedWay.equals(wayA)) nextWay = wayB;
216
217                Node nextNode = findOtherEnd(nextWay, selectedNode);
218
219                //Select the next node
220                MainApplication.getLayerManager().getEditDataSet().setSelected(nextNode);
221                MainApplication.getMap().mapView.zoomTo(nextNode.getEastNorth());
222            }
223        }
224
225        @Override
226        protected void updateEnabledState() {
227            setEnabled(getLayerManager().getEditLayer() != null);
228        }
229
230        @Override
231        protected void updateEnabledState(Collection<? extends OsmPrimitive> selection) {
232            // do nothing
233        }
234    }
235
236    /**
237     * Check whether there is a potentially duplicate node for <code>referenceNode</code>.
238     *
239     * @param referenceNode the reference node
240     * @return the potential duplicate node. null, if no duplicate found.
241     */
242    private Node findDuplicateNode(Node referenceNode) {
243        DataSet ds = MainApplication.getLayerManager().getEditDataSet();
244        List<Node> candidates = ds.searchNodes(new Bounds(referenceNode.getCoor(), 0.0003, 0.0005).toBBox());
245        for (Node candidate: candidates) {
246            if (!candidate.equals(referenceNode)
247                    && !candidate.isIncomplete()
248                    && candidate.getCoor().equals(referenceNode.getCoor()))
249                return candidate;
250        }
251        return null;
252    }
253
254    /**
255     * Given the node on one end of the way, return the node on the other end
256     * @param way way
257     * @param firstEnd one end
258     * @return other end
259     */
260    private Node findOtherEnd(Way way, Node firstEnd) {
261        Node otherEnd = way.firstNode();
262        if (otherEnd.equals(firstEnd)) otherEnd = way.lastNode();
263        return otherEnd;
264    }
265
266    /**
267     * Replies the list of ways <code>referenceNode</code> is either the first or the
268     * last node in.
269     *
270     * @param referenceNode the reference node
271     * @return the list of ways. May be empty, but null.
272     */
273    private List<Way> findConnectedWays(Node referenceNode) {
274        List<Way> referers = new ArrayList<>(Utils.filteredCollection(referenceNode.getReferrers(), Way.class));
275        ArrayList<Way> connectedWays = new ArrayList<>(referers.size());
276
277        //loop through referers
278        for (Way way: referers) {
279            if (way.getNodesCount() >= 2 && way.isFirstLastNode(referenceNode)) {
280                    connectedWays.add(way);
281            }
282        }
283        return connectedWays;
284    }
285
286    /**
287     * given a selected way, select a node on the end of the way which is not in a downloaded area
288     * return true if this worked
289     * @param selection selected way
290     * @return true if a node has been selected
291     */
292    private boolean workFromWaySelection(Collection<? extends OsmPrimitive> selection) {
293        if (selection.size() != 1)
294            return false;
295        Way selectedWay = (Way) selection.iterator().next();
296        selectedNode = selectedWay.firstNode();
297
298        if (isDownloaded(selectedNode)) {
299            selectedNode = selectedWay.lastNode();
300
301            if (isDownloaded(selectedNode)) return false;
302        }
303        MainApplication.getLayerManager().getEditDataSet().setSelected(selectedNode);
304        return true;
305    }
306
307    private boolean isDownloaded(Node node) {
308        for (DataSource datasource : MainApplication.getLayerManager().getEditDataSet().getDataSources()) {
309            Bounds bounds = datasource.bounds;
310            if (bounds != null && bounds.contains(node.getCoor())) return true;
311        }
312        return false;
313    }
314
315    private static void showWarningMessage(final String msg) {
316        if (msg != null) {
317            Logging.warn(msg.replace("<html>", "").replace("</html>", ""));
318            GuiHelper.runInEDT(new Runnable() {
319                @Override
320                public void run() {
321                    new Notification(msg)
322                    .setIcon(JOptionPane.WARNING_MESSAGE)
323                    .show();
324                }
325            });
326        }
327    }
328
329    private static void showErrorMessage(final String msg) {
330        if (msg != null) {
331            Logging.error(msg.replace("<html>", "").replace("</html>", ""));
332            GuiHelper.runInEDT(new Runnable() {
333                @Override
334                public void run() {
335                    new Notification(msg)
336                    .setIcon(JOptionPane.ERROR_MESSAGE)
337                    .show();
338                }
339            });
340        }
341    }
342
343    private static void showInfoMessage(final String msg) {
344        if (msg != null) {
345            Logging.info(msg.replace("<html>", "").replace("</html>", ""));
346            GuiHelper.runInEDT(new Runnable() {
347                @Override
348                public void run() {
349                    new Notification(msg)
350                    .setIcon(JOptionPane.INFORMATION_MESSAGE)
351                    .setDuration(Notification.TIME_SHORT)
352                    .show();
353                }
354            });
355        }
356    }
357}
Note: See TracBrowser for help on using the repository browser.