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

Last change on this file was 35976, checked in by taylor.smock, 23 months ago

see #22104: Remove usages of Node#getCoor where possible

This also accounts for cases where Node has the methods used later,
so a new LatLon is unnecessary.

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.ILatLon;
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 ILatLon ll = selectedNode;
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)) 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.