001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.io.remotecontrol.handler; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.Point; 007import java.util.ArrayList; 008import java.util.Arrays; 009import java.util.HashMap; 010import java.util.LinkedList; 011import java.util.List; 012import java.util.Map; 013 014import org.openstreetmap.josm.Main; 015import org.openstreetmap.josm.actions.AutoScaleAction; 016import org.openstreetmap.josm.command.AddCommand; 017import org.openstreetmap.josm.command.Command; 018import org.openstreetmap.josm.command.SequenceCommand; 019import org.openstreetmap.josm.data.coor.LatLon; 020import org.openstreetmap.josm.data.osm.Node; 021import org.openstreetmap.josm.data.osm.OsmPrimitive; 022import org.openstreetmap.josm.data.osm.Way; 023import org.openstreetmap.josm.gui.util.GuiHelper; 024import org.openstreetmap.josm.io.remotecontrol.AddTagsDialog; 025import org.openstreetmap.josm.io.remotecontrol.PermissionPrefWithDefault; 026 027/** 028 * Adds a way to the current dataset. For instance, {@code /add_way?way=lat1,lon2;lat2,lon2}. 029 */ 030public class AddWayHandler extends RequestHandler { 031 032 /** 033 * The remote control command name used to add a way. 034 */ 035 public static final String command = "add_way"; 036 037 private final List<LatLon> allCoordinates = new ArrayList<LatLon>(); 038 039 /** 040 * The place to remeber already added nodes (they are reused if needed @since 5845 041 */ 042 Map<LatLon, Node> addedNodes; 043 044 @Override 045 public String[] getMandatoryParams() { 046 return new String[]{"way"}; 047 } 048 049 @Override 050 public String[] getOptionalParams() { 051 return new String[] { "addtags" }; 052 } 053 054 @Override 055 public String[] getUsageExamples() { 056 return new String[] { 057 "/add_way?way=53.2,13.3;53.3,13.3;53.3,13.2", 058 "/add_way?&addtags=building=yes&way=45.437213,-2.810792;45.437988,-2.455983;45.224080,-2.455036;45.223302,-2.809845;45.437213,-2.810792" 059 }; 060 } 061 062 @Override 063 protected void handleRequest() throws RequestHandlerErrorException, RequestHandlerBadRequestException { 064 GuiHelper.runInEDTAndWait(new Runnable() { 065 @Override public void run() { 066 addWay(); 067 } 068 }); 069 // parse parameter addtags=tag1=value1|tag2=value2 070 AddTagsDialog.addTags(args, sender); 071 } 072 073 @Override 074 public String getPermissionMessage() { 075 return tr("Remote Control has been asked to create a new way."); 076 } 077 078 @Override 079 public PermissionPrefWithDefault getPermissionPref() { 080 return PermissionPrefWithDefault.CREATE_OBJECTS; 081 } 082 083 @Override 084 protected void validateRequest() throws RequestHandlerBadRequestException { 085 allCoordinates.clear(); 086 for (String coordinatesString : args.get("way").split(";\\s*")) { 087 String[] coordinates = coordinatesString.split(",\\s*", 2); 088 if (coordinates.length < 2) { 089 throw new RequestHandlerBadRequestException( 090 tr("Invalid coordinates: {0}", Arrays.toString(coordinates))); 091 } 092 try { 093 double lat = Double.parseDouble(coordinates[0]); 094 double lon = Double.parseDouble(coordinates[1]); 095 allCoordinates.add(new LatLon(lat, lon)); 096 } catch (NumberFormatException e) { 097 throw new RequestHandlerBadRequestException("NumberFormatException ("+e.getMessage()+")"); 098 } 099 } 100 if (allCoordinates.isEmpty()) { 101 throw new RequestHandlerBadRequestException(tr("Empty ways")); 102 } else if (allCoordinates.size() == 1) { 103 throw new RequestHandlerBadRequestException(tr("One node ways")); 104 } 105 if (!Main.main.hasEditLayer()) { 106 throw new RequestHandlerBadRequestException(tr("There is no layer opened to add way")); 107 } 108 } 109 110 /** 111 * Find the node with almost the same ccords in dataset or in already added nodes 112 * @since 5845 113 **/ 114 Node findOrCreateNode(LatLon ll, List<Command> commands) { 115 Node nd = null; 116 117 if (Main.isDisplayingMapView()) { 118 Point p = Main.map.mapView.getPoint(ll); 119 nd = Main.map.mapView.getNearestNode(p, OsmPrimitive.isUsablePredicate); 120 if (nd!=null && nd.getCoor().greatCircleDistance(ll) > Main.pref.getDouble("remote.tolerance", 0.1)) { 121 nd = null; // node is too far 122 } 123 } 124 125 Node prev = null; 126 for (LatLon lOld: addedNodes.keySet()) { 127 if (lOld.greatCircleDistance(ll) < Main.pref.getDouble("remotecontrol.tolerance", 0.1)) { 128 prev = addedNodes.get(lOld); 129 break; 130 } 131 } 132 133 if (prev!=null) { 134 nd = prev; 135 } else if (nd==null) { 136 nd = new Node(ll); 137 // Now execute the commands to add this node. 138 commands.add(new AddCommand(nd)); 139 addedNodes.put(ll, nd); 140 } 141 return nd; 142 } 143 144 /* 145 * This function creates the way with given coordinates of nodes 146 */ 147 private void addWay() { 148 addedNodes = new HashMap<LatLon, Node>(); 149 Way way = new Way(); 150 List<Command> commands = new LinkedList<Command>(); 151 for (LatLon ll : allCoordinates) { 152 Node node = findOrCreateNode(ll, commands); 153 way.addNode(node); 154 } 155 allCoordinates.clear(); 156 commands.add(new AddCommand(way)); 157 Main.main.undoRedo.add(new SequenceCommand(tr("Add way"), commands)); 158 Main.main.getCurrentDataSet().setSelected(way); 159 if (PermissionPrefWithDefault.CHANGE_VIEWPORT.isAllowed()) { 160 AutoScaleAction.autoScale("selection"); 161 } else { 162 Main.map.mapView.repaint(); 163 } 164 } 165}