Changeset 19084 in osm for applications/editors/josm/plugins/terracer
- Timestamp:
- 2009-12-13T22:47:51+01:00 (15 years ago)
- Location:
- applications/editors/josm/plugins/terracer/src/terracer
- Files:
-
- 2 added
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
applications/editors/josm/plugins/terracer/src/terracer/TerracerAction.java
r17874 r19084 11 11 import static org.openstreetmap.josm.tools.I18n.trn; 12 12 13 import java.awt.Choice;14 import java.awt.Component;15 import java.awt.GridBagLayout;16 13 import java.awt.event.ActionEvent; 17 import java.awt.event.ItemEvent;18 import java.awt.event.ItemListener;19 14 import java.awt.event.KeyEvent; 20 15 import java.util.ArrayList; … … 24 19 import java.util.TreeSet; 25 20 26 import javax.swing.JFormattedTextField;27 import javax.swing.JLabel;28 21 import javax.swing.JOptionPane; 29 import javax.swing.JPanel;30 import javax.swing.JSpinner;31 import javax.swing.SpinnerNumberModel;32 import javax.swing.event.ChangeEvent;33 import javax.swing.event.ChangeListener;34 22 35 23 import org.openstreetmap.josm.Main; … … 44 32 import org.openstreetmap.josm.data.osm.RelationMember; 45 33 import org.openstreetmap.josm.data.osm.Way; 46 import org.openstreetmap.josm.gui.widgets.AutoCompleteComboBox;47 import org.openstreetmap.josm.tools.GBC;48 34 import org.openstreetmap.josm.tools.Pair; 49 35 import org.openstreetmap.josm.tools.Shortcut; … … 63 49 // smsms1 asked for the last value to be remembered to make it easier to do 64 50 // repeated terraces. this is the easiest, but not necessarily nicest, way. 65 // private static String lastSelectedValue = "";51 // private static String lastSelectedValue = ""; 66 52 67 53 public TerracerAction() { 68 super(tr("Terrace a building"), 69 "terrace", 54 super(tr("Terrace a building"), "terrace", 70 55 tr("Creates individual buildings from a long building."), 71 Shortcut.registerShortcut("tools:Terracer", 72 tr("Tool: {0}", tr("Terrace a building")), 73 KeyEvent.VK_T, Shortcut.GROUP_EDIT, 74 Shortcut.SHIFT_DEFAULT), 75 true); 56 Shortcut.registerShortcut("tools:Terracer", tr("Tool: {0}", 57 tr("Terrace a building")), KeyEvent.VK_T, 58 Shortcut.GROUP_EDIT, Shortcut.SHIFT_DEFAULT), true); 76 59 } 77 60 … … 81 64 */ 82 65 public void actionPerformed(ActionEvent e) { 83 Collection<OsmPrimitive> sel = Main.main.getCurrentDataSet().getSelected(); 66 Collection<OsmPrimitive> sel = Main.main.getCurrentDataSet() 67 .getSelected(); 84 68 boolean badSelect = false; 85 69 … … 88 72 89 73 if (prim instanceof Way) { 90 Way way = (Way)prim; 91 92 if ((way.getNodesCount() >= 5) && 93 way.isClosed()) { 74 Way way = (Way) prim; 75 76 if ((way.getNodesCount() >= 5) && way.isClosed()) { 77 String title = trn("Change {0} object", 78 "Change {0} objects", sel.size(), sel.size()); 79 if (sel.size() == 0) 80 title = tr("Nothing selected!"); 81 94 82 // first ask the user how many buildings to terrace into 95 HouseNumberDialog dialog = new HouseNumberDialog(); 96 final JOptionPane optionPane = new JOptionPane(dialog, JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION); 97 98 String title = trn("Change {0} object", "Change {0} objects", sel.size(), sel.size()); 99 if(sel.size() == 0) 100 title = tr("Nothing selected!"); 101 102 optionPane.createDialog(Main.parent, title).setVisible(true); 103 Object answerObj = optionPane.getValue(); 104 if (answerObj != null && 105 answerObj != JOptionPane.UNINITIALIZED_VALUE && 106 (answerObj instanceof Integer && 107 (Integer)answerObj == JOptionPane.OK_OPTION)) { 108 109 // call out to the method which does the actual 110 // terracing. 111 terraceBuilding(way, 112 dialog.numberFrom(), 113 dialog.numberTo(), 114 dialog.stepSize(), 115 dialog.streetName()); 116 117 } 83 HouseNumberInputHandler handler = new HouseNumberInputHandler( 84 this, way, title); 85 86 // IntermediateInputDialog inputDialog = new 87 // IntermediateInputDialog( 88 // this, way); 89 // inputDialog.pack(); 90 // inputDialog.setVisible(true); 91 92 // HouseNumberDialog dialog = new HouseNumberDialog(this); 93 // final JOptionPane optionPane = new JOptionPane(dialog, 94 // JOptionPane.PLAIN_MESSAGE, 95 // JOptionPane.OK_CANCEL_OPTION); 96 // 97 // String title = trn("Change {0} object", 98 // "Change {0} objects", sel.size(), sel.size()); 99 // if (sel.size() == 0) 100 // title = tr("Nothing selected!"); 101 // 102 // optionPane.createDialog(Main.parent, title) 103 // .setVisible(true); 104 // Object answerObj = optionPane.getValue(); 105 // if (answerObj != null 106 // && answerObj != JOptionPane.UNINITIALIZED_VALUE 107 // && (answerObj instanceof Integer && (Integer) answerObj 108 // == JOptionPane.OK_OPTION)) { 109 // 110 // // call out to the method which does the actual 111 // // terracing. 112 // terraceBuilding(way, dialog.numberFrom(), dialog 113 // .numberTo(), dialog.stepSize(), dialog 114 // .streetName()); 115 // 116 // } 118 117 } else { 119 118 badSelect = true; … … 142 141 * @param w The closed, quadrilateral way to terrace. 143 142 */ 144 private void terraceBuilding(Way w, int from, int to, int step, String streetName) { 145 final int nb = 1 + (to - from) / step; 143 public void terraceBuilding(Way w, Integer segments, Integer from, 144 Integer to, int step, String streetName) { 145 final int nb; 146 if (to != null && from != null) { 147 nb = 1 + (to.intValue() - from.intValue()) / step; 148 } else if (segments != null) { 149 nb = segments.intValue(); 150 } else { 151 // TODO: we're in trouble. 152 // do exception handling. 153 nb = 0; 154 } 146 155 147 156 // now find which is the longest side connecting the first node 148 Pair<Way, Way> interp = findFrontAndBack(w);157 Pair<Way, Way> interp = findFrontAndBack(w); 149 158 150 159 final double frontLength = wayLength(interp.a); … … 159 168 // create intermediate nodes by interpolating. 160 169 for (int i = 0; i <= nb; ++i) { 161 new_nodes[0][i] = interpolateAlong(interp.a, frontLength * (i) / (nb)); 162 new_nodes[1][i] = interpolateAlong(interp.b, backLength * (i) / (nb)); 170 new_nodes[0][i] = interpolateAlong(interp.a, frontLength * (i) 171 / (nb)); 172 new_nodes[1][i] = interpolateAlong(interp.b, backLength * (i) 173 / (nb)); 163 174 commands.add(new AddCommand(new_nodes[0][i])); 164 175 commands.add(new AddCommand(new_nodes[1][i])); … … 172 183 } 173 184 // note that we don't actually add the street member to the relation, as 174 // the name isn't unambiguous and it could cause confusion if the editor were 185 // the name isn't unambiguous and it could cause confusion if the editor 186 // were 175 187 // to automatically select one which wasn't the one the user intended. 176 188 … … 178 190 for (int i = 0; i < nb; ++i) { 179 191 Way terr = new Way(); 180 // Using Way.nodes.add rather than Way.addNode because the latter doesn't 192 // Using Way.nodes.add rather than Way.addNode because the latter 193 // doesn't 181 194 // exist in older versions of JOSM. 182 195 terr.addNode(new_nodes[0][i]); 183 terr.addNode(new_nodes[0][i +1]);184 terr.addNode(new_nodes[1][i +1]);196 terr.addNode(new_nodes[0][i + 1]); 197 terr.addNode(new_nodes[1][i + 1]); 185 198 terr.addNode(new_nodes[1][i]); 186 199 terr.addNode(new_nodes[0][i]); 187 terr.put("addr:housenumber", "" + (from + i * step)); 200 201 if (from != null) { 202 // only, if the user has specified house numbers 203 terr.put("addr:housenumber", "" + (from + i * step)); 204 } 188 205 terr.put("building", "yes"); 189 206 if (streetName != null) { … … 215 232 private Node interpolateAlong(Way w, double l) { 216 233 Node n = null; 217 for (Pair<Node,Node> p : w.getNodePairs(false)) { 218 final double seg_length = p.a.getCoor().greatCircleDistance(p.b.getCoor()); 234 for (Pair<Node, Node> p : w.getNodePairs(false)) { 235 final double seg_length = p.a.getCoor().greatCircleDistance( 236 p.b.getCoor()); 219 237 if (l <= seg_length) { 220 238 n = interpolateNode(p.a, p.b, l / seg_length); … … 225 243 } 226 244 if (n == null) { 227 // sometimes there is a small overshoot due to numerical roundoff, so we just 228 // set these cases to be equal to the last node. its not pretty, but it works ;-) 245 // sometimes there is a small overshoot due to numerical roundoff, 246 // so we just 247 // set these cases to be equal to the last node. its not pretty, but 248 // it works ;-) 229 249 n = w.getNode(w.getNodesCount() - 1); 230 250 } … … 241 261 private double wayLength(Way w) { 242 262 double length = 0.0; 243 for (Pair<Node, Node> p : w.getNodePairs(false)) {263 for (Pair<Node, Node> p : w.getNodePairs(false)) { 244 264 length += p.a.getCoor().greatCircleDistance(p.b.getCoor()); 245 265 } … … 275 295 276 296 // if the second side has a shorter length and an approximately equal 277 // sideness then its better to choose the shorter, as with quadrilaterals 297 // sideness then its better to choose the shorter, as with 298 // quadrilaterals 278 299 // created using the orthogonalise tool the sideness will be about the 279 300 // same for all sides. 280 if (sideLength(w, side1) > sideLength(w, side1 + 1) &&281 Math.abs(sideness[side1] - sideness[side1 + 1]) < 0.001) {301 if (sideLength(w, side1) > sideLength(w, side1 + 1) 302 && Math.abs(sideness[side1] - sideness[side1 + 1]) < 0.001) { 282 303 side1 = side1 + 1; 283 304 side2 = (side2 + 1) % (w.getNodesCount() - 1); … … 286 307 // swap side1 and side2 into sorted order. 287 308 if (side1 > side2) { 288 // i can't believe i have to write swap() myself - surely java standard 309 // i can't believe i have to write swap() myself - surely java 310 // standard 289 311 // library has this somewhere??!!?ONE! 290 312 int tmp = side2; … … 316 338 private double sideLength(Way w, int i) { 317 339 Node a = w.getNode(i); 318 Node b = w.getNode((i +1) % (w.getNodesCount() - 1));340 Node b = w.getNode((i + 1) % (w.getNodesCount() - 1)); 319 341 return a.getCoor().greatCircleDistance(b.getCoor()); 320 342 } … … 336 358 public double x; 337 359 public int i; 360 338 361 public SortWithIndex(double a, int b) { 339 x = a; i = b; 340 } 362 x = a; 363 i = b; 364 } 365 341 366 public int compareTo(SortWithIndex o) { 342 367 return Double.compare(x, o.x); … … 366 391 double[] sideness = new double[length]; 367 392 368 sideness[0] = calculateSideness( 369 w.getNode(length - 1), w.getNode(0), 370 w.getNode(1), w.getNode(2)); 393 sideness[0] = calculateSideness(w.getNode(length - 1), w.getNode(0), w 394 .getNode(1), w.getNode(2)); 371 395 for (int i = 1; i < length - 1; ++i) { 372 sideness[i] = calculateSideness( 373 w.getNode(i-1), w.getNode(i), 374 w.getNode(i+1), w.getNode(i+2)); 375 } 376 sideness[length-1] = calculateSideness( 377 w.getNode(length - 2), w.getNode(length - 1), 378 w.getNode(length), w.getNode(1)); 396 sideness[i] = calculateSideness(w.getNode(i - 1), w.getNode(i), w 397 .getNode(i + 1), w.getNode(i + 2)); 398 } 399 sideness[length - 1] = calculateSideness(w.getNode(length - 2), w 400 .getNode(length - 1), w.getNode(length), w.getNode(1)); 379 401 380 402 return sideness; … … 392 414 final double pdy = d.getCoor().getY() - c.getCoor().getY(); 393 415 394 return (ndx * pdx + ndy * pdy) / 395 Math.sqrt((ndx * ndx + ndy * ndy) * (pdx * pdx + pdy * pdy)); 396 } 397 398 /** 399 * Dialog box to allow users to input housenumbers in a nice way. 400 */ 401 class HouseNumberDialog extends JPanel { 402 private SpinnerNumberModel lo, hi; 403 private JSpinner clo, chi; 404 private Choice step; 405 private AutoCompleteComboBox street; 406 407 public HouseNumberDialog() { 408 super(new GridBagLayout()); 409 lo = new SpinnerNumberModel(1, 1, 1, 1); 410 hi = new SpinnerNumberModel(1, 1, null, 1); 411 step = new Choice(); 412 step.add(tr("All")); 413 step.add(tr("Even")); 414 step.add(tr("Odd")); 415 clo = new JSpinner(lo); 416 chi = new JSpinner(hi); 417 418 lo.addChangeListener(new ChangeListener() { 419 public void stateChanged(ChangeEvent e) { 420 hi.setMinimum((Integer)lo.getNumber()); 421 } 422 }); 423 hi.addChangeListener(new ChangeListener() { 424 public void stateChanged(ChangeEvent e) { 425 lo.setMaximum((Integer)hi.getNumber()); 426 } 427 }); 428 step.addItemListener(new ItemListener() { 429 public void itemStateChanged(ItemEvent e) { 430 if (step.getSelectedItem() == tr("All")) { 431 hi.setStepSize(1); 432 lo.setStepSize(1); 433 } else { 434 int odd_or_even = 0; 435 int min = 0; 436 437 if (step.getSelectedItem() == tr("Even")) { 438 odd_or_even = 0; 439 min = 2; 440 } else { 441 odd_or_even = 1; 442 min = 1; 443 } 444 445 if ((lo.getNumber().intValue() & 1) != odd_or_even) { 446 int nextval = lo.getNumber().intValue() - 1; 447 lo.setValue((nextval > min) ? nextval : min); 448 } 449 if ((hi.getNumber().intValue() & 1) != odd_or_even) { 450 int nextval = hi.getNumber().intValue() - 1; 451 hi.setValue((nextval > min) ? nextval : min); 452 } 453 lo.setMinimum(min); 454 hi.setStepSize(2); 455 lo.setStepSize(2); 456 } 457 } 458 }); 459 460 final TreeSet<String> names = createAutoCompletionInfo(); 461 462 street = new AutoCompleteComboBox(); 463 street.setPossibleItems(names); 464 street.setEditable(true); 465 street.setSelectedItem(null); 466 467 JFormattedTextField x; 468 x = ((JSpinner.DefaultEditor)clo.getEditor()).getTextField(); 469 x.setColumns(5); 470 x = ((JSpinner.DefaultEditor)chi.getEditor()).getTextField(); 471 x.setColumns(5); 472 addLabelled(tr("Highest number") + ": ", chi); 473 addLabelled(tr("Lowest number") + ": ", clo); 474 addLabelled(tr("Interpolation") + ": ", step); 475 addLabelled(tr("Street name") + " (" + tr("Optional") + "): ", street); 476 } 477 478 private void addLabelled(String str, Component c) { 479 JLabel label = new JLabel(str); 480 add(label, GBC.std()); 481 label.setLabelFor(c); 482 add(c, GBC.eol()); 483 } 484 485 public int numberFrom() { 486 return lo.getNumber().intValue(); 487 } 488 489 public int numberTo() { 490 return hi.getNumber().intValue(); 491 } 492 493 public int stepSize() { 494 return (step.getSelectedItem() == tr("All")) ? 1 : 2; 495 } 496 497 public String streetName() { 498 Object selected = street.getSelectedItem(); 499 if (selected == null) { 500 return null; 501 } else { 502 String name = selected.toString(); 503 if (name.length() == 0) { 504 return null; 505 } else { 506 return name; 507 } 508 } 509 } 416 return (ndx * pdx + ndy * pdy) 417 / Math.sqrt((ndx * ndx + ndy * ndy) * (pdx * pdx + pdy * pdy)); 510 418 } 511 419 … … 513 421 * Generates a list of all visible names of highways in order to do 514 422 * autocompletion on the road name. 515 */ 516 private TreeSet<String> createAutoCompletionInfo() { 423 * 424 * TODO: REMOVE this method here! 425 */ 426 TreeSet<String> createAutoCompletionInfo() { 517 427 final TreeSet<String> names = new TreeSet<String>(); 518 for (OsmPrimitive osm : Main.main.getCurrentDataSet() .allNonDeletedPrimitives()) {519 if (osm.getKeys() != null &&520 osm.keySet().contains("highway") &&521 osm.keySet().contains("name")) {428 for (OsmPrimitive osm : Main.main.getCurrentDataSet() 429 .allNonDeletedPrimitives()) { 430 if (osm.getKeys() != null && osm.keySet().contains("highway") 431 && osm.keySet().contains("name")) { 522 432 names.add(osm.get("name")); 523 433 } … … 553 463 // screen coordinates rather than lat/lon, but it doesn't seem to 554 464 // make a great deal of difference at the scale of most terraces. 555 return new LatLon(a.getCoor().lat() * (1.0 - f) + b.getCoor().lat() * f, 556 a.getCoor().lon() * (1.0 - f) + b.getCoor().lon() * f); 465 return new LatLon( 466 a.getCoor().lat() * (1.0 - f) + b.getCoor().lat() * f, a 467 .getCoor().lon() 468 * (1.0 - f) + b.getCoor().lon() * f); 557 469 } 558 470 }
Note:
See TracChangeset
for help on using the changeset viewer.