Ticket #2847: CreateCircleAction.java.patch
| File CreateCircleAction.java.patch, 13.7 KB (added by , 17 years ago) |
|---|
-
src/org/openstreetmap/josm/actions/CreateCircleAction.java
13 13 import org.openstreetmap.josm.Main; 14 14 import org.openstreetmap.josm.command.Command; 15 15 import org.openstreetmap.josm.command.AddCommand; 16 import org.openstreetmap.josm.command.DeleteCommand; 16 17 import org.openstreetmap.josm.command.ChangeCommand; 17 18 import org.openstreetmap.josm.command.SequenceCommand; 18 19 import org.openstreetmap.josm.data.coor.EastNorth; … … 21 22 import org.openstreetmap.josm.data.osm.Way; 22 23 import org.openstreetmap.josm.tools.Shortcut; 23 24 25 import org.openstreetmap.josm.data.osm.visitor.CollectBackReferencesVisitor; 26 24 27 /** 25 * Create a new circle from three selected nodes--or a way with 3 nodes. (Useful for roundabouts) 28 * - Create a new circle from two selected nodes or a way with 2 nodes which represent the diameter of the circle. 29 * - Create a new circle from three selected nodes--or a way with 3 nodes. 30 * - Useful for roundabouts 26 31 * 27 32 * Note: If a way is selected, it is changed. If nodes are selected a new way is created. 28 * So if you've got a way with 3nodes it makes a difference between running this on the way or the nodes!33 * So if you've got a way with nodes it makes a difference between running this on the way or the nodes! 29 34 * 30 35 * BTW: Someone might want to implement projection corrections for this... 31 36 * 32 37 * @author Henry Loenwind, based on much copy&Paste from other Actions. 38 * @author Sebastian Masch 33 39 */ 34 40 public final class CreateCircleAction extends JosmAction { 35 41 36 42 public CreateCircleAction() { 37 43 super(tr("Create Circle"), "createcircle", tr("Create a circle from three selected nodes."), 38 Shortcut.registerShortcut("tools:createcircle", tr("Tool: {0}", tr("Create Circle")), KeyEvent.VK_O, Shortcut.GROUP_EDIT, Shortcut.SHIFT_DEFAULT), true);44 Shortcut.registerShortcut("tools:createcircle", tr("Tool: {0}", tr("Create Circle")), KeyEvent.VK_O, Shortcut.GROUP_EDIT, Shortcut.SHIFT_DEFAULT), true); 39 45 } 40 46 41 47 private double calcang(double xc, double yc, double x, double y) { 42 48 // calculate the angle from xc|yc to x|y 43 if (xc == x && yc == y) {49 if (xc == x && yc == y) 44 50 return 0; // actually invalid, but we won't have this case in this context 45 }46 51 double yd = Math.abs(y - yc); 47 if (yd == 0 && xc < x) {52 if (yd == 0 && xc < x) 48 53 return 0; 49 } 50 if (yd == 0 && xc > x) { 54 if (yd == 0 && xc > x) 51 55 return Math.PI; 52 }53 56 double xd = Math.abs(x - xc); 54 57 double a = Math.atan2(xd, yd); 55 58 if (y > yc) { … … 81 84 Way existingWay = null; 82 85 83 86 for (OsmPrimitive osm : sel) 84 if (osm instanceof Node) 87 if (osm instanceof Node) { 85 88 nodes.add((Node)osm); 89 } 86 90 87 91 // special case if no single nodes are selected and exactly one way is: 88 92 // then use the way's nodes 89 if ((nodes.size() == 0) && (sel.size() == 1)) 93 if ((nodes.size() == 0) && (sel.size() == 1)) { 90 94 for (OsmPrimitive osm : sel) 91 95 if (osm instanceof Way) { 92 96 existingWay = ((Way)osm); 93 97 for (Node n : ((Way)osm).nodes) 94 98 { 95 if(!nodes.contains(n)) 99 if(!nodes.contains(n)) { 96 100 nodes.add(n); 101 } 97 102 } 98 103 } 99 100 if (nodes.size() != 3) {101 JOptionPane.showMessageDialog(Main.parent, tr("Please select exactly three nodes or one way with exactly three nodes."));102 return;103 104 } 104 105 105 // let's get some shorter names 106 Node n1 = ((Node)nodes.toArray()[0]); 107 double x1 = n1.getEastNorth().east(); 108 double y1 = n1.getEastNorth().north(); 109 Node n2 = ((Node)nodes.toArray()[1]); 110 double x2 = n2.getEastNorth().east(); 111 double y2 = n2.getEastNorth().north(); 112 Node n3 = ((Node)nodes.toArray()[2]); 113 double x3 = n3.getEastNorth().east(); 114 double y3 = n3.getEastNorth().north(); 106 // now we can start doing things to OSM data 107 Collection<Command> cmds = new LinkedList<Command>(); 115 108 116 // calculate the center (xc/yc) 117 double s = 0.5*((x2 - x3)*(x1 - x3) - (y2 - y3)*(y3 - y1)); 118 double sUnder = (x1 - x2)*(y3 - y1) - (y2 - y1)*(x1 - x3); 109 if (nodes.size() == 2) { 110 // diameter: two single nodes needed or a way with two nodes 119 111 120 if (sUnder == 0) { 121 JOptionPane.showMessageDialog(Main.parent, tr("Those nodes are not in a circle.")); 122 return; 123 } 112 Node n1 = ((Node)nodes.toArray()[0]); 113 double x1 = n1.getEastNorth().east(); 114 double y1 = n1.getEastNorth().north(); 115 Node n2 = ((Node)nodes.toArray()[1]); 116 double x2 = n2.getEastNorth().east(); 117 double y2 = n2.getEastNorth().north(); 124 118 125 s /= sUnder; 119 // calculate the center (xc/yc) 120 double xc = 0.5 * (x1 + x2); 121 double yc = 0.5 * (y1 + y2); 126 122 127 double xc = 0.5*(x1 + x2) + s*(y2 - y1);128 double yc = 0.5*(y1 + y2) + s*(x1 - x2);123 // calculate the radius (r) 124 double r = Math.sqrt(Math.pow(xc-x1,2) + Math.pow(yc-y1,2)); 129 125 130 // calculate the radius (r) 131 double r = Math.sqrt(Math.pow(xc-x1,2) + Math.pow(yc-y1,2)); 126 // find where to put the existing nodes 127 double a1 = calcang(xc, yc, x1, y1); 128 double a2 = calcang(xc, yc, x2, y2); 129 if (a1 < a2) { double at = a1; Node nt = n1; a1 = a2; n1 = n2; a2 = at; n2 = nt; } 132 130 133 // find where to put the existing nodes 134 double a1 = calcang(xc, yc, x1, y1); 135 double a2 = calcang(xc, yc, x2, y2); 136 double a3 = calcang(xc, yc, x3, y3); 137 if (a1 < a2) { double at = a1; Node nt = n1; a1 = a2; n1 = n2; a2 = at; n2 = nt; } 138 if (a2 < a3) { double at = a2; Node nt = n2; a2 = a3; n2 = n3; a3 = at; n3 = nt; } 139 if (a1 < a2) { double at = a1; Node nt = n1; a1 = a2; n1 = n2; a2 = at; n2 = nt; } 131 // build a way for the circle 132 Way wayToAdd; 133 if (existingWay == null) { 134 wayToAdd = new Way(); 135 } else { 136 // re-use existing way if it was selected 137 wayToAdd = new Way(existingWay); 138 wayToAdd.nodes.clear(); 139 } 140 140 141 // now we can start doing thigs to OSM data142 Collection<Command> cmds = new LinkedList<Command>();141 for (int i = 1; i <= numberOfNodesInCircle; i++) { 142 double a = a2 + 2*Math.PI*(1.0 - i/(double)numberOfNodesInCircle); // "1-" to get it clock-wise 143 143 144 // build a way for the circle 145 Way wayToAdd; 146 if (existingWay == null) { 147 wayToAdd = new Way(); 148 } else { 149 // re-use existing way if it was selected 150 wayToAdd = new Way(existingWay); 151 wayToAdd.nodes.clear(); 152 } 153 for (int i = 1; i <= numberOfNodesInCircle; i++) { 154 double a = 2*Math.PI*(1.0 - i/(double)numberOfNodesInCircle); // "1-" to get it clock-wise 155 // insert existing nodes if they fit before this new node (999 means "already added this node") 156 if (a1 < 999 && a1 > a) { 157 wayToAdd.nodes.add(n1); 158 a1 = 999; 144 // insert existing nodes if they fit before this new node (999 means "already added this node") 145 if ((a1 < 999) && (a1 > a - 1E-9) && (a1 < a + 1E-9)) { 146 wayToAdd.nodes.add(n1); 147 a1 = 999; 148 } 149 else if ((a2 < 999) && (a2 > a - 1E-9) && (a2 < a + 1E-9)) { 150 wayToAdd.nodes.add(n2); 151 a2 = 999; 152 } 153 else { 154 // get the position of the new node and insert it 155 double x = xc + r*Math.cos(a); 156 double y = yc + r*Math.sin(a); 157 Node n = new Node(Main.proj.eastNorth2latlon(new EastNorth(x,y))); 158 wayToAdd.nodes.add(n); 159 cmds.add(new AddCommand(n)); 160 } 159 161 } 160 if (a2 < 999 && a2 > a) { 161 wayToAdd.nodes.add(n2); 162 a2 = 999; 162 wayToAdd.nodes.add(wayToAdd.nodes.get(0)); // close the circle 163 if (existingWay == null) { 164 cmds.add(new AddCommand(wayToAdd)); 165 } else { 166 cmds.add(new ChangeCommand(existingWay, wayToAdd)); 163 167 } 164 if (a3 < 999 && a3 > a) { 165 wayToAdd.nodes.add(n3); 166 a3 = 999; 168 169 // the first node may be unused/abandoned if createcircle.nodecount is odd 170 if (a1 < 999) { 171 // if it is, delete it 172 CollectBackReferencesVisitor refs = new CollectBackReferencesVisitor(Main.ds); 173 refs.visit(n1); 174 if (refs.data.isEmpty() || ((refs.data.size() == 1) && (refs.data.contains(existingWay)))) { 175 cmds.add(new DeleteCommand(n1)); 176 } 177 178 // or insert it 179 // wayToAdd.nodes.add((numberOfNodesInCircle - 1) / 2, n1); 167 180 } 168 // get the position of the new node and insert it 169 double x = xc + r*Math.cos(a); 170 double y = yc + r*Math.sin(a); 171 Node n = new Node(Main.proj.eastNorth2latlon(new EastNorth(x,y))); 172 wayToAdd.nodes.add(n); 173 cmds.add(new AddCommand(n)); 174 } 175 wayToAdd.nodes.add(wayToAdd.nodes.get(0)); // close the circle 176 if (existingWay == null) { 177 cmds.add(new AddCommand(wayToAdd)); 181 182 } else if (nodes.size() == 3) { 183 // triangle: three single nodes needed or a way with three nodes 184 185 // let's get some shorter names 186 Node n1 = ((Node)nodes.toArray()[0]); 187 double x1 = n1.getEastNorth().east(); 188 double y1 = n1.getEastNorth().north(); 189 Node n2 = ((Node)nodes.toArray()[1]); 190 double x2 = n2.getEastNorth().east(); 191 double y2 = n2.getEastNorth().north(); 192 Node n3 = ((Node)nodes.toArray()[2]); 193 double x3 = n3.getEastNorth().east(); 194 double y3 = n3.getEastNorth().north(); 195 196 // calculate the center (xc/yc) 197 double s = 0.5*((x2 - x3)*(x1 - x3) - (y2 - y3)*(y3 - y1)); 198 double sUnder = (x1 - x2)*(y3 - y1) - (y2 - y1)*(x1 - x3); 199 200 if (sUnder == 0) { 201 JOptionPane.showMessageDialog(Main.parent, tr("Those nodes are not in a circle.")); 202 return; 203 } 204 205 s /= sUnder; 206 207 double xc = 0.5*(x1 + x2) + s*(y2 - y1); 208 double yc = 0.5*(y1 + y2) + s*(x1 - x2); 209 210 // calculate the radius (r) 211 double r = Math.sqrt(Math.pow(xc-x1,2) + Math.pow(yc-y1,2)); 212 213 // find where to put the existing nodes 214 double a1 = calcang(xc, yc, x1, y1); 215 double a2 = calcang(xc, yc, x2, y2); 216 double a3 = calcang(xc, yc, x3, y3); 217 if (a1 < a2) { double at = a1; Node nt = n1; a1 = a2; n1 = n2; a2 = at; n2 = nt; } 218 if (a2 < a3) { double at = a2; Node nt = n2; a2 = a3; n2 = n3; a3 = at; n3 = nt; } 219 if (a1 < a2) { double at = a1; Node nt = n1; a1 = a2; n1 = n2; a2 = at; n2 = nt; } 220 221 // build a way for the circle 222 Way wayToAdd; 223 if (existingWay == null) { 224 wayToAdd = new Way(); 225 } else { 226 // re-use existing way if it was selected 227 wayToAdd = new Way(existingWay); 228 wayToAdd.nodes.clear(); 229 } 230 for (int i = 1; i <= numberOfNodesInCircle; i++) { 231 double a = 2*Math.PI*(1.0 - i/(double)numberOfNodesInCircle); // "1-" to get it clock-wise 232 // insert existing nodes if they fit before this new node (999 means "already added this node") 233 if (a1 < 999 && a1 > a) { 234 wayToAdd.nodes.add(n1); 235 a1 = 999; 236 } 237 if (a2 < 999 && a2 > a) { 238 wayToAdd.nodes.add(n2); 239 a2 = 999; 240 } 241 if (a3 < 999 && a3 > a) { 242 wayToAdd.nodes.add(n3); 243 a3 = 999; 244 } 245 // get the position of the new node and insert it 246 double x = xc + r*Math.cos(a); 247 double y = yc + r*Math.sin(a); 248 Node n = new Node(Main.proj.eastNorth2latlon(new EastNorth(x,y))); 249 wayToAdd.nodes.add(n); 250 cmds.add(new AddCommand(n)); 251 } 252 wayToAdd.nodes.add(wayToAdd.nodes.get(0)); // close the circle 253 if (existingWay == null) { 254 cmds.add(new AddCommand(wayToAdd)); 255 } else { 256 cmds.add(new ChangeCommand(existingWay, wayToAdd)); 257 } 258 178 259 } else { 179 cmds.add(new ChangeCommand(existingWay, wayToAdd)); 260 JOptionPane.showMessageDialog(Main.parent, tr("Please select exactly two or three nodes or one way with exactly two or three nodes.")); 261 return; 180 262 } 181 263 182 264 Main.main.undoRedo.add(new SequenceCommand(tr("Create Circle"), cmds));
