Changeset 36181 in osm for applications/editors/josm
- Timestamp:
- 2023-10-25T18:19:52+02:00 (13 months ago)
- Location:
- applications/editors/josm/plugins/terracer
- Files:
-
- 8 added
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
applications/editors/josm/plugins/terracer/src/org/openstreetmap/josm/plugins/terracer/HouseNumberInputDialog.java
r36088 r36181 4 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 5 6 import java.awt.Choice;7 6 import java.awt.Color; 8 7 import java.awt.Container; … … 11 10 import java.awt.GridBagLayout; 12 11 import java.awt.event.ActionEvent; 13 import java.util.ArrayList;14 12 import java.util.Iterator; 13 import java.util.List; 14 import java.util.Set; 15 15 import java.util.TreeSet; 16 16 17 17 import javax.swing.BoxLayout; 18 18 import javax.swing.JCheckBox; 19 import javax.swing.JComponent; 19 20 import javax.swing.JLabel; 20 21 import javax.swing.JPanel; … … 32 33 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionManager; 33 34 import org.openstreetmap.josm.gui.util.WindowGeometry; 35 import org.openstreetmap.josm.gui.widgets.JosmComboBox; 34 36 import org.openstreetmap.josm.spi.preferences.Config; 35 37 import org.openstreetmap.josm.tools.GBC; … … 37 39 /** 38 40 * The HouseNumberInputDialog is the layout of the house number input logic. 39 * 41 * <p> 40 42 * This dialog is concerned with the layout, all logic goes into the 41 43 * HouseNumberinputHandler class. … … 58 60 private final String buildingType; 59 61 private final boolean relationExists; 60 final ArrayList<Node> housenumbers;62 final List<Node> houseNumbers; 61 63 62 64 protected static final String DEFAULT_MESSAGE = tr("Enter housenumbers or amount of segments"); 63 65 private Container jContentPane; 64 66 private JPanel inputPanel; 65 private JLabel loLabel;66 67 JTextField lo; 67 private JLabel hiLabel;68 68 JTextField hi; 69 69 private JLabel numbersLabel; 70 70 JTextField numbers; 71 private JLabel streetLabel;72 71 AutoCompComboBox<String> streetComboBox; 73 private JLabel buildingLabel;74 72 AutoCompComboBox<AutoCompletionItem> buildingComboBox; 75 private JLabel segmentsLabel;76 73 JTextField segments; 77 74 JTextArea messageLabel; 78 private JLabel interpolationLabel; 79 Choice interpolation; 75 JosmComboBox<String> interpolationType; 80 76 JCheckBox handleRelationCheckBox; 81 77 JCheckBox keepOutlineCheckBox; … … 84 80 85 81 /** 82 * Create a new dialog to get settings for the current operation 86 83 * @param street If street is not null, we assume, the name of the street to be fixed 87 84 * and just show a label. If street is null, we show a ComboBox/InputField. … … 91 88 * @param buildingType The value to add for building key 92 89 * @param relationExists If the buildings can be added to an existing relation or not. 93 * @param housenumbers a list of house numbers in this outline (may be empty) 90 * @param houseNumbers a list of house numbers in this outline (may be empty) 91 * @param handler The callback for the inputs 94 92 */ 95 93 public HouseNumberInputDialog(HouseNumberInputHandler handler, Way street, String streetName, 96 String buildingType, boolean relationExists, ArrayList<Node> housenumbers) {94 String buildingType, boolean relationExists, List<Node> houseNumbers) { 97 95 super(MainApplication.getMainFrame(), 98 96 tr("Terrace a house"), … … 105 103 this.buildingType = buildingType; 106 104 this.relationExists = relationExists; 107 this.house numbers = housenumbers;105 this.houseNumbers = houseNumbers; 108 106 handler.dialog = this; 109 107 JPanel content = getInputPanel(); 110 108 setContent(content); 111 setButtonIcons( new String[] {"ok", "cancel" });109 setButtonIcons("ok", "cancel"); 112 110 getJContentPane(); 113 111 initialize(); … … 128 126 this.hi.addFocusListener(this.inputHandler); 129 127 this.segments.addFocusListener(this.inputHandler); 130 this.interpolation .addItemListener(this.inputHandler);128 this.interpolationType.addItemListener(this.inputHandler); 131 129 } 132 130 … … 165 163 messageLabel.setFocusable(false); // Needed so that lowest number can have focus immediately 166 164 167 interpolationLabel = new JLabel(tr("Interpolation"));168 segmentsLabel = new JLabel(tr("Segments"));169 streetLabel = new JLabel(tr("Street"));170 buildingLabel = new JLabel(tr("Building"));171 loLabel = new JLabel(tr("Lowest Number"));165 JLabel interpolationLabel = new JLabel(tr("Interpolation")); 166 JLabel segmentsLabel = new JLabel(tr("Segments")); 167 JLabel streetLabel = new JLabel(tr("Street")); 168 JLabel buildingLabel = new JLabel(tr("Building")); 169 JLabel loLabel = new JLabel(tr("Lowest Number")); 172 170 loLabel.setPreferredSize(new Dimension(111, 16)); 173 171 loLabel.setToolTipText(tr("Lowest housenumber of the terraced house")); 174 hiLabel = new JLabel(tr("Highest Number"));172 JLabel hiLabel = new JLabel(tr("Highest Number")); 175 173 numbersLabel = new JLabel(tr("List of Numbers")); 176 174 loLabel.setPreferredSize(new Dimension(111, 16)); 177 175 final String txt = relationExists ? tr("add to existing associatedStreet relation") : tr("create an associatedStreet relation"); 178 176 179 handleRelationCheckBox = new JCheckBox(txt, relationExists ? Config.getPref().getBoolean(HANDLE_RELATION, true) : false);177 handleRelationCheckBox = new JCheckBox(txt, relationExists && Config.getPref().getBoolean(HANDLE_RELATION, true)); 180 178 keepOutlineCheckBox = new JCheckBox(tr("keep outline way"), Config.getPref().getBoolean(KEEP_OUTLINE, false)); 181 179 … … 187 185 188 186 inputPanel.add(loLabel, GBC.std().insets(3, 3, 0, 0)); 189 inputPanel.add(getLo(), GBC.eol().fill(G BC.HORIZONTAL).insets(5, 3, 0, 0));187 inputPanel.add(getLo(), GBC.eol().fill(GridBagConstraints.HORIZONTAL).insets(5, 3, 0, 0)); 190 188 inputPanel.add(hiLabel, GBC.std().insets(3, 3, 0, 0)); 191 inputPanel.add(getHi(), GBC.eol().fill(G BC.HORIZONTAL).insets(5, 3, 0, 0));189 inputPanel.add(getHi(), GBC.eol().fill(GridBagConstraints.HORIZONTAL).insets(5, 3, 0, 0)); 192 190 inputPanel.add(numbersLabel, GBC.std().insets(3, 3, 0, 0)); 193 inputPanel.add(getNumbers(), GBC.eol().fill(G BC.HORIZONTAL).insets(5, 3, 0, 0));191 inputPanel.add(getNumbers(), GBC.eol().fill(GridBagConstraints.HORIZONTAL).insets(5, 3, 0, 0)); 194 192 inputPanel.add(interpolationLabel, GBC.std().insets(3, 3, 0, 0)); 195 193 inputPanel.add(getInterpolation(), GBC.eol().insets(5, 3, 0, 0)); 196 194 inputPanel.add(segmentsLabel, GBC.std().insets(3, 3, 0, 0)); 197 inputPanel.add(getSegments(), GBC.eol().fill(G BC.HORIZONTAL).insets(5, 3, 0, 0));195 inputPanel.add(getSegments(), GBC.eol().fill(GridBagConstraints.HORIZONTAL).insets(5, 3, 0, 0)); 198 196 if (streetName == null) { 199 197 inputPanel.add(streetLabel, GBC.std().insets(3, 3, 0, 0)); … … 215 213 hi.setEnabled(false); 216 214 interpolationLabel.setVisible(false); 217 interpolation .setVisible(false);218 interpolation .setEnabled(false);219 segments.setText(String.valueOf(house numbers.size()));215 interpolationType.setVisible(false); 216 interpolationType.setEnabled(false); 217 segments.setText(String.valueOf(houseNumbers.size())); 220 218 segments.setEditable(false); 221 219 } … … 267 265 numbers = new JTextField(); 268 266 269 Iterator<Node> it = house numbers.iterator();267 Iterator<Node> it = houseNumbers.iterator(); 270 268 StringBuilder s = new StringBuilder(256); 271 269 if (it.hasNext()) { … … 338 336 * @return java.awt.Choice 339 337 */ 340 private Choice getInterpolation() { 341 if (interpolation == null) { 342 interpolation = new Choice(); 343 interpolation.add(tr("All")); 344 interpolation.add(tr("Even/Odd")); 338 private JComponent getInterpolation() { 339 if (interpolationType == null) { 340 interpolationType = new JosmComboBox<>(); 341 interpolationType.setEditable(false); 342 interpolationType.addItem(tr("All")); 343 interpolationType.addItem(tr("Even/Odd")); 345 344 if (Config.getPref().getInt(INTERPOLATION, 2) == 1) { 346 interpolation .select(tr("All"));345 interpolationType.setSelectedItemText(tr("All")); 347 346 } else { 348 interpolation .select(tr("Even/Odd"));349 } 350 } 351 return interpolation ;347 interpolationType.setSelectedItemText(tr("Even/Odd")); 348 } 349 } 350 return interpolationType; 352 351 } 353 352 … … 355 354 * Generates a list of all visible names of highways in order to do 356 355 * autocompletion on the road name. 357 */ 358 TreeSet<String> createAutoCompletionInfo() { 356 * @return The visible names 357 */ 358 Set<String> createAutoCompletionInfo() { 359 359 final TreeSet<String> names = new TreeSet<>(); 360 360 for (OsmPrimitive osm : MainApplication.getLayerManager().getEditDataSet() -
applications/editors/josm/plugins/terracer/src/org/openstreetmap/josm/plugins/terracer/HouseNumberInputHandler.java
r35827 r36181 8 8 import java.awt.Container; 9 9 import java.awt.event.ActionEvent; 10 import java.awt.event.ActionListener;11 10 import java.awt.event.FocusEvent; 12 11 import java.awt.event.FocusListener; 13 12 import java.awt.event.ItemEvent; 14 13 import java.awt.event.ItemListener; 15 import java.util. ArrayList;14 import java.util.List; 16 15 17 16 import javax.swing.JButton; 18 import javax.swing.JOptionPane;19 17 import javax.swing.JTextField; 20 18 … … 26 24 import org.openstreetmap.josm.gui.tagging.ac.AutoCompComboBox; 27 25 import org.openstreetmap.josm.spi.preferences.Config; 28 import org.openstreetmap.josm.tools.Logging;29 import org.openstreetmap.josm.tools.UserCancelException;30 26 import org.openstreetmap.josm.tools.Utils; 31 27 … … 33 29 * The Class HouseNumberInputHandler contains all the logic 34 30 * behind the house number input dialog. 35 * 31 * <p> 36 32 * From a refactoring viewpoint, this class is indeed more interested in the fields 37 33 * of the HouseNumberInputDialog. This is desired design, as the HouseNumberInputDialog … … 40 36 * @author casualwalker - Copyright 2009 CloudMade Ltd 41 37 */ 42 public class HouseNumberInputHandler extends JosmAction implements ActionListener,FocusListener, ItemListener {38 public class HouseNumberInputHandler extends JosmAction implements FocusListener, ItemListener { 43 39 private final TerracerAction terracerAction; 44 private final Way outline, street; 40 private final Way outline; 41 private final Way street; 45 42 private final String streetName; 46 43 private final Node init; 47 44 private final Relation associatedStreet; 48 private final ArrayList<Node> housenumbers;49 publicHouseNumberInputDialog dialog;45 private final List<Node> housenumbers; 46 HouseNumberInputDialog dialog; 50 47 51 48 /** … … 67 64 final Way outline, final Node init, final Way street, final String streetName, final String buildingType, 68 65 final Relation associatedStreet, 69 final ArrayList<Node> housenumbers, final String title) {66 final List<Node> housenumbers, final String title) { 70 67 this.terracerAction = terracerAction; 71 68 this.outline = outline; … … 109 106 * When the validation fails, a red message is 110 107 * displayed and the OK button is disabled. 111 * 108 * <p> 112 109 * Should be triggered each time the input changes. 110 * @return {@code true} if the inputs are ok 113 111 */ 114 112 private boolean validateInput() { 115 113 boolean isOk = true; 116 StringBuffer message = new StringBuffer();117 118 isOk = isOk &&checkNumberOrder(message);119 isOk = isOk &&checkSegmentsFromHousenumber(message);120 isOk = isOk &&checkSegments(message);114 final StringBuilder message = new StringBuilder(); 115 116 isOk &= checkNumberOrder(message); 117 isOk &= checkSegmentsFromHousenumber(message); 118 isOk &= checkSegments(message); 121 119 122 120 // Allow non numeric characters for the low number as long as there is 123 121 // no high number of the segmentcount is 1 124 122 if (dialog.hi.getText().length() > 0 && (segments() != null || segments() < 1)) { 125 isOk = isOk 126 && checkNumberStringField(dialog.lo, tr("Lowest number"), 123 isOk &= checkNumberStringField(dialog.lo, tr("Lowest number"), 127 124 message); 128 125 } 129 isOk = isOk 130 && checkNumberStringField(dialog.hi, tr("Highest number"), 126 isOk &= checkNumberStringField(dialog.hi, tr("Highest number"), 131 127 message); 132 isOk = isOk 133 && checkNumberStringField(dialog.segments, tr("Segments"), 128 isOk &= checkNumberStringField(dialog.segments, tr("Segments"), 134 129 message); 135 130 131 JButton okButton = getButton(dialog, "OK"); 132 if (okButton != null) 133 okButton.setEnabled(isOk); 136 134 if (isOk) { 137 JButton okButton = getButton(dialog, "OK");138 if (okButton != null)139 okButton.setEnabled(true);140 135 141 136 // For some reason the messageLabel doesn't want to show up … … 144 139 return true; 145 140 } else { 146 JButton okButton = getButton(dialog, "OK");147 if (okButton != null)148 okButton.setEnabled(false);149 150 141 // For some reason the messageLabel doesn't want to show up, so a 151 142 // MessageDialog is shown instead. Someone more knowledgeable might fix this. … … 168 159 * @return true, if successful 169 160 */ 170 private boolean checkNumberOrder(final StringBuffer message) { 171 if (numberFrom() != null && numberTo() != null) { 172 if (numberFrom().intValue() > numberTo().intValue()) { 173 appendMessageNewLine(message); 174 message.append(tr("Lowest housenumber cannot be higher than highest housenumber")); 175 return false; 176 } 161 private boolean checkNumberOrder(final StringBuilder message) { 162 if (numberFrom() != null && numberTo() != null && numberFrom() > numberTo()) { 163 appendMessageNewLine(message); 164 message.append(tr("Lowest housenumber cannot be higher than highest housenumber")); 165 return false; 177 166 } 178 167 return true; … … 182 171 * Obtain the number segments from the house number fields and check, 183 172 * if they are valid. 184 * 173 * <p> 185 174 * Also disables the segments field, if the house numbers contain 186 175 * valid information. … … 190 179 * @return true, if successful 191 180 */ 192 private boolean checkSegmentsFromHousenumber(final StringBu ffer message) {181 private boolean checkSegmentsFromHousenumber(final StringBuilder message) { 193 182 if (!dialog.numbers.isVisible()) { 194 183 dialog.segments.setEditable(true); 195 184 196 185 if (numberFrom() != null && numberTo() != null) { 197 int segments = numberTo() .intValue() - numberFrom().intValue();186 int segments = numberTo() - numberFrom(); 198 187 199 188 if (segments % stepSize() != 0) { … … 222 211 * @return true, if successful 223 212 */ 224 private boolean checkSegments(final StringBu ffer message) {225 if (segments() == null || segments() .intValue()< 1) {213 private boolean checkSegments(final StringBuilder message) { 214 if (segments() == null || segments() < 1) { 226 215 appendMessageNewLine(message); 227 216 message.append(tr("Segment must be a number greater 1")); … … 241 230 * @return true, if successful 242 231 */ 243 private boolean checkNumberStringField(final JTextField field,244 final String label, final StringBu ffer message) {232 private static boolean checkNumberStringField(final JTextField field, 233 final String label, final StringBuilder message) { 245 234 final String content = field.getText(); 246 235 if (content != null && content.length() != 0) { … … 267 256 * @param message the message 268 257 */ 269 private void appendMessageNewLine(final StringBuffer message) {258 private static void appendMessageNewLine(final StringBuilder message) { 270 259 if (message.length() > 0) { 271 260 message.append("\n"); … … 287 276 saveValues(); 288 277 289 try { 290 terracerAction.terraceBuilding( 291 outline, 292 init, 293 street, 294 associatedStreet, 295 segments(), 296 dialog.lo.getText(), 297 dialog.hi.getText(), 298 stepSize(), 299 housenumbers, 300 streetName(), 301 doHandleRelation(), 302 doKeepOutline(), buildingType()); 303 } catch (UserCancelException ex) { 304 Logging.trace(ex); 305 } 278 terracerAction.terraceBuilding( 279 outline, 280 init, 281 street, 282 associatedStreet, 283 segments(), 284 dialog.lo.getText(), 285 dialog.hi.getText(), 286 stepSize(), 287 housenumbers, 288 streetName(), 289 doHandleRelation(), 290 doKeepOutline(), buildingType()); 306 291 307 292 this.dialog.setVisible(false); … … 323 308 */ 324 309 public Integer stepSize() { 325 return dialog.interpolation.getSelectedItem().equals(tr("All")) ? 1 : 2;310 return tr("All").equals(dialog.interpolationType.getSelectedItem()) ? 1 : 2; 326 311 } 327 312 … … 405 390 * Whether the user likes to create a relation or add to 406 391 * an existing one. 392 * @return {@code true} if the user wants to create a relation 407 393 */ 408 394 public boolean doHandleRelation() { 409 if (this.dialog == null) { 410 JOptionPane.showMessageDialog(null, "dialog", "alert", JOptionPane.ERROR_MESSAGE); 411 } 412 if (this.dialog.handleRelationCheckBox == null) { 413 JOptionPane.showMessageDialog(null, "checkbox", "alert", JOptionPane.ERROR_MESSAGE); 414 return true; 415 } else { 416 return this.dialog.handleRelationCheckBox.isSelected(); 417 } 395 return this.dialog.handleRelationCheckBox.isSelected(); 418 396 } 419 397 420 398 /** 421 399 * Whether the user likes to keep the outline way. 400 * @return {@code true} if the user wants to keep the selected outline 422 401 */ 423 402 public boolean doKeepOutline() { -
applications/editors/josm/plugins/terracer/src/org/openstreetmap/josm/plugins/terracer/ReverseTerraceAction.java
r35579 r36181 6 6 import java.awt.event.ActionEvent; 7 7 import java.awt.event.KeyEvent; 8 import java.util.ArrayDeque; 8 9 import java.util.Collection; 9 10 import java.util.Collections; 11 import java.util.Deque; 10 12 import java.util.HashSet; 11 13 import java.util.LinkedList; 14 import java.util.Set; 12 15 13 16 import javax.swing.JOptionPane; … … 26 29 /** 27 30 * Tool to reverse the house numbers in a terrace. 28 * 31 * <p> 29 32 * Useful for when you're using the Terracer tool and the house numbers come out 30 33 * in the wrong direction, or when someone has added house numbers in the wrong 31 34 * direction anyway. 32 * 33 * Finds all connected ways which have a building=* tag on them in order (breadth35 * <p> 36 * Finds all connected ways which have a building=* and addr tag on them in order (breadth 34 37 * first search) and then changes the tags to be the reverse of the order in which 35 38 * they were found. 36 39 */ 37 40 public class ReverseTerraceAction extends JosmAction { 41 private static final String ADDR_HOUSENUMBER = "addr:housenumber"; 38 42 43 /** 44 * Create a new action for reversing a terrace 45 */ 39 46 public ReverseTerraceAction() { 40 47 super(tr("Reverse a terrace"), … … 54 61 public void actionPerformed(ActionEvent e) { 55 62 Collection<Way> selectedWays = MainApplication.getLayerManager().getEditDataSet().getSelectedWays(); 63 reverseTerracedAddresses(selectedWays); 64 } 56 65 66 static void reverseTerracedAddresses(Collection<Way> selectedWays) { 57 67 // Set to keep track of all the nodes that have been visited - that is: if 58 68 // we encounter them again we will not follow onto the connected ways. 59 HashSet<Node> visitedNodes = new HashSet<>();69 Set<Node> visitedNodes = new HashSet<>(); 60 70 61 71 // Set to keep track of the ways the algorithm has seen, but not yet visited. 62 72 // Since when a way is visited all of its nodes are marked as visited, there 63 73 // is no need to keep a visitedWays set. 64 HashSet<Way> front = new HashSet<>(); 65 66 // Find the first or last way from the teracced houses. 67 // It should be connected to exactly one other way. 68 for (Way w : selectedWays) { 69 int conn = 0; 70 for (Way v : selectedWays) { 71 if (w.equals(v)) continue; 72 if (!Collections.disjoint(w.getNodes(), v.getNodes())) { 73 ++conn; 74 } 75 } 76 if (conn == 1) { 77 front.add(w); 78 break; 79 } 80 } 74 final Deque<Way> front = findFirstWay(selectedWays); 81 75 82 76 if (front.isEmpty()) { … … 86 80 } 87 81 82 88 83 // This is like a visitedWays set, but in a linear order. 89 84 LinkedList<Way> orderedWays = new LinkedList<>(); … … 92 87 LinkedList<String> houseNumbers = new LinkedList<>(); 93 88 94 while ( front.size() > 0) {89 while (!front.isEmpty()) { 95 90 // Java apparently doesn't have useful methods to get single items from sets... 96 Way w = front. iterator().next();91 Way w = front.pop(); 97 92 98 93 // Visit all the nodes in the way, adding the building's they're members of … … 101 96 if (!visitedNodes.contains(n)) { 102 97 for (OsmPrimitive prim : n.getReferrers()) { 103 if (prim.keySet().contains("building") && prim instanceof Way) { 98 if (prim.hasKey("building") && prim.hasKey(ADDR_HOUSENUMBER) 99 && prim instanceof Way && !front.contains(prim)) { 104 100 front.add((Way) prim); 105 101 } … … 111 107 // We've finished visiting this way, so record the attributes we're interested 112 108 // in for re-writing. 113 front.remove(w);114 109 orderedWays.addLast(w); 115 houseNumbers.addFirst(w.get( "addr:housenumber"));110 houseNumbers.addFirst(w.get(ADDR_HOUSENUMBER)); 116 111 } 117 112 … … 120 115 commands.add(new ChangePropertyCommand( 121 116 orderedWays.get(i), 122 "addr:housenumber",117 ADDR_HOUSENUMBER, 123 118 houseNumbers.get(i))); 124 119 } … … 128 123 } 129 124 125 private static Deque<Way> findFirstWay(Collection<Way> selectedWays) { 126 // Find the first or last way from the terraced houses. 127 // It should be connected to exactly one other way. 128 for (Way w : selectedWays) { 129 int conn = 0; 130 for (Way v : selectedWays) { 131 if (!w.equals(v) && !Collections.disjoint(w.getNodes(), v.getNodes())) { 132 ++conn; 133 if (conn > 1) { 134 break; 135 } 136 } 137 } 138 if (conn == 1) { 139 return new ArrayDeque<>(Collections.singletonList(w)); 140 } 141 } 142 return new ArrayDeque<>(); 143 } 144 130 145 @Override 131 146 protected void updateEnabledState() { -
applications/editors/josm/plugins/terracer/src/org/openstreetmap/josm/plugins/terracer/TerracerAction.java
r36088 r36181 14 14 import java.util.HashMap; 15 15 import java.util.HashSet; 16 import java.util.Iterator;17 16 import java.util.LinkedList; 18 17 import java.util.List; … … 55 54 * a street (highway=*, name=*) then the given street will be added 56 55 * to the 'associatedStreet' relation. 57 * 56 * <p> 58 57 * 59 58 * At present it only works on quadrilaterals, but there is no reason … … 64 63 */ 65 64 public final class TerracerAction extends JosmAction { 65 private static final String BUILDING = "building"; 66 private static final String ADDR_HOUSENUMBER = "addr:housenumber"; 67 private static final String ADDR_STREET = "addr:street"; 66 68 67 69 private Collection<Command> commands; … … 123 125 } else if (sel.size() > 1) { 124 126 List<Way> ways = new ArrayList<>(Utils.filteredCollection(sel, Way.class)); 125 Iterator<Way> wit = ways.iterator(); 126 while (wit.hasNext()) { 127 Way way = wit.next(); 128 if (way.hasKey("building")) { 127 for (Way way : ways) { 128 if (way.hasKey(BUILDING)) { 129 129 if (outline != null) 130 130 // already have a building … … 140 140 throw new InvalidUserInputException("street does not have any name"); 141 141 } else 142 throw new InvalidUserInputException(way +" is neither a building nor a highway");142 throw new InvalidUserInputException(way + " is neither a building nor a highway"); 143 143 } 144 144 … … 147 147 148 148 List<Node> nodes = new ArrayList<>(Utils.filteredCollection(sel, Node.class)); 149 Iterator<Node> nit = nodes.iterator();150 149 // Actually this should test if the selected address nodes lie 151 150 // within the selected outline. Any ideas how to do this? 152 while (nit.hasNext()) { 153 Node node = nit.next(); 154 if (node.hasKey("addr:housenumber")) { 155 String nodesStreetName = node.get("addr:street"); 151 for (Node node : nodes) { 152 if (node.hasKey(ADDR_HOUSENUMBER)) { 153 String nodesStreetName = node.get(ADDR_STREET); 156 154 // if a node has a street name if must be equal 157 155 // to the one of the other address nodes … … 174 172 } 175 173 176 Collections.sort(housenumbers,new HousenumberNodeComparator());174 housenumbers.sort(new HousenumberNodeComparator()); 177 175 } 178 176 … … 182 180 } catch (InvalidUserInputException ex) { 183 181 Logging.warn("Terracer: "+ex.getMessage()); 184 new ExtendedDialog(MainApplication.getMainFrame(), tr("Invalid selection"), new String[] {"OK"})185 .setButtonIcons( new String[] {"ok"}).setIcon(JOptionPane.INFORMATION_MESSAGE)182 new ExtendedDialog(MainApplication.getMainFrame(), tr("Invalid selection"), "OK") 183 .setButtonIcons("ok").setIcon(JOptionPane.INFORMATION_MESSAGE) 186 184 .setContent(tr("Select a single, closed way of at least four nodes. " + 187 185 "(Optionally you can also select a street for the addr:street tag " + … … 217 215 // Special case of one outline and one address node. 218 216 // Don't open the dialog 219 try { 220 terraceBuilding(outline, init, street, associatedStreet, 0, null, null, 0, 221 housenumbers, streetname, associatedStreet != null, false, "yes"); 222 } catch (UserCancelException ex) { 223 Logging.trace(ex); 224 } 217 terraceBuilding(outline, init, street, associatedStreet, 0, null, null, 0, 218 housenumbers, streetname, associatedStreet != null, false, "yes"); 225 219 } else { 226 220 String title = trn("Change {0} object", "Change {0} objects", sel.size(), sel.size()); 227 221 // show input dialog. 228 new HouseNumberInputHandler(this, outline, init, street, streetname, outline.get( "building"),222 new HouseNumberInputHandler(this, outline, init, street, streetname, outline.get(BUILDING), 229 223 associatedStreet, housenumbers, title).dialog.showDialog(); 230 224 } … … 238 232 } 239 233 240 p ublic Integer getNumber(String number) {234 private static Integer getNumber(String number) { 241 235 try { 242 236 return Integer.parseInt(number); … … 251 245 */ 252 246 static class HousenumberNodeComparator implements Comparator<Node> { 253 private final Pattern pat = Pattern.compile("^(\\d+)\\s*(.*)");247 private static final Pattern PATTERN_HOUSE_NUMBER = Pattern.compile("^(\\d+)\\s*(.*)", Pattern.UNICODE_CHARACTER_CLASS); 254 248 255 249 @Override … … 259 253 // doesn't work for numbers with different number of digits, 260 254 // e.g. 9 is higher than 11 261 String node1String = node1.get( "addr:housenumber");262 String node2String = node2.get( "addr:housenumber");263 Matcher mat = pat.matcher(node1String);255 String node1String = node1.get(ADDR_HOUSENUMBER); 256 String node2String = node2.get(ADDR_HOUSENUMBER); 257 Matcher mat = PATTERN_HOUSE_NUMBER.matcher(node1String); 264 258 if (mat.find()) { 265 259 Integer node1Int = Integer.valueOf(mat.group(1)); 266 260 String node1Rest = mat.group(2); 267 mat = pat.matcher(node2String);261 mat = PATTERN_HOUSE_NUMBER.matcher(node2String); 268 262 if (mat.find()) { 269 263 Integer node2Int = Integer.valueOf(mat.group(1)); … … 285 279 /** 286 280 * Terraces a single, closed, quadrilateral way. 287 * 281 * <p> 288 282 * Any node must be adjacent to both a short and long edge, we naively 289 283 * choose the longest edge and its opposite and interpolate along them … … 307 301 * @param keepOutline If the outline way should be kept 308 302 * @param buildingValue The value for {@code building} key to add 309 * @throws UserCancelException if user cancels the operation310 303 */ 311 304 public void terraceBuilding(final Way outline, Node init, Way street, Relation associatedStreet, Integer segments, 312 305 String start, String end, int step, List<Node> housenumbers, String streetName, boolean handleRelations, 313 boolean keepOutline, String buildingValue) throws UserCancelException{306 boolean keepOutline, String buildingValue) { 314 307 final int nb; 315 Integer to = null, from = null; 308 Integer to; 309 Integer from = null; 316 310 if (housenumbers == null || housenumbers.isEmpty()) { 317 311 to = getNumber(end); 318 312 from = getNumber(start); 319 313 if (to != null && from != null) { 320 nb = 1 + (to .intValue() - from.intValue()) / step;314 nb = 1 + (to - from) / step; 321 315 } else if (segments != null) { 322 nb = segments .intValue();316 nb = segments; 323 317 } else { 324 318 // if we get here, there is is a bug in the input validation. … … 335 329 Pair<Way, Way> interp = findFrontAndBack(outline); 336 330 337 final boolean swap = init != null && (in terp.a.lastNode().equals(init) || interp.b.lastNode().equals(init));331 final boolean swap = init != null && (init.equals(interp.a.lastNode()) || init.equals(interp.b.lastNode())); 338 332 339 333 final double frontLength = wayLength(interp.a); … … 418 412 // Remove the address nodes since their tags have been incorporated into the terraces. 419 413 // Or should removing them also be an option? 420 if ( !housenumbers.isEmpty()) {414 if (housenumbers != null && !housenumbers.isEmpty()) { 421 415 commands.add(DeleteCommand.delete(housenumbers, true, true)); 422 416 } … … 511 505 * @param associatedStreet The associated street. Used to determine if addr:street should be set or not. 512 506 * @param buildingValue The value for {@code building} key to add 513 * @throws UserCancelException if user cancels the operation 507 * @param houseNumbers The house numbers to use 508 * @param i The index to use in {@code houseNumbers} for a replacement house number (preferential) 509 * @param defaultNumber The number to use if there was not an underlying house number 514 510 */ 515 511 private void addressBuilding(Way outline, Way street, String streetName, Relation associatedStreet, 516 List<Node> house numbers, int i, String defaultNumber, String buildingValue) throws UserCancelException{517 Node houseNum = (house numbers != null && i >= 0 && i < housenumbers.size()) ? housenumbers.get(i) : null;512 List<Node> houseNumbers, int i, String defaultNumber, String buildingValue) { 513 Node houseNum = (houseNumbers != null && i >= 0 && i < houseNumbers.size()) ? houseNumbers.get(i) : null; 518 514 boolean buildingAdded = false; 519 515 boolean numberAdded = false; 520 516 Map<String, String> tags = new HashMap<>(); 521 517 if (houseNum != null) { 522 primitives = Arrays.asList( new OsmPrimitive[]{houseNum, outline});518 primitives = Arrays.asList(houseNum, outline); 523 519 524 520 TagCollection tagsToCopy = TagCollection.unionOfAllPrimitives(primitives).getTagsFor(houseNum.keySet()); … … 530 526 } 531 527 532 buildingAdded = houseNum.hasKey( "building");533 numberAdded = houseNum.hasKey( "addr:housenumber");528 buildingAdded = houseNum.hasKey(BUILDING); 529 numberAdded = houseNum.hasKey(ADDR_HOUSENUMBER); 534 530 } 535 531 if (!buildingAdded && buildingValue != null && !buildingValue.isEmpty()) { 536 tags.put( "building", buildingValue);532 tags.put(BUILDING, buildingValue); 537 533 } 538 534 if (defaultNumber != null && !numberAdded) { 539 tags.put( "addr:housenumber", defaultNumber);535 tags.put(ADDR_HOUSENUMBER, defaultNumber); 540 536 } 541 537 // Only put addr:street if no relation exists or if it has no name 542 538 if (associatedStreet == null || !associatedStreet.hasKey("name")) { 543 539 if (street != null) { 544 tags.put( "addr:street", street.get("name"));540 tags.put(ADDR_STREET, street.get("name")); 545 541 } else if (streetName != null && !streetName.trim().isEmpty()) { 546 tags.put( "addr:street", streetName.trim());542 tags.put(ADDR_STREET, streetName.trim()); 547 543 } 548 544 } … … 555 551 * Creates a node at a certain distance along a way, as calculated by the 556 552 * great circle distance. 557 * 553 * <p> 558 554 * Note that this really isn't an efficient way to do this and leads to 559 555 * O(N^2) running time for the main algorithm, but its simple and easy … … 564 560 * @return A node at a distance l along w from the first point. 565 561 */ 566 private Node interpolateAlong(Way w, double l) {562 private static Node interpolateAlong(Way w, double l) { 567 563 List<Pair<Node, Node>> pairs = w.getNodePairs(false); 568 564 for (int i = 0; i < pairs.size(); ++i) { … … 587 583 * @return The length of the way. 588 584 */ 589 private double wayLength(Way w) {585 private static double wayLength(Way w) { 590 586 double length = 0.0; 591 587 for (Pair<Node, Node> p : w.getNodePairs(false)) { … … 603 599 * @return A pair of ways (front, back) pointing in the same directions. 604 600 */ 605 private Pair<Way, Way> findFrontAndBack(Way w) {601 private static Pair<Way, Way> findFrontAndBack(Way w) { 606 602 // calculate the "side-ness" score for each segment of the way 607 603 double[] sideness = calculateSideness(w); … … 658 654 /** 659 655 * returns the distance of two segments of a closed polygon 660 */ 661 private int indexDistance(int i1, int i2, int n) { 656 * @param i1 The first segment index 657 * @param i2 The second segment index 658 * @param n The number of segments in the polygon 659 * @return The distance between the two segments 660 */ 661 private static int indexDistance(int i1, int i2, int n) { 662 662 return Math.min(positiveModulus(i1 - i2, n), positiveModulus(i2 - i1, n)); 663 663 } … … 665 665 /** 666 666 * return the modulus in the range [0, n) 667 */ 668 private int positiveModulus(int a, int n) { 667 * @param a dividend 668 * @param n divisor 669 * @return The positive modulus (if {@code a} is negative) 670 */ 671 private static int positiveModulus(int a, int n) { 669 672 if (n <= 0) 670 673 throw new IllegalArgumentException(); … … 679 682 * Calculate the length of a side (from node i to i+1) in a way. This assumes that 680 683 * the way is closed, but I only ever call it for buildings. 681 */ 682 private double sideLength(Way w, int i) { 684 * @param w The way 685 * @param i The side (0 indexed) 686 * @return The length of that way segment 687 */ 688 private static double sideLength(Way w, int i) { 683 689 Node a = w.getNode(i); 684 690 Node b = w.getNode((i + 1) % (w.getNodesCount() - 1)); … … 690 696 * into order and return the array of indexes such that, for a returned array 691 697 * x, a[x[i]] is sorted for ascending index i. 692 * 698 * <p> 693 699 * This isn't efficient at all, but should be fine for the small arrays we're 694 700 * expecting. If this gets slow - replace it with some more efficient algorithm. … … 698 704 * is in sorted order. 699 705 */ 700 private int[] sortedIndexes(final double[] a) { 701 class SortWithIndex implements Comparable<SortWithIndex> { 702 public double x; 703 public int i; 704 705 SortWithIndex(double a, int b) { 706 x = a; 707 i = b; 708 } 709 710 @Override 711 public int compareTo(SortWithIndex o) { 712 return Double.compare(x, o.x); 713 } 714 } 706 private static int[] sortedIndexes(final double[] a) { 715 707 716 708 final int length = a.length; … … 731 723 /** 732 724 * Calculate "sideness" metric for each segment in a way. 733 */ 734 private double[] calculateSideness(Way w) { 725 * @param w The way to get the sideness metric for 726 * @return The sideness for each segment of the way 727 */ 728 private static double[] calculateSideness(Way w) { 735 729 final int length = w.getNodesCount() - 1; 736 730 double[] sideness = new double[length]; … … 752 746 * segment and its previous and next segments in order. Sideness is calculated 753 747 * for the segment b-c. 754 */ 755 private double calculateSideness(Node a, Node b, Node c, Node d) { 748 * @param a The previous node 749 * @param b The first node of the current segment 750 * @param c The last node of the current segment 751 * @param d The next node 752 * @return the sideness 753 */ 754 private static double calculateSideness(ILatLon a, ILatLon b, ILatLon c, ILatLon d) { 756 755 final double ndx = b.lon() - a.lon(); 757 756 final double pdx = d.lon() - c.lon(); … … 766 765 * Creates a new node at the interpolated position between the argument 767 766 * nodes. Interpolates linearly in projected coordinates. 768 * 767 * <p> 769 768 * If new node coordinate matches a or b coordinates, a or b is returned. 770 769 * … … 774 773 * @return A new node at the interpolated position (or a or b in case if f ≈ 0 or f ≈ 1). 775 774 */ 776 private Node interpolateNode(Node a, Node b, double f) {775 private static Node interpolateNode(Node a, Node b, double f) { 777 776 Node n = new Node(a.getEastNorth().interpolate(b.getEastNorth(), f)); 778 777 if (n.equalsEpsilon(a, ILatLon.MAX_SERVER_PRECISION)) 779 778 return a; 780 779 if (n.equalsEpsilon(b, ILatLon.MAX_SERVER_PRECISION)) … … 787 786 setEnabled(getLayerManager().getEditDataSet() != null); 788 787 } 788 789 private static class SortWithIndex implements Comparable<SortWithIndex> { 790 /** 791 * The value to sort 792 */ 793 public final double x; 794 /** 795 * The index in the original array 796 */ 797 public final int i; 798 799 SortWithIndex(double a, int b) { 800 x = a; 801 i = b; 802 } 803 804 @Override 805 public int compareTo(SortWithIndex o) { 806 return Double.compare(x, o.x); 807 } 808 } 789 809 } -
applications/editors/josm/plugins/terracer/src/org/openstreetmap/josm/plugins/terracer/TerracerPlugin.java
r35327 r36181 26 26 */ 27 27 public class TerracerPlugin extends Plugin implements Destroyable { 28 privateList<JosmAction> actions = Arrays.asList(new TerracerAction(), new ReverseTerraceAction());28 private final List<JosmAction> actions = Arrays.asList(new TerracerAction(), new ReverseTerraceAction()); 29 29 30 30 public TerracerPlugin(PluginInformation info) { 31 31 super(info); 32 32 for (JosmAction action : actions) { 33 33 MainMenu.add(MainApplication.getMenu().moreToolsMenu, action); 34 34 } 35 35 } 36 36 37 38 37 @Override 38 public void destroy() { 39 39 final JMenu moreToolsMenu = MainApplication.getMenu().moreToolsMenu; 40 final Map<Action, Component> actionsMap = Arrays. asList(moreToolsMenu.getMenuComponents()).stream()40 final Map<Action, Component> actionsMap = Arrays.stream(moreToolsMenu.getMenuComponents()) 41 41 .filter(JMenuItem.class::isInstance).map(JMenuItem.class::cast) 42 42 .collect(Collectors.toMap(JMenuItem::getAction, component -> component)); … … 47 47 } 48 48 } 49 50 49 actions.forEach(JosmAction::destroy); 50 } 51 51 }
Note:
See TracChangeset
for help on using the changeset viewer.