import static org.openstreetmap.josm.tools.I18n.tr; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedList; import javax.swing.JOptionPane; import org.openstreetmap.josm.Main; import org.openstreetmap.josm.actions.JosmAction; import org.openstreetmap.josm.actions.MergeNodesAction; import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTask; import org.openstreetmap.josm.data.Bounds; import org.openstreetmap.josm.data.osm.DataSource; import org.openstreetmap.josm.data.osm.Node; import org.openstreetmap.josm.data.osm.OsmPrimitive; import org.openstreetmap.josm.data.osm.Way; import org.openstreetmap.josm.gui.MainMenu; import org.openstreetmap.josm.gui.progress.PleaseWaitProgressMonitor; import org.openstreetmap.josm.plugins.Plugin; import org.openstreetmap.josm.tools.Shortcut; /** * Plugin class for the Way Downloader plugin * * @author Harry Wood */ public class WayDownloaderPlugin extends Plugin { private Way priorConnectedWay = null; private Node selectedNode = null; /** Plugin constructor called at JOSM startup */ public WayDownloaderPlugin() { //add WayDownloadAction to tools menu MainMenu.add(Main.main.menu.toolsMenu, new WayDownloadAction()); } private class WayDownloadAction extends JosmAction implements Runnable { /** Set up the action (text appearing on the menu, keyboard shortcut etc */ public WayDownloadAction() { super( "Way Download" , "way-download", "Download map data on the end of selected way", Shortcut.registerShortcut("waydownloader:waydownload", "Way Download", KeyEvent.VK_W, Shortcut.GROUP_MENU, Shortcut.SHIFT_DEFAULT), true); } /** Called when the WayDownloadAction action is triggered (e.g. user clicked the menu option) */ public void actionPerformed(ActionEvent e) { System.out.println("Way Download"); String errMsg = null; selectedNode = null; Collection selection = Main.main.getCurrentDataSet().getSelectedNodes(); if (selection.size()==0) { selection = Main.main.getCurrentDataSet().getSelectedWays(); if (!workFromWaySelection(selection)) { errMsg = tr("Select a starting node on the end of a way"); } selection = Main.main.getCurrentDataSet().getSelectedNodes(); } if ( selection.size()==0 || selection.size()>1 ) { errMsg = tr("Select a starting node on the end of a way"); } else { OsmPrimitive p = selection.iterator().next(); if (!(p instanceof Node)) { errMsg = tr("Select a starting node on the end of a way"); } else { selectedNode = (Node) p; Main.map.mapView.zoomTo(selectedNode.getEastNorth()); //Before downloading. Figure a few things out. //Find connected way ArrayList connectedWays = findConnectedWays(); if (connectedWays.size()==0) { errMsg = tr("Select a starting node on the end of a way"); } else { priorConnectedWay =connectedWays.get(0); //Download a little rectangle around the selected node double latbuffer=0.0003; //TODO make this an option double lonbuffer=0.0005; DownloadOsmTask downloadTask = new DownloadOsmTask(); downloadTask.download( null, selectedNode.getCoor().lat()-latbuffer, selectedNode.getCoor().lon()-lonbuffer, selectedNode.getCoor().lat()+latbuffer, selectedNode.getCoor().lon()+lonbuffer, new PleaseWaitProgressMonitor()); //The download is scheduled to be executed. //Now schedule the run() method (below) to be executed once that's completed. Main.worker.execute(this); } } } if(errMsg != null) JOptionPane.showMessageDialog(Main.parent, errMsg); } /** * Logic to excute after the download has happened */ public void run() { //Find ways connected to the node after the download ArrayList connectedWays = findConnectedWays(); String errMsg = null; if (connectedWays.size()==0) { throw new RuntimeException("Way downloader data inconsistency. priorConnectedWay (" + priorConnectedWay.toString() + ") wasn't discovered after download"); } else if (connectedWays.size()==1) { //Just one way connecting the node still. Presumably the one which was there before //Check if it's just a duplicate node Node dupeNode = duplicateNode(); if (dupeNode!=null) { if (JOptionPane.showConfirmDialog(null, "Merge duplicate node?")==JOptionPane.YES_OPTION) { LinkedList dupeNodes = new LinkedList(); dupeNodes.add(dupeNode); new MergeNodesAction().mergeNodes(dupeNodes, selectedNode); connectedWays = findConnectedWays(); //Carry on } } else { errMsg = tr("Reached the end of the line"); } } if (connectedWays.size()>2) { //Three or more ways meeting at this node. Means we have a junction. errMsg = tr("Reached a junction"); } else if (connectedWays.size()==2) { //Two connected ways (The "normal" way downloading case) //Figure out which of the two is new. System.out.println("connectedWays.toString()=" + connectedWays.toString()); Way wayA = connectedWays.get(0); Way wayB = connectedWays.get(1); Way nextWay = wayA; if (priorConnectedWay.equals(wayA)) nextWay = wayB; Node nextNode = findOtherEnd(nextWay, selectedNode); //Select the next node Main.main.getCurrentDataSet().setSelected(nextNode); Main.map.mapView.zoomTo(nextNode.getEastNorth()); } if(errMsg != null) JOptionPane.showMessageDialog(Main.parent, errMsg); } } /** See if there's another node at the same coordinates. If so return it. Otherwise null */ private Node duplicateNode() { for (Node onNode:Main.main.getCurrentDataSet().nodes) { if (!onNode.equals(this.selectedNode) && !onNode.incomplete && onNode.getCoor().lat()==selectedNode.getCoor().lat() && onNode.getCoor().lon()==selectedNode.getCoor().lon()) { return onNode; } } return null; } /** Given the the node on one end of the way, return the node on the other end */ private Node findOtherEnd(Way way, Node firstEnd) { Node otherEnd = way.firstNode(); if (otherEnd.equals(firstEnd)) otherEnd = way.lastNode(); return otherEnd; } /** find set of ways which have an end on the selectedNode */ private ArrayList findConnectedWays() { ArrayList connectedWays = new ArrayList(); //loop through every way for (Way onWay:Main.main.getCurrentDataSet().ways) { if (onWay.getNodesCount() >= 2) { if (onWay.isFirstLastNode(selectedNode)) { //Found it connectedWays.add(onWay); } } } return connectedWays; } /** * given a selected way, select a node on the end of the way which is not in a downloaded area * return true if this worked */ private boolean workFromWaySelection(Collection selection) { if (selection.size() != 1) return false; Way selectedWay = (Way) selection.iterator().next(); selectedNode = selectedWay.firstNode(); if (isDownloaded(selectedNode)) { selectedNode = selectedWay.lastNode(); if (isDownloaded(selectedNode)) return false; } Main.main.getCurrentDataSet().setSelected(selectedNode); return true; } private boolean isDownloaded(Node node) { for (DataSource datasource:Main.main.getCurrentDataSet().dataSources) { Bounds bounds = datasource.bounds; if (node.getCoor().lat()>bounds.min.lat() && node.getCoor().lat()bounds.min.lon() && node.getCoor().lon()