Changeset 23191 in osm for applications/editors/josm/plugins/addrinterpolation
- Timestamp:
- 2010-09-15T18:56:19+02:00 (15 years ago)
- Location:
- applications/editors/josm/plugins/addrinterpolation/src/org/openstreetmap/josm/plugins/AddrInterpolation
- Files:
-
- 4 edited
-
AddrInterpolationAction.java (modified) (1 diff)
-
AddrInterpolationDialog.java (modified) (1 diff)
-
AddrInterpolationPlugin.java (modified) (1 diff)
-
EscapeDialog.java (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
TabularUnified applications/editors/josm/plugins/addrinterpolation/src/org/openstreetmap/josm/plugins/AddrInterpolation/AddrInterpolationAction.java ¶
r17762 r23191 22 22 SelectionChangedListener { 23 23 24 public AddrInterpolationAction(){25 super(tr("Address Interpolation"), "AddrInterpolation", tr("Handy Address Interpolation Functions"),26 Shortcut.registerShortcut("tools:AddressInterpolation", tr("Tool: {0}", tr("Address Interpolation")),27 KeyEvent.VK_A, Shortcut.GROUP_MENU,28 InputEvent.ALT_DOWN_MASK | InputEvent.CTRL_DOWN_MASK), false);29 setEnabled(false);30 DataSet.selListeners.add(this);31 }24 public AddrInterpolationAction(){ 25 super(tr("Address Interpolation"), "AddrInterpolation", tr("Handy Address Interpolation Functions"), 26 Shortcut.registerShortcut("tools:AddressInterpolation", tr("Tool: {0}", tr("Address Interpolation")), 27 KeyEvent.VK_A, Shortcut.GROUP_MENU, 28 InputEvent.ALT_DOWN_MASK | InputEvent.CTRL_DOWN_MASK), false); 29 setEnabled(false); 30 DataSet.selListeners.add(this); 31 } 32 32 33 public void actionPerformed(ActionEvent e) {34 AddrInterpolationDialog addrDialog = new AddrInterpolationDialog(tr("Define Address Interpolation"));33 public void actionPerformed(ActionEvent e) { 34 AddrInterpolationDialog addrDialog = new AddrInterpolationDialog(tr("Define Address Interpolation")); 35 35 36 36 37 }37 } 38 38 39 public void selectionChanged(40 Collection<? extends OsmPrimitive> newSelection) {39 public void selectionChanged( 40 Collection<? extends OsmPrimitive> newSelection) { 41 41 42 for (OsmPrimitive osm : newSelection) {43 if (osm instanceof Way) {44 setEnabled(true);45 return;46 }47 }48 setEnabled(false);42 for (OsmPrimitive osm : newSelection) { 43 if (osm instanceof Way) { 44 setEnabled(true); 45 return; 46 } 47 } 48 setEnabled(false); 49 49 50 }50 } 51 51 52 52 } -
TabularUnified applications/editors/josm/plugins/addrinterpolation/src/org/openstreetmap/josm/plugins/AddrInterpolation/AddrInterpolationDialog.java ¶
r23102 r23191 69 69 public class AddrInterpolationDialog extends JDialog implements ActionListener { 70 70 71 private Way selectedStreet = null;72 private Way addrInterpolationWay = null;73 private Relation associatedStreetRelation = null;74 private ArrayList<Node> houseNumberNodes = null; // Additional nodes with addr:housenumber75 76 private static String lastIncrement = "";77 private static int lastAccuracyIndex = 0;78 private static String lastCity = "";79 private static String lastState = "";80 private static String lastPostCode = "";81 private static String lastCountry = "";82 private static String lastFullAddress = "";83 private static boolean lastConvertToHousenumber = false;84 85 // Edit controls86 private EscapeDialog dialog=null;87 private JRadioButton streetNameButton = null;88 private JRadioButton streetRelationButton = null;89 private JTextField startTextField = null;90 private JTextField endTextField = null;91 private JTextField incrementTextField = null;92 private JTextField cityTextField = null;93 private JTextField stateTextField = null;94 private JTextField postCodeTextField = null;95 private JTextField countryTextField = null;96 private JTextField fullTextField = null;97 private Checkbox cbConvertToHouseNumbers = null;98 99 private boolean relationChanged = false; // Whether to re-trigger data changed for relation100 // Track whether interpolation method is known so that auto detect doesn't override a previous choice.101 private boolean interpolationMethodSet = false;102 103 104 // NOTE: The following 2 arrays must match in number of elements and position105 // Tag values for map (Except that 'Numeric' is replaced by actual # on map)106 String[] addrInterpolationTags = { "odd", "even", "all", "alphabetic", "Numeric" };107 String[] addrInterpolationStrings = { tr("Odd"), tr("Even"), tr("All"), tr("Alphabetic"), tr("Numeric") }; // Translatable names for display108 private final int NumericIndex = 4;109 private JComboBox addrInterpolationList = null;110 111 // NOTE: The following 2 arrays must match in number of elements and position112 String[] addrInclusionTags = { "actual", "estimate", "potential" }; // Tag values for map113 String[] addrInclusionStrings = { tr("Actual"), tr("Estimate"), tr("Potential") }; // Translatable names for display114 private JComboBox addrInclusionList = null;115 116 117 118 // For tracking edit changes as group for undo119 private Collection<Command> commandGroup = null;120 private Relation editedRelation = null;121 122 public AddrInterpolationDialog(String name) {123 124 if (!FindAndSaveSelections()) {125 return;126 }127 128 JPanel editControlsPane = CreateEditControls();129 130 ShowDialog(editControlsPane, name);131 132 }133 134 135 136 private void ShowDialog(JPanel editControlsPane, String name) {137 dialog = new EscapeDialog((Frame) Main.parent, name, true);138 139 dialog.add(editControlsPane);140 dialog.setSize(new Dimension(300,500));141 dialog.setLocation(new Point(100,300));142 143 // Listen for windowOpened event to set focus144 dialog.addWindowListener( new WindowAdapter()145 {146 @Override147 public void windowOpened( WindowEvent e )148 {149 if (addrInterpolationWay != null) {150 startTextField.requestFocus();151 }152 else {153 cityTextField.requestFocus();154 }155 }156 });157 158 dialog.setVisible(true);159 160 lastIncrement = incrementTextField.getText();161 lastCity = cityTextField.getText();162 lastState = stateTextField.getText();163 lastPostCode = postCodeTextField.getText();164 lastCountry = countryTextField.getText();165 lastFullAddress = fullTextField.getText();166 lastConvertToHousenumber = cbConvertToHouseNumbers.getState();167 168 }169 170 171 172 // Create edit control items and return JPanel on which they reside173 private JPanel CreateEditControls() {174 175 JPanel editControlsPane = new JPanel();176 GridBagLayout gridbag = new GridBagLayout();177 GridBagConstraints c = new GridBagConstraints();178 179 editControlsPane.setLayout(gridbag);180 181 editControlsPane.setBorder(BorderFactory.createEmptyBorder(15,15,15,15));182 183 184 String streetName = selectedStreet.get("name");185 String streetRelation = FindRelation();186 if (streetRelation.equals("")) {187 streetRelation = " (Create new)";188 }189 streetNameButton = new JRadioButton(tr("Name: {0}", streetName));190 streetRelationButton = new JRadioButton(tr("Relation: {0}", streetRelation));191 if (associatedStreetRelation == null) {192 streetNameButton.setSelected(true);193 }else {194 streetRelationButton.setSelected(true);195 }196 197 // Create edit controls for street / relation radio buttons198 ButtonGroup group = new ButtonGroup();199 group.add(streetNameButton);200 group.add(streetRelationButton);201 JPanel radioButtonPanel = new JPanel(new BorderLayout());202 radioButtonPanel.setBorder(BorderFactory.createTitledBorder(tr("Associate with street using:")));203 radioButtonPanel.add(streetNameButton, BorderLayout.NORTH);204 radioButtonPanel.add(streetRelationButton, BorderLayout.SOUTH);205 206 // Add to edit panel207 c.gridx = 0;208 c.gridwidth = 2; // # of columns to span209 c.fill = GridBagConstraints.HORIZONTAL; // Full width210 c.gridwidth = GridBagConstraints.REMAINDER; //end row211 editControlsPane.add(radioButtonPanel, c);212 213 JLabel numberingLabel = new JLabel(tr("Numbering Scheme:"));214 addrInterpolationList = new JComboBox(addrInterpolationStrings);215 216 JLabel incrementLabel = new JLabel(tr("Increment:"));217 incrementTextField = new JTextField(lastIncrement, 100);218 incrementTextField.setEnabled(false);219 220 JLabel startLabel = new JLabel(tr("Starting #:"));221 JLabel endLabel = new JLabel(tr("Ending #:"));222 223 startTextField = new JTextField(10);224 endTextField = new JTextField(10);225 226 JLabel inclusionLabel = new JLabel(tr("Accuracy:"));227 addrInclusionList = new JComboBox(addrInclusionStrings);228 addrInclusionList.setSelectedIndex(lastAccuracyIndex);229 230 // Preload any values already set in map231 GetExistingMapKeys();232 233 234 JLabel[] textLabels = {startLabel, endLabel, numberingLabel, incrementLabel, inclusionLabel};235 Component[] editFields = {startTextField, endTextField, addrInterpolationList, incrementTextField, addrInclusionList};236 AddEditControlRows(textLabels, editFields,editControlsPane);237 238 cbConvertToHouseNumbers = new Checkbox(tr("Convert way to individual house numbers."), null, lastConvertToHousenumber);239 // cbConvertToHouseNumbers.setSelected(lastConvertToHousenumber);240 241 // Address interpolation fields not valid if Way not selected242 if (addrInterpolationWay == null) {243 addrInterpolationList.setEnabled(false);244 startTextField.setEnabled(false);245 endTextField.setEnabled(false);246 cbConvertToHouseNumbers.setEnabled(false);247 }248 249 250 251 JPanel optionPanel = CreateOptionalFields();252 c.gridx = 0;253 c.gridwidth = 2; // # of columns to span254 c.fill = GridBagConstraints.BOTH; // Full width255 c.gridwidth = GridBagConstraints.REMAINDER; //end row256 257 editControlsPane.add(optionPanel, c);258 259 260 KeyAdapter enterProcessor = new KeyAdapter() {261 @Override262 public void keyPressed(KeyEvent e) {263 if (e.getKeyCode() == KeyEvent.VK_ENTER) {264 if (ValidateAndSave()) {265 dialog.dispose();266 }267 268 }269 }270 };271 272 // Make Enter == OK click on fields using this adapter273 endTextField.addKeyListener(enterProcessor);274 cityTextField.addKeyListener(enterProcessor);275 addrInterpolationList.addKeyListener(enterProcessor);276 incrementTextField.addKeyListener(enterProcessor);277 278 279 // Watch when Interpolation Method combo box is selected so that280 // it can auto-detect method based on entered numbers.281 addrInterpolationList.addFocusListener(new FocusAdapter() {282 @Override283 public void focusGained(FocusEvent fe){284 if (!interpolationMethodSet) {285 if (AutoDetectInterpolationMethod()) {286 interpolationMethodSet = true; // Don't auto detect over a previous choice287 }288 }289 }290 });291 292 293 // Watch when Interpolation Method combo box is changed so that294 // Numeric increment box can be enabled or disabled.295 addrInterpolationList.addActionListener(new ActionListener() {296 public void actionPerformed(ActionEvent e){297 int selectedIndex = addrInterpolationList.getSelectedIndex();298 incrementTextField.setEnabled(selectedIndex == NumericIndex); // Enable or disable numeric field299 }300 });301 302 303 editControlsPane.add(cbConvertToHouseNumbers, c);304 305 306 307 if (houseNumberNodes.size() > 0) {308 JLabel houseNumberNodeNote = new JLabel(tr("Will associate {0} additional house number nodes",309 houseNumberNodes.size() ));310 editControlsPane.add(houseNumberNodeNote, c);311 }312 313 editControlsPane.add(new UrlLabel("http://wiki.openstreetmap.org/wiki/JOSM/Plugins/AddrInterpolation",314 tr("More information about this feature")), c);315 316 317 c.gridx = 0;318 c.gridwidth = 1; //next-to-last319 c.fill = GridBagConstraints.NONE; //reset to default320 c.weightx = 0.0;321 c.insets = new Insets(15, 0, 0, 0);322 c.anchor = GridBagConstraints.LINE_END;323 JButton okButton = new JButton(tr("OK"), ImageProvider.get("ok"));324 editControlsPane.add(okButton, c);325 326 c.gridx = 1;327 c.gridwidth = GridBagConstraints.REMAINDER; //end row328 c.weightx = 1.0;329 c.anchor = GridBagConstraints.LINE_START;330 331 JButton cancelButton = new JButton(tr("Cancel"), ImageProvider.get("cancel"));332 editControlsPane.add(cancelButton, c);333 334 okButton.setActionCommand("ok");335 okButton.addActionListener(this);336 cancelButton.setActionCommand("cancel");337 cancelButton.addActionListener(this);338 339 return editControlsPane;340 }341 342 343 344 // Call after both starting and ending housenumbers have been entered - usually when345 // combo box gets focus.346 // Return true if a method was detected347 private boolean AutoDetectInterpolationMethod() {348 349 String startValueString = ReadTextField(startTextField);350 String endValueString = ReadTextField(endTextField);351 if ( (startValueString == null) || (endValueString== null) ) {352 // Not all values entered yet353 return false;354 }355 356 // String[] addrInterpolationTags = { "odd", "even", "all", "alphabetic", ### }; // Tag values for map357 358 if (isLong(startValueString) && isLong(endValueString)) {359 // Have 2 numeric values360 long startValue = Long.parseLong( startValueString );361 long endValue = Long.parseLong( endValueString );362 363 if (isEven(startValue)) {364 if (isEven(endValue)) {365 SelectInterpolationMethod("even");366 }367 else {368 SelectInterpolationMethod("all");369 }370 } else {371 if (!isEven(endValue)) {372 SelectInterpolationMethod("odd");373 }374 else {375 SelectInterpolationMethod("all");376 }377 }378 379 380 } else {381 // Test for possible alpha382 char startingChar = startValueString.charAt(startValueString.length()-1);383 char endingChar = endValueString.charAt(endValueString.length()-1);384 385 if ( (!IsNumeric("" + startingChar)) && (!IsNumeric("" + endingChar)) ) {386 // Both end with alpha387 SelectInterpolationMethod("alphabetic");388 return true;389 }390 391 if ( (IsNumeric("" + startingChar)) && (!IsNumeric("" + endingChar)) ) {392 endingChar = Character.toUpperCase(endingChar);393 if ( (endingChar >= 'A') && (endingChar <= 'Z') ) {394 // First is a number, last is Latin alpha395 SelectInterpolationMethod("alphabetic");396 return true;397 }398 }399 400 // Did not detect alpha401 return false;402 403 }404 405 return true;406 407 }408 409 410 411 // Set Interpolation Method combo box to method specified by 'currentMethod' (an OSM key value)412 private void SelectInterpolationMethod(String currentMethod) {413 int currentIndex = 0;414 if (isLong(currentMethod)) {415 // Valid number: Numeric increment method416 currentIndex = addrInterpolationTags.length-1;417 incrementTextField.setText(currentMethod);418 incrementTextField.setEnabled(true);419 }420 else {421 // Must scan OSM key values because combo box is already loaded with translated strings422 for (int i=0; i<addrInterpolationTags.length; i++) {423 if (addrInterpolationTags[i].equals(currentMethod)) {424 currentIndex = i;425 break;426 }427 }428 }429 addrInterpolationList.setSelectedIndex(currentIndex);430 431 }432 433 434 // Set Inclusion Method combo box to method specified by 'currentMethod' (an OSM key value)435 private void SelectInclusion(String currentMethod) {436 int currentIndex = 0;437 // Must scan OSM key values because combo box is already loaded with translated strings438 for (int i=0; i<addrInclusionTags.length; i++) {439 if (addrInclusionTags[i].equals(currentMethod)) {440 currentIndex = i;441 break;442 }443 }444 addrInclusionList.setSelectedIndex(currentIndex);445 446 }447 448 449 450 // Create optional control fields in a group box451 private JPanel CreateOptionalFields() {452 453 JPanel editControlsPane = new JPanel();454 GridBagLayout gridbag = new GridBagLayout();455 456 editControlsPane.setLayout(gridbag);457 458 editControlsPane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));459 460 JLabel[] optionalTextLabels = {new JLabel(tr("City:")),461 new JLabel(tr("State:")),462 new JLabel(tr("Post Code:")),463 new JLabel(tr("Country:")),464 new JLabel(tr("Full Address:"))};465 cityTextField = new JTextField(lastCity, 100);466 stateTextField = new JTextField(lastState, 100);467 postCodeTextField = new JTextField(lastPostCode, 20);468 countryTextField = new JTextField(lastCountry, 2);469 fullTextField = new JTextField(lastFullAddress, 300);470 471 // Special processing for addr:country code, max length and uppercase472 countryTextField.addKeyListener(new KeyAdapter() {473 @Override474 public void keyTyped(KeyEvent e) {475 JTextField jtextfield = (JTextField)e.getSource();476 String text = jtextfield.getText();477 int length = text.length();478 if (length == jtextfield.getColumns()) {479 e.consume();480 } else if (length > jtextfield.getColumns()) {481 // show error message ??482 e.consume();483 } else {484 // Accept key; convert to upper case485 if (!e.isActionKey()) {486 e.setKeyChar(Character.toUpperCase(e.getKeyChar()) );487 }488 }489 }490 });491 492 Component[] optionalEditFields = {cityTextField, stateTextField, postCodeTextField, countryTextField, fullTextField};493 AddEditControlRows(optionalTextLabels, optionalEditFields,editControlsPane);494 495 496 497 JPanel optionPanel = new JPanel(new BorderLayout());498 Border groupBox = BorderFactory.createEtchedBorder();499 TitledBorder titleBorder = BorderFactory.createTitledBorder(groupBox, tr("Optional Information:"),500 TitledBorder.LEFT, TitledBorder.TOP);501 502 optionPanel.setBorder(titleBorder);503 optionPanel.add(editControlsPane, BorderLayout.CENTER);504 505 return optionPanel;506 }507 508 509 510 // Populate dialog for any possible existing settings if editing an existing Address interpolation way511 private void GetExistingMapKeys() {512 513 514 // Check all nodes for optional addressing data515 // Address interpolation nodes will overwrite these value if they contain optional data516 for (Node node : houseNumberNodes) {517 CheckNodeForAddressTags(node);518 }519 520 if (addrInterpolationWay != null) {521 String currentMethod = addrInterpolationWay.get("addr:interpolation");522 if (currentMethod != null) {523 524 SelectInterpolationMethod(currentMethod);525 interpolationMethodSet = true; // Don't auto detect over a previous choice526 }527 528 String currentInclusion = addrInterpolationWay.get("addr:inclusion");529 if (currentInclusion != null) {530 SelectInclusion(currentInclusion);531 }532 533 Node firstNode = addrInterpolationWay.getNode(0);534 Node lastNode = addrInterpolationWay.getNode(addrInterpolationWay.getNodesCount()-1);535 536 // Get any existing start / end # values537 String value = firstNode.get("addr:housenumber");538 if (value != null) {539 startTextField.setText(value);540 }541 542 value = lastNode.get("addr:housenumber");543 if (value != null) {544 endTextField.setText(value);545 }546 CheckNodeForAddressTags(firstNode);547 CheckNodeForAddressTags(lastNode);548 549 }550 551 552 553 }554 555 556 // Check for any existing address data. If found,557 // overwrite any previous data558 private void CheckNodeForAddressTags(Node checkNode) {559 560 // Interrogate possible existing optional values561 String value = checkNode.get("addr:city");562 if (value != null) {563 lastCity = value;564 }565 value = checkNode.get("addr:state");566 if (value != null) {567 lastState = value;568 }569 value = checkNode.get("addr:postcode");570 if (value != null) {571 lastPostCode = value;572 }573 value = checkNode.get("addr:country");574 if (value != null) {575 lastCountry = value;576 }577 value = checkNode.get("addr:full");578 if (value != null) {579 lastFullAddress = value;580 }581 582 }583 584 585 586 // Look for a possible 'associatedStreet' type of relation for selected street587 // Returns relation description, or an empty string588 private String FindRelation() {589 String relationDescription = null;590 DataSet currentDataSet = Main.main.getCurrentDataSet();591 if (currentDataSet != null) {592 for (Relation relation : currentDataSet.getRelations()) {593 594 String relationType = relation.get("type");595 if (relationType != null) {596 if (relationType.equals("associatedStreet")) {597 for (RelationMember relationMember : relation.getMembers()) {598 if (relationMember.isWay()){599 Way way = (Way) relationMember.getMember();600 // System.out.println("Name: " + way.get("name") );601 if (way == selectedStreet) {602 associatedStreetRelation = relation;603 relationDescription = Long.toString(way.getId());604 605 String streetName = "";606 if (relation.getKeys().containsKey("name")) {607 streetName = relation.get("name");608 } else {609 // Relation is unnamed - use street name610 streetName = selectedStreet.get("name");611 }612 relationDescription += " (" + streetName + ")";613 return relationDescription;614 }615 }616 617 }618 }619 620 }621 }622 623 }624 625 return "";626 }627 628 629 // We can proceed only if there is both a named way (the 'street') and630 // one un-named way (the address interpolation way ) selected.631 // The plugin menu item is enabled after a single way is selected to display a more meaningful632 // message (a new user may not realize that they need to select both the street and633 // address interpolation way first).634 // Also, selected street and working address interpolation ways are saved.635 private boolean FindAndSaveSelections() {636 637 boolean isValid = false;638 639 int namedWayCount = 0;640 int unNamedWayCount = 0;641 DataSet currentDataSet = Main.main.getCurrentDataSet();642 if (currentDataSet != null) {643 for (OsmPrimitive osm : currentDataSet.getSelectedWays()) {644 Way way = (Way) osm;645 if (way.getKeys().containsKey("name")) {646 namedWayCount++;647 this.selectedStreet = way;648 }649 else {650 unNamedWayCount++;651 this.addrInterpolationWay = way;652 }653 }654 655 // Get additional nodes with addr:housenumber tags:656 // Either selected or in the middle of the Address Interpolation way657 // Do not include end points of Address Interpolation way in this set yet.658 houseNumberNodes = new ArrayList<Node>();659 // Check selected nodes660 for (OsmPrimitive osm : currentDataSet.getSelectedNodes()) {661 Node node = (Node) osm;662 if (node.getKeys().containsKey("addr:housenumber")) {663 houseNumberNodes.add(node);664 }665 }666 667 if (addrInterpolationWay != null) {668 // Check nodes in middle of address interpolation way669 if (addrInterpolationWay.getNodesCount() > 2) {670 for (int i=1; i<(addrInterpolationWay.getNodesCount()-2); i++) {671 Node testNode = addrInterpolationWay.getNode(i);672 if (testNode.getKeys().containsKey("addr:housenumber")) {673 houseNumberNodes.add(testNode);674 }675 }676 }677 }678 679 }680 681 if (namedWayCount != 1) {682 JOptionPane.showMessageDialog(683 Main.parent,684 tr("Please select a street to associate with address interpolation way"),685 tr("Error"),686 JOptionPane.ERROR_MESSAGE687 );688 } else {689 // Avoid 2 error dialogs if both conditions don't match690 if (unNamedWayCount != 1) {691 // Allow for street + house number nodes only to be selected (no address interpolation way).692 if (houseNumberNodes.size() > 0) {693 isValid = true;694 } else {695 JOptionPane.showMessageDialog(696 Main.parent,697 tr("Please select address interpolation way for this street"),698 tr("Error"),699 JOptionPane.ERROR_MESSAGE700 );701 }702 } else {703 isValid = true;704 }705 }706 707 708 return isValid;709 }710 711 712 /**713 * Add rows of edit controls - with labels in the left column, and controls in the right714 * column on the gridbag of the specified container.715 */716 private void AddEditControlRows(JLabel[] labels,717 Component[] editFields,718 Container container) {719 GridBagConstraints c = new GridBagConstraints();720 c.anchor = GridBagConstraints.EAST;721 int numLabels = labels.length;722 723 for (int i = 0; i < numLabels; i++) {724 c.gridx = 0;725 c.gridwidth = 1; //next-to-last726 c.fill = GridBagConstraints.NONE; //reset to default727 c.weightx = 0.0; //reset to default728 container.add(labels[i], c);729 730 c.gridx = 1;731 c.gridwidth = GridBagConstraints.REMAINDER; //end row732 c.fill = GridBagConstraints.HORIZONTAL;733 c.weightx = 1.0;734 container.add(editFields[i], c);735 }736 }737 738 739 740 public void actionPerformed(ActionEvent e) {741 if ("ok".equals(e.getActionCommand())) {742 if (ValidateAndSave()) {743 dialog.dispose();744 }745 746 } else if ("cancel".equals(e.getActionCommand())) {747 dialog.dispose();748 749 }750 }751 752 753 754 // For Alpha interpolation, return base string755 // For example: "22A" -> "22"756 // For example: "A" -> ""757 // Input string must not be empty758 private String BaseAlpha(String strValue) {759 if (strValue.length() > 0) {760 return strValue.substring(0, strValue.length()-1);761 }762 else {763 return "";764 }765 }766 767 768 private char LastChar(String strValue) {769 if (strValue.length() > 0) {770 return strValue.charAt(strValue.length()-1);771 }772 else {773 return 0;774 }775 }776 777 778 // Test for valid positive long int779 private boolean isLong( String input )780 {781 try782 {783 Long val = Long.parseLong( input );784 return (val > 0);785 }786 catch( Exception e)787 {788 return false;789 }790 }791 792 private boolean isEven( Long input )793 {794 return ((input %2) == 0);795 }796 797 private static Pattern p = Pattern.compile("^[0-9]+$");798 private static boolean IsNumeric(String s) {799 return p.matcher(s).matches();800 }801 802 803 private void InterpolateAlphaSection(int startNodeIndex, int endNodeIndex, String endValueString,804 char startingChar, char endingChar) {805 806 807 String baseAlpha = BaseAlpha(endValueString);808 int nSegments =endNodeIndex - startNodeIndex;809 810 double[] segmentLengths = new double[nSegments];811 // Total length of address interpolation way section812 double totalLength= CalculateSegmentLengths(startNodeIndex, endNodeIndex, segmentLengths);813 814 815 int nHouses = endingChar - startingChar-1; // # of house number nodes to create816 if (nHouses > 0) {817 818 double houseSpacing = totalLength / (nHouses+1);819 820 Node lastHouseNode = addrInterpolationWay.getNode(startNodeIndex);821 int currentSegment = 0; // Segment being used to place new house # node822 char currentChar= startingChar;823 while (nHouses > 0) {824 double distanceNeeded = houseSpacing;825 826 // Move along segments until we can place the new house number827 while (distanceNeeded > segmentLengths[currentSegment]) {828 distanceNeeded -= segmentLengths[currentSegment];829 currentSegment++;830 lastHouseNode = addrInterpolationWay.getNode(startNodeIndex + currentSegment);831 }832 833 // House number is to be positioned in current segment.834 double proportion = distanceNeeded / segmentLengths[currentSegment];835 Node toNode = addrInterpolationWay.getNode(startNodeIndex + 1 + currentSegment);836 LatLon newHouseNumberPosition = lastHouseNode.getCoor().interpolate(toNode.getCoor(), proportion);837 838 839 840 Node newHouseNumberNode = new Node(newHouseNumberPosition);841 currentChar++;842 if ( (currentChar >'Z') && (currentChar <'a')) {843 // Wraparound past uppercase Z: go directly to lower case a844 currentChar = 'a';845 846 }847 String newHouseNumber = baseAlpha + currentChar;848 newHouseNumberNode.put("addr:housenumber", newHouseNumber);849 850 commandGroup.add(new AddCommand(newHouseNumberNode));851 houseNumberNodes.add(newHouseNumberNode); // Street, etc information to be added later852 853 lastHouseNode = newHouseNumberNode;854 855 856 segmentLengths[currentSegment] -= distanceNeeded; // Track amount used857 nHouses -- ;858 }859 }860 861 862 }863 864 865 private void CreateAlphaInterpolation(String startValueString, String endValueString) {866 char startingChar = LastChar(startValueString);867 char endingChar = LastChar(endValueString);868 869 if (isLong(startValueString)) {870 // Special case of numeric first value, followed by 'A'871 startingChar = 'A'-1;872 }873 874 // Search for possible anchors from the 2nd node to 2nd from last, interpolating between each anchor875 int startIndex = 0; // Index into first interpolation zone of address interpolation way876 for (int i=1; i<addrInterpolationWay.getNodesCount()-1; i++) {877 Node testNode = addrInterpolationWay.getNode(i);878 String endNodeNumber = testNode.get("addr:housenumber");879 if (endNodeNumber != null) {880 // This is a potential anchor node881 if (endNodeNumber != "") {882 char anchorChar = LastChar(endNodeNumber);883 if ( (anchorChar >startingChar) && (anchorChar < endingChar) ) {884 // Lies within the expected range885 InterpolateAlphaSection(startIndex, i, endNodeNumber, startingChar, anchorChar);886 887 // For next interpolation section888 startingChar = anchorChar;889 startValueString = endNodeNumber;890 startIndex = i;891 }892 }893 894 }895 }896 897 // End nodes do not actually contain housenumber value yet (command has not executed), so use user-entered value898 InterpolateAlphaSection(startIndex, addrInterpolationWay.getNodesCount()-1, endValueString, startingChar, endingChar);899 900 }901 902 903 private double CalculateSegmentLengths(int startNodeIndex, int endNodeIndex, double segmentLengths[]) {904 Node fromNode = addrInterpolationWay.getNode(startNodeIndex);905 double totalLength = 0.0;906 int nSegments = segmentLengths.length;907 for (int segment = 0; segment < nSegments; segment++) {908 Node toNode = addrInterpolationWay.getNode(startNodeIndex + 1 + segment);909 segmentLengths[segment]= fromNode.getCoor().greatCircleDistance(toNode.getCoor());910 totalLength += segmentLengths[segment];911 912 fromNode = toNode;913 }914 return totalLength;915 916 }917 918 919 private void InterpolateNumericSection(int startNodeIndex, int endNodeIndex,920 long startingAddr, long endingAddr,921 long increment) {922 923 924 int nSegments =endNodeIndex - startNodeIndex;925 926 double[] segmentLengths = new double[nSegments];927 928 // Total length of address interpolation way section929 double totalLength= CalculateSegmentLengths(startNodeIndex, endNodeIndex, segmentLengths);930 931 932 int nHouses = (int)((endingAddr - startingAddr) / increment) -1;933 if (nHouses > 0) {934 935 double houseSpacing = totalLength / (nHouses+1);936 937 Node lastHouseNode = addrInterpolationWay.getNode(startNodeIndex);938 int currentSegment = 0; // Segment being used to place new house # node939 long currentHouseNumber = startingAddr;940 while (nHouses > 0) {941 double distanceNeeded = houseSpacing;942 943 // Move along segments until we can place the new house number944 while (distanceNeeded > segmentLengths[currentSegment]) {945 distanceNeeded -= segmentLengths[currentSegment];946 currentSegment++;947 lastHouseNode = addrInterpolationWay.getNode(startNodeIndex + currentSegment);948 }949 950 // House number is to be positioned in current segment.951 double proportion = distanceNeeded / segmentLengths[currentSegment];952 Node toNode = addrInterpolationWay.getNode(startNodeIndex + 1 + currentSegment);953 LatLon newHouseNumberPosition = lastHouseNode.getCoor().interpolate(toNode.getCoor(), proportion);954 955 956 Node newHouseNumberNode = new Node(newHouseNumberPosition);957 currentHouseNumber += increment;958 String newHouseNumber = Long.toString(currentHouseNumber);959 newHouseNumberNode.put("addr:housenumber", newHouseNumber);960 961 commandGroup.add(new AddCommand(newHouseNumberNode));962 houseNumberNodes.add(newHouseNumberNode); // Street, etc information to be added later963 964 lastHouseNode = newHouseNumberNode;965 966 967 segmentLengths[currentSegment] -= distanceNeeded; // Track amount used968 nHouses -- ;969 }970 }971 972 973 }974 975 976 private void CreateNumericInterpolation(String startValueString, String endValueString, long increment) {977 978 long startingAddr = Long.parseLong( startValueString );979 long endingAddr = Long.parseLong( endValueString );980 981 982 // Search for possible anchors from the 2nd node to 2nd from last, interpolating between each anchor983 int startIndex = 0; // Index into first interpolation zone of address interpolation way984 for (int i=1; i<addrInterpolationWay.getNodesCount()-1; i++) {985 Node testNode = addrInterpolationWay.getNode(i);986 String strEndNodeNumber = testNode.get("addr:housenumber");987 if (strEndNodeNumber != null) {988 // This is a potential anchor node989 if (isLong(strEndNodeNumber)) {990 991 long anchorAddrNumber = Long.parseLong( strEndNodeNumber );992 if ( (anchorAddrNumber >startingAddr) && (anchorAddrNumber < endingAddr) ) {993 // Lies within the expected range994 InterpolateNumericSection(startIndex, i, startingAddr, anchorAddrNumber, increment);995 996 // For next interpolation section997 startingAddr = anchorAddrNumber;998 startValueString = strEndNodeNumber;999 startIndex = i;1000 }1001 }1002 1003 }1004 }1005 1006 // End nodes do not actually contain housenumber value yet (command has not executed), so use user-entered value1007 InterpolateNumericSection(startIndex, addrInterpolationWay.getNodesCount()-1, startingAddr, endingAddr, increment);1008 }1009 1010 1011 // Called if user has checked "Convert to House Numbers" checkbox.1012 private void ConvertWayToHousenumbers(String selectedMethod, String startValueString, String endValueString,1013 String incrementString) {1014 // - Use nodes labeled with 'same type' as interim anchors in the middle of the way to identify unequal spacing.1015 // - Ignore nodes of different type; for example '25b' is ignored in sequence 5..151016 1017 // Calculate required number of house numbers to create1018 if (selectedMethod.equals("alphabetic")) {1019 1020 CreateAlphaInterpolation(startValueString, endValueString);1021 1022 1023 } else {1024 long increment = 1;1025 if (selectedMethod.equals("odd") || selectedMethod.equals("even")) {1026 increment = 2;1027 } else if (selectedMethod.equals("Numeric")) {1028 increment = Long.parseLong(incrementString);1029 }1030 CreateNumericInterpolation(startValueString, endValueString, increment);1031 1032 }1033 1034 1035 RemoveAddressInterpolationWay();1036 1037 }1038 1039 1040 private void RemoveAddressInterpolationWay() {1041 1042 // Remove untagged nodes1043 for (int i=1; i<addrInterpolationWay.getNodesCount()-1; i++) {1044 Node testNode = addrInterpolationWay.getNode(i);1045 if (!testNode.hasKeys()) {1046 commandGroup.add(new DeleteCommand(testNode));1047 }1048 }1049 1050 // Remove way1051 commandGroup.add(new DeleteCommand(addrInterpolationWay));1052 addrInterpolationWay = null;1053 1054 }1055 1056 1057 1058 private boolean ValidateAndSave() {1059 1060 String startValueString = ReadTextField(startTextField);1061 String endValueString = ReadTextField(endTextField);1062 String incrementString = ReadTextField(incrementTextField);1063 String city = ReadTextField(cityTextField);1064 String state = ReadTextField(stateTextField);1065 String postCode = ReadTextField(postCodeTextField);1066 String country = ReadTextField(countryTextField);1067 String fullAddress = ReadTextField(fullTextField);1068 1069 String selectedMethod = GetInterpolationMethod();1070 if (addrInterpolationWay != null) {1071 Long startAddr=0L, endAddr=0L;1072 if (!selectedMethod.equals("alphabetic")) {1073 Long[] addrArray = {startAddr, endAddr};1074 if (!ValidAddressNumbers(startValueString, endValueString, addrArray )) {1075 return false;1076 }1077 startAddr = addrArray[0];1078 endAddr = addrArray[1];1079 }1080 1081 String errorMessage = "";1082 if (selectedMethod.equals("odd")) {1083 if (isEven(startAddr) || isEven(endAddr)) {1084 errorMessage = tr("Expected odd numbers for addresses");1085 }1086 1087 } else if (selectedMethod.equals("even")) {1088 if (!isEven(startAddr) || !isEven(endAddr)) {1089 errorMessage = tr("Expected even numbers for addresses");1090 }1091 } else if (selectedMethod.equals("all")) {1092 1093 }else if (selectedMethod.equals("alphabetic")) {1094 errorMessage = ValidateAlphaAddress(startValueString, endValueString);1095 1096 }else if (selectedMethod.equals("Numeric")) {1097 1098 if (!ValidNumericIncrementString(incrementString, startAddr, endAddr)) {1099 errorMessage = tr("Expected valid number for address increment");1100 }1101 1102 }1103 if (!errorMessage.equals("")) {1104 JOptionPane.showMessageDialog(Main.parent, errorMessage, tr("Error"),JOptionPane.ERROR_MESSAGE);1105 return false;1106 }1107 }1108 1109 if (country != null) {1110 if (country.length() != 2) {1111 JOptionPane.showMessageDialog(Main.parent,1112 tr("Country code must be 2 letters"), tr("Error"),JOptionPane.ERROR_MESSAGE);1113 return false;1114 }1115 }1116 1117 // Entries are valid ... save in map1118 1119 commandGroup = new LinkedList<Command>();1120 1121 String streetName = selectedStreet.get("name");1122 1123 if (addrInterpolationWay != null) {1124 1125 Node firstNode = addrInterpolationWay.getNode(0);1126 Node lastNode = addrInterpolationWay.getNode(addrInterpolationWay.getNodesCount()-1);1127 1128 // De-select address interpolation way; leave street selected1129 DataSet currentDataSet = Main.main.getCurrentDataSet();1130 if (currentDataSet != null) {1131 currentDataSet.clearSelection(addrInterpolationWay);1132 currentDataSet.clearSelection(lastNode); // Workaround for JOSM Bug #38381133 }1134 1135 1136 String interpolationTagValue = selectedMethod;1137 if (selectedMethod.equals("Numeric")) {1138 // The interpolation method is the number for 'Numeric' case1139 interpolationTagValue = incrementString;1140 }1141 1142 if (cbConvertToHouseNumbers.getState()) {1143 // Convert way to house numbers is checked.1144 // Create individual nodes and delete interpolation way1145 ConvertWayToHousenumbers(selectedMethod, startValueString, endValueString, incrementString);1146 } else {1147 // Address interpolation way will remain1148 commandGroup.add(new ChangePropertyCommand(addrInterpolationWay, "addr:interpolation", interpolationTagValue));1149 commandGroup.add(new ChangePropertyCommand(addrInterpolationWay, "addr:inclusion", GetInclusionMethod()));1150 }1151 1152 commandGroup.add(new ChangePropertyCommand(firstNode, "addr:housenumber", startValueString));1153 commandGroup.add(new ChangePropertyCommand(lastNode, "addr:housenumber", endValueString));1154 // Add address interpolation house number nodes to main house number node list for common processing1155 houseNumberNodes.add(firstNode);1156 houseNumberNodes.add(lastNode);1157 1158 }1159 1160 1161 1162 if (streetRelationButton.isSelected()) {1163 1164 // Relation button was selected1165 if (associatedStreetRelation == null) {1166 CreateRelation(streetName);1167 // relationChanged = true; (not changed since it was created)1168 }1169 // Make any additional changes only to the copy1170 editedRelation = new Relation(associatedStreetRelation);1171 1172 if (addrInterpolationWay != null) {1173 AddToRelation(associatedStreetRelation, addrInterpolationWay, "house");1174 }1175 }1176 1177 1178 // For all nodes, add to relation and1179 // Add optional text fields to all nodes if specified1180 for (Node node : houseNumberNodes) {1181 1182 if (streetRelationButton.isSelected()) {1183 AddToRelation(associatedStreetRelation, node, "house");1184 }1185 if ((city != null) || (streetNameButton.isSelected()) ) {1186 // Include street unconditionally if adding nodes only or city name specified1187 commandGroup.add(new ChangePropertyCommand(node, "addr:street", streetName));1188 }1189 // Set or remove remaining optional fields1190 commandGroup.add(new ChangePropertyCommand(node, "addr:city", city));1191 commandGroup.add(new ChangePropertyCommand(node, "addr:state", state));1192 commandGroup.add(new ChangePropertyCommand(node, "addr:postcode", postCode));1193 commandGroup.add(new ChangePropertyCommand(node, "addr:country", country));1194 commandGroup.add(new ChangePropertyCommand(node, "addr:full", fullAddress));1195 }1196 1197 if (relationChanged) {1198 commandGroup.add(new ChangeCommand(associatedStreetRelation, editedRelation));1199 }1200 1201 1202 Main.main.undoRedo.add(new SequenceCommand(tr("Address Interpolation"), commandGroup));1203 Main.map.repaint();1204 1205 return true;1206 }1207 1208 1209 private boolean ValidNumericIncrementString(String incrementString, long startingAddr, long endingAddr) {1210 1211 if (!isLong(incrementString)) {1212 return false;1213 }1214 long testIncrement = Long.parseLong(incrementString);1215 if ( (testIncrement <=0) || (testIncrement > endingAddr ) ) {1216 return false;1217 }1218 1219 if ( ((endingAddr - startingAddr) % testIncrement) != 0) {1220 return false;1221 }1222 return true;1223 }1224 1225 1226 1227 // Create Associated Street relation, add street, and add to list of commands to perform1228 private void CreateRelation(String streetName) {1229 associatedStreetRelation = new Relation();1230 associatedStreetRelation.put("name", streetName);1231 associatedStreetRelation.put("type", "associatedStreet");1232 RelationMember newStreetMember = new RelationMember("street", selectedStreet);1233 associatedStreetRelation.addMember(newStreetMember);1234 commandGroup.add(new AddCommand(associatedStreetRelation));1235 }1236 1237 1238 1239 // Read from dialog text box, removing leading and trailing spaces1240 // Return the string, or null for a zero length string1241 private String ReadTextField(JTextField field) {1242 String value = field.getText();1243 if (value != null) {1244 value = value.trim();1245 if (value.equals("")) {1246 value = null;1247 }1248 }1249 return value;1250 }1251 1252 1253 // Test if relation contains specified member1254 // If not already present, it is added1255 private void AddToRelation(Relation relation,OsmPrimitive testMember, String role) {1256 boolean isFound = false;1257 for (RelationMember relationMember : relation.getMembers()) {1258 1259 if (testMember == relationMember.getMember()) {1260 isFound = true;1261 break;1262 }1263 }1264 1265 if (!isFound) {1266 RelationMember newMember = new RelationMember(role, testMember);1267 editedRelation.addMember(newMember);1268 1269 relationChanged = true;1270 }1271 }1272 1273 1274 1275 // Check alphabetic style address1276 // Last character of both must be alpha1277 // Last character of ending must be greater than starting1278 // Return empty error message if OK1279 private String ValidateAlphaAddress(String startValueString,1280 String endValueString) {1281 String errorMessage="";1282 1283 if (startValueString.equals("") || endValueString.equals("")) {1284 errorMessage = tr("Please enter valid number for starting and ending address");1285 } else {1286 char startingChar = LastChar(startValueString);1287 char endingChar = LastChar(endValueString);1288 1289 1290 boolean isOk = false;1291 if ( (IsNumeric("" + startingChar)) && (!IsNumeric("" + endingChar)) ) {1292 endingChar = Character.toUpperCase(endingChar);1293 if ( (endingChar >= 'A') && (endingChar <= 'Z') ) {1294 // First is a number, last is Latin alpha1295 isOk = true;1296 }1297 } else if ( (!IsNumeric("" + startingChar)) && (!IsNumeric("" + endingChar)) ) {1298 // Both are alpha1299 isOk = true;1300 }1301 if (!isOk) {1302 errorMessage = tr("Alphabetic address must end with a letter");1303 }1304 1305 1306 // if a number is included, validate that it is the same number1307 if (endValueString.length() > 1) {1308 1309 // Get number portion of first item: may or may not have letter suffix1310 String numStart = BaseAlpha(startValueString);1311 if (IsNumeric(startValueString)) {1312 numStart = startValueString;1313 }1314 1315 String numEnd = BaseAlpha(endValueString);1316 if (!numStart.equals(numEnd)) {1317 errorMessage = tr("Starting and ending numbers must be the same for alphabetic addresses");1318 }1319 }1320 1321 // ?? Character collation in all languages ??1322 if (startingChar >= endingChar) {1323 errorMessage = tr("Starting address letter must be less than ending address letter");1324 }1325 1326 }1327 1328 return errorMessage;1329 }1330 1331 1332 1333 // Convert string addresses to numeric, with error check1334 private boolean ValidAddressNumbers(String startValueString,1335 String endValueString, Long[] addrArray) {1336 String errorMessage = "";1337 1338 if (!isLong(startValueString)) {1339 errorMessage = tr("Please enter valid number for starting address");1340 }1341 if (!isLong(endValueString)) {1342 errorMessage = tr("Please enter valid number for ending address");1343 }1344 if (errorMessage.equals("")) {1345 addrArray[0] = Long.parseLong( startValueString );1346 addrArray[1] = Long.parseLong( endValueString );1347 1348 if (addrArray[1] <= addrArray[0]) {1349 errorMessage = tr("Starting address number must be less than ending address number");1350 }1351 }1352 1353 if (errorMessage.equals("")) {1354 return true;1355 1356 } else {1357 JOptionPane.showMessageDialog(Main.parent, errorMessage, tr("Error"),JOptionPane.ERROR_MESSAGE);1358 return false;1359 }1360 }1361 1362 1363 1364 private String GetInterpolationMethod() {1365 int selectedIndex = addrInterpolationList.getSelectedIndex();1366 return addrInterpolationTags[selectedIndex];1367 }1368 1369 1370 private String GetInclusionMethod() {1371 int selectedIndex = addrInclusionList.getSelectedIndex();1372 lastAccuracyIndex = selectedIndex;1373 return addrInclusionTags[selectedIndex];1374 }71 private Way selectedStreet = null; 72 private Way addrInterpolationWay = null; 73 private Relation associatedStreetRelation = null; 74 private ArrayList<Node> houseNumberNodes = null; // Additional nodes with addr:housenumber 75 76 private static String lastIncrement = ""; 77 private static int lastAccuracyIndex = 0; 78 private static String lastCity = ""; 79 private static String lastState = ""; 80 private static String lastPostCode = ""; 81 private static String lastCountry = ""; 82 private static String lastFullAddress = ""; 83 private static boolean lastConvertToHousenumber = false; 84 85 // Edit controls 86 private EscapeDialog dialog=null; 87 private JRadioButton streetNameButton = null; 88 private JRadioButton streetRelationButton = null; 89 private JTextField startTextField = null; 90 private JTextField endTextField = null; 91 private JTextField incrementTextField = null; 92 private JTextField cityTextField = null; 93 private JTextField stateTextField = null; 94 private JTextField postCodeTextField = null; 95 private JTextField countryTextField = null; 96 private JTextField fullTextField = null; 97 private Checkbox cbConvertToHouseNumbers = null; 98 99 private boolean relationChanged = false; // Whether to re-trigger data changed for relation 100 // Track whether interpolation method is known so that auto detect doesn't override a previous choice. 101 private boolean interpolationMethodSet = false; 102 103 104 // NOTE: The following 2 arrays must match in number of elements and position 105 // Tag values for map (Except that 'Numeric' is replaced by actual # on map) 106 String[] addrInterpolationTags = { "odd", "even", "all", "alphabetic", "Numeric" }; 107 String[] addrInterpolationStrings = { tr("Odd"), tr("Even"), tr("All"), tr("Alphabetic"), tr("Numeric") }; // Translatable names for display 108 private final int NumericIndex = 4; 109 private JComboBox addrInterpolationList = null; 110 111 // NOTE: The following 2 arrays must match in number of elements and position 112 String[] addrInclusionTags = { "actual", "estimate", "potential" }; // Tag values for map 113 String[] addrInclusionStrings = { tr("Actual"), tr("Estimate"), tr("Potential") }; // Translatable names for display 114 private JComboBox addrInclusionList = null; 115 116 117 118 // For tracking edit changes as group for undo 119 private Collection<Command> commandGroup = null; 120 private Relation editedRelation = null; 121 122 public AddrInterpolationDialog(String name) { 123 124 if (!FindAndSaveSelections()) { 125 return; 126 } 127 128 JPanel editControlsPane = CreateEditControls(); 129 130 ShowDialog(editControlsPane, name); 131 132 } 133 134 135 136 private void ShowDialog(JPanel editControlsPane, String name) { 137 dialog = new EscapeDialog((Frame) Main.parent, name, true); 138 139 dialog.add(editControlsPane); 140 dialog.setSize(new Dimension(300,500)); 141 dialog.setLocation(new Point(100,300)); 142 143 // Listen for windowOpened event to set focus 144 dialog.addWindowListener( new WindowAdapter() 145 { 146 @Override 147 public void windowOpened( WindowEvent e ) 148 { 149 if (addrInterpolationWay != null) { 150 startTextField.requestFocus(); 151 } 152 else { 153 cityTextField.requestFocus(); 154 } 155 } 156 }); 157 158 dialog.setVisible(true); 159 160 lastIncrement = incrementTextField.getText(); 161 lastCity = cityTextField.getText(); 162 lastState = stateTextField.getText(); 163 lastPostCode = postCodeTextField.getText(); 164 lastCountry = countryTextField.getText(); 165 lastFullAddress = fullTextField.getText(); 166 lastConvertToHousenumber = cbConvertToHouseNumbers.getState(); 167 168 } 169 170 171 172 // Create edit control items and return JPanel on which they reside 173 private JPanel CreateEditControls() { 174 175 JPanel editControlsPane = new JPanel(); 176 GridBagLayout gridbag = new GridBagLayout(); 177 GridBagConstraints c = new GridBagConstraints(); 178 179 editControlsPane.setLayout(gridbag); 180 181 editControlsPane.setBorder(BorderFactory.createEmptyBorder(15,15,15,15)); 182 183 184 String streetName = selectedStreet.get("name"); 185 String streetRelation = FindRelation(); 186 if (streetRelation.equals("")) { 187 streetRelation = " (Create new)"; 188 } 189 streetNameButton = new JRadioButton(tr("Name: {0}", streetName)); 190 streetRelationButton = new JRadioButton(tr("Relation: {0}", streetRelation)); 191 if (associatedStreetRelation == null) { 192 streetNameButton.setSelected(true); 193 }else { 194 streetRelationButton.setSelected(true); 195 } 196 197 // Create edit controls for street / relation radio buttons 198 ButtonGroup group = new ButtonGroup(); 199 group.add(streetNameButton); 200 group.add(streetRelationButton); 201 JPanel radioButtonPanel = new JPanel(new BorderLayout()); 202 radioButtonPanel.setBorder(BorderFactory.createTitledBorder(tr("Associate with street using:"))); 203 radioButtonPanel.add(streetNameButton, BorderLayout.NORTH); 204 radioButtonPanel.add(streetRelationButton, BorderLayout.SOUTH); 205 206 // Add to edit panel 207 c.gridx = 0; 208 c.gridwidth = 2; // # of columns to span 209 c.fill = GridBagConstraints.HORIZONTAL; // Full width 210 c.gridwidth = GridBagConstraints.REMAINDER; //end row 211 editControlsPane.add(radioButtonPanel, c); 212 213 JLabel numberingLabel = new JLabel(tr("Numbering Scheme:")); 214 addrInterpolationList = new JComboBox(addrInterpolationStrings); 215 216 JLabel incrementLabel = new JLabel(tr("Increment:")); 217 incrementTextField = new JTextField(lastIncrement, 100); 218 incrementTextField.setEnabled(false); 219 220 JLabel startLabel = new JLabel(tr("Starting #:")); 221 JLabel endLabel = new JLabel(tr("Ending #:")); 222 223 startTextField = new JTextField(10); 224 endTextField = new JTextField(10); 225 226 JLabel inclusionLabel = new JLabel(tr("Accuracy:")); 227 addrInclusionList = new JComboBox(addrInclusionStrings); 228 addrInclusionList.setSelectedIndex(lastAccuracyIndex); 229 230 // Preload any values already set in map 231 GetExistingMapKeys(); 232 233 234 JLabel[] textLabels = {startLabel, endLabel, numberingLabel, incrementLabel, inclusionLabel}; 235 Component[] editFields = {startTextField, endTextField, addrInterpolationList, incrementTextField, addrInclusionList}; 236 AddEditControlRows(textLabels, editFields, editControlsPane); 237 238 cbConvertToHouseNumbers = new Checkbox(tr("Convert way to individual house numbers."), null, lastConvertToHousenumber); 239 // cbConvertToHouseNumbers.setSelected(lastConvertToHousenumber); 240 241 // Address interpolation fields not valid if Way not selected 242 if (addrInterpolationWay == null) { 243 addrInterpolationList.setEnabled(false); 244 startTextField.setEnabled(false); 245 endTextField.setEnabled(false); 246 cbConvertToHouseNumbers.setEnabled(false); 247 } 248 249 250 251 JPanel optionPanel = CreateOptionalFields(); 252 c.gridx = 0; 253 c.gridwidth = 2; // # of columns to span 254 c.fill = GridBagConstraints.BOTH; // Full width 255 c.gridwidth = GridBagConstraints.REMAINDER; //end row 256 257 editControlsPane.add(optionPanel, c); 258 259 260 KeyAdapter enterProcessor = new KeyAdapter() { 261 @Override 262 public void keyPressed(KeyEvent e) { 263 if (e.getKeyCode() == KeyEvent.VK_ENTER) { 264 if (ValidateAndSave()) { 265 dialog.dispose(); 266 } 267 268 } 269 } 270 }; 271 272 // Make Enter == OK click on fields using this adapter 273 endTextField.addKeyListener(enterProcessor); 274 cityTextField.addKeyListener(enterProcessor); 275 addrInterpolationList.addKeyListener(enterProcessor); 276 incrementTextField.addKeyListener(enterProcessor); 277 278 279 // Watch when Interpolation Method combo box is selected so that 280 // it can auto-detect method based on entered numbers. 281 addrInterpolationList.addFocusListener(new FocusAdapter() { 282 @Override 283 public void focusGained(FocusEvent fe){ 284 if (!interpolationMethodSet) { 285 if (AutoDetectInterpolationMethod()) { 286 interpolationMethodSet = true; // Don't auto detect over a previous choice 287 } 288 } 289 } 290 }); 291 292 293 // Watch when Interpolation Method combo box is changed so that 294 // Numeric increment box can be enabled or disabled. 295 addrInterpolationList.addActionListener(new ActionListener() { 296 public void actionPerformed(ActionEvent e){ 297 int selectedIndex = addrInterpolationList.getSelectedIndex(); 298 incrementTextField.setEnabled(selectedIndex == NumericIndex); // Enable or disable numeric field 299 } 300 }); 301 302 303 editControlsPane.add(cbConvertToHouseNumbers, c); 304 305 306 307 if (houseNumberNodes.size() > 0) { 308 JLabel houseNumberNodeNote = new JLabel(tr("Will associate {0} additional house number nodes", 309 houseNumberNodes.size() )); 310 editControlsPane.add(houseNumberNodeNote, c); 311 } 312 313 editControlsPane.add(new UrlLabel("http://wiki.openstreetmap.org/wiki/JOSM/Plugins/AddrInterpolation", 314 tr("More information about this feature")), c); 315 316 317 c.gridx = 0; 318 c.gridwidth = 1; //next-to-last 319 c.fill = GridBagConstraints.NONE; //reset to default 320 c.weightx = 0.0; 321 c.insets = new Insets(15, 0, 0, 0); 322 c.anchor = GridBagConstraints.LINE_END; 323 JButton okButton = new JButton(tr("OK"), ImageProvider.get("ok")); 324 editControlsPane.add(okButton, c); 325 326 c.gridx = 1; 327 c.gridwidth = GridBagConstraints.REMAINDER; //end row 328 c.weightx = 1.0; 329 c.anchor = GridBagConstraints.LINE_START; 330 331 JButton cancelButton = new JButton(tr("Cancel"), ImageProvider.get("cancel")); 332 editControlsPane.add(cancelButton, c); 333 334 okButton.setActionCommand("ok"); 335 okButton.addActionListener(this); 336 cancelButton.setActionCommand("cancel"); 337 cancelButton.addActionListener(this); 338 339 return editControlsPane; 340 } 341 342 343 344 // Call after both starting and ending housenumbers have been entered - usually when 345 // combo box gets focus. 346 // Return true if a method was detected 347 private boolean AutoDetectInterpolationMethod() { 348 349 String startValueString = ReadTextField(startTextField); 350 String endValueString = ReadTextField(endTextField); 351 if ( (startValueString == null) || (endValueString== null) ) { 352 // Not all values entered yet 353 return false; 354 } 355 356 // String[] addrInterpolationTags = { "odd", "even", "all", "alphabetic", ### }; // Tag values for map 357 358 if (isLong(startValueString) && isLong(endValueString)) { 359 // Have 2 numeric values 360 long startValue = Long.parseLong( startValueString ); 361 long endValue = Long.parseLong( endValueString ); 362 363 if (isEven(startValue)) { 364 if (isEven(endValue)) { 365 SelectInterpolationMethod("even"); 366 } 367 else { 368 SelectInterpolationMethod("all"); 369 } 370 } else { 371 if (!isEven(endValue)) { 372 SelectInterpolationMethod("odd"); 373 } 374 else { 375 SelectInterpolationMethod("all"); 376 } 377 } 378 379 380 } else { 381 // Test for possible alpha 382 char startingChar = startValueString.charAt(startValueString.length()-1); 383 char endingChar = endValueString.charAt(endValueString.length()-1); 384 385 if ( (!IsNumeric("" + startingChar)) && (!IsNumeric("" + endingChar)) ) { 386 // Both end with alpha 387 SelectInterpolationMethod("alphabetic"); 388 return true; 389 } 390 391 if ( (IsNumeric("" + startingChar)) && (!IsNumeric("" + endingChar)) ) { 392 endingChar = Character.toUpperCase(endingChar); 393 if ( (endingChar >= 'A') && (endingChar <= 'Z') ) { 394 // First is a number, last is Latin alpha 395 SelectInterpolationMethod("alphabetic"); 396 return true; 397 } 398 } 399 400 // Did not detect alpha 401 return false; 402 403 } 404 405 return true; 406 407 } 408 409 410 411 // Set Interpolation Method combo box to method specified by 'currentMethod' (an OSM key value) 412 private void SelectInterpolationMethod(String currentMethod) { 413 int currentIndex = 0; 414 if (isLong(currentMethod)) { 415 // Valid number: Numeric increment method 416 currentIndex = addrInterpolationTags.length-1; 417 incrementTextField.setText(currentMethod); 418 incrementTextField.setEnabled(true); 419 } 420 else { 421 // Must scan OSM key values because combo box is already loaded with translated strings 422 for (int i=0; i<addrInterpolationTags.length; i++) { 423 if (addrInterpolationTags[i].equals(currentMethod)) { 424 currentIndex = i; 425 break; 426 } 427 } 428 } 429 addrInterpolationList.setSelectedIndex(currentIndex); 430 431 } 432 433 434 // Set Inclusion Method combo box to method specified by 'currentMethod' (an OSM key value) 435 private void SelectInclusion(String currentMethod) { 436 int currentIndex = 0; 437 // Must scan OSM key values because combo box is already loaded with translated strings 438 for (int i=0; i<addrInclusionTags.length; i++) { 439 if (addrInclusionTags[i].equals(currentMethod)) { 440 currentIndex = i; 441 break; 442 } 443 } 444 addrInclusionList.setSelectedIndex(currentIndex); 445 446 } 447 448 449 450 // Create optional control fields in a group box 451 private JPanel CreateOptionalFields() { 452 453 JPanel editControlsPane = new JPanel(); 454 GridBagLayout gridbag = new GridBagLayout(); 455 456 editControlsPane.setLayout(gridbag); 457 458 editControlsPane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 459 460 JLabel[] optionalTextLabels = {new JLabel(tr("City:")), 461 new JLabel(tr("State:")), 462 new JLabel(tr("Post Code:")), 463 new JLabel(tr("Country:")), 464 new JLabel(tr("Full Address:"))}; 465 cityTextField = new JTextField(lastCity, 100); 466 stateTextField = new JTextField(lastState, 100); 467 postCodeTextField = new JTextField(lastPostCode, 20); 468 countryTextField = new JTextField(lastCountry, 2); 469 fullTextField = new JTextField(lastFullAddress, 300); 470 471 // Special processing for addr:country code, max length and uppercase 472 countryTextField.addKeyListener(new KeyAdapter() { 473 @Override 474 public void keyTyped(KeyEvent e) { 475 JTextField jtextfield = (JTextField)e.getSource(); 476 String text = jtextfield.getText(); 477 int length = text.length(); 478 if (length == jtextfield.getColumns()) { 479 e.consume(); 480 } else if (length > jtextfield.getColumns()) { 481 // show error message ?? 482 e.consume(); 483 } else { 484 // Accept key; convert to upper case 485 if (!e.isActionKey()) { 486 e.setKeyChar(Character.toUpperCase(e.getKeyChar()) ); 487 } 488 } 489 } 490 }); 491 492 Component[] optionalEditFields = {cityTextField, stateTextField, postCodeTextField, countryTextField, fullTextField}; 493 AddEditControlRows(optionalTextLabels, optionalEditFields, editControlsPane); 494 495 496 497 JPanel optionPanel = new JPanel(new BorderLayout()); 498 Border groupBox = BorderFactory.createEtchedBorder(); 499 TitledBorder titleBorder = BorderFactory.createTitledBorder(groupBox, tr("Optional Information:"), 500 TitledBorder.LEFT, TitledBorder.TOP); 501 502 optionPanel.setBorder(titleBorder); 503 optionPanel.add(editControlsPane, BorderLayout.CENTER); 504 505 return optionPanel; 506 } 507 508 509 510 // Populate dialog for any possible existing settings if editing an existing Address interpolation way 511 private void GetExistingMapKeys() { 512 513 514 // Check all nodes for optional addressing data 515 // Address interpolation nodes will overwrite these value if they contain optional data 516 for (Node node : houseNumberNodes) { 517 CheckNodeForAddressTags(node); 518 } 519 520 if (addrInterpolationWay != null) { 521 String currentMethod = addrInterpolationWay.get("addr:interpolation"); 522 if (currentMethod != null) { 523 524 SelectInterpolationMethod(currentMethod); 525 interpolationMethodSet = true; // Don't auto detect over a previous choice 526 } 527 528 String currentInclusion = addrInterpolationWay.get("addr:inclusion"); 529 if (currentInclusion != null) { 530 SelectInclusion(currentInclusion); 531 } 532 533 Node firstNode = addrInterpolationWay.getNode(0); 534 Node lastNode = addrInterpolationWay.getNode(addrInterpolationWay.getNodesCount()-1); 535 536 // Get any existing start / end # values 537 String value = firstNode.get("addr:housenumber"); 538 if (value != null) { 539 startTextField.setText(value); 540 } 541 542 value = lastNode.get("addr:housenumber"); 543 if (value != null) { 544 endTextField.setText(value); 545 } 546 CheckNodeForAddressTags(firstNode); 547 CheckNodeForAddressTags(lastNode); 548 549 } 550 551 552 553 } 554 555 556 // Check for any existing address data. If found, 557 // overwrite any previous data 558 private void CheckNodeForAddressTags(Node checkNode) { 559 560 // Interrogate possible existing optional values 561 String value = checkNode.get("addr:city"); 562 if (value != null) { 563 lastCity = value; 564 } 565 value = checkNode.get("addr:state"); 566 if (value != null) { 567 lastState = value; 568 } 569 value = checkNode.get("addr:postcode"); 570 if (value != null) { 571 lastPostCode = value; 572 } 573 value = checkNode.get("addr:country"); 574 if (value != null) { 575 lastCountry = value; 576 } 577 value = checkNode.get("addr:full"); 578 if (value != null) { 579 lastFullAddress = value; 580 } 581 582 } 583 584 585 586 // Look for a possible 'associatedStreet' type of relation for selected street 587 // Returns relation description, or an empty string 588 private String FindRelation() { 589 String relationDescription = null; 590 DataSet currentDataSet = Main.main.getCurrentDataSet(); 591 if (currentDataSet != null) { 592 for (Relation relation : currentDataSet.getRelations()) { 593 594 String relationType = relation.get("type"); 595 if (relationType != null) { 596 if (relationType.equals("associatedStreet")) { 597 for (RelationMember relationMember : relation.getMembers()) { 598 if (relationMember.isWay()){ 599 Way way = (Way) relationMember.getMember(); 600 // System.out.println("Name: " + way.get("name") ); 601 if (way == selectedStreet) { 602 associatedStreetRelation = relation; 603 relationDescription = Long.toString(way.getId()); 604 605 String streetName = ""; 606 if (relation.getKeys().containsKey("name")) { 607 streetName = relation.get("name"); 608 } else { 609 // Relation is unnamed - use street name 610 streetName = selectedStreet.get("name"); 611 } 612 relationDescription += " (" + streetName + ")"; 613 return relationDescription; 614 } 615 } 616 617 } 618 } 619 620 } 621 } 622 623 } 624 625 return ""; 626 } 627 628 629 // We can proceed only if there is both a named way (the 'street') and 630 // one un-named way (the address interpolation way ) selected. 631 // The plugin menu item is enabled after a single way is selected to display a more meaningful 632 // message (a new user may not realize that they need to select both the street and 633 // address interpolation way first). 634 // Also, selected street and working address interpolation ways are saved. 635 private boolean FindAndSaveSelections() { 636 637 boolean isValid = false; 638 639 int namedWayCount = 0; 640 int unNamedWayCount = 0; 641 DataSet currentDataSet = Main.main.getCurrentDataSet(); 642 if (currentDataSet != null) { 643 for (OsmPrimitive osm : currentDataSet.getSelectedWays()) { 644 Way way = (Way) osm; 645 if (way.getKeys().containsKey("name")) { 646 namedWayCount++; 647 this.selectedStreet = way; 648 } 649 else { 650 unNamedWayCount++; 651 this.addrInterpolationWay = way; 652 } 653 } 654 655 // Get additional nodes with addr:housenumber tags: 656 // Either selected or in the middle of the Address Interpolation way 657 // Do not include end points of Address Interpolation way in this set yet. 658 houseNumberNodes = new ArrayList<Node>(); 659 // Check selected nodes 660 for (OsmPrimitive osm : currentDataSet.getSelectedNodes()) { 661 Node node = (Node) osm; 662 if (node.getKeys().containsKey("addr:housenumber")) { 663 houseNumberNodes.add(node); 664 } 665 } 666 667 if (addrInterpolationWay != null) { 668 // Check nodes in middle of address interpolation way 669 if (addrInterpolationWay.getNodesCount() > 2) { 670 for (int i=1; i<(addrInterpolationWay.getNodesCount()-2); i++) { 671 Node testNode = addrInterpolationWay.getNode(i); 672 if (testNode.getKeys().containsKey("addr:housenumber")) { 673 houseNumberNodes.add(testNode); 674 } 675 } 676 } 677 } 678 679 } 680 681 if (namedWayCount != 1) { 682 JOptionPane.showMessageDialog( 683 Main.parent, 684 tr("Please select a street to associate with address interpolation way"), 685 tr("Error"), 686 JOptionPane.ERROR_MESSAGE 687 ); 688 } else { 689 // Avoid 2 error dialogs if both conditions don't match 690 if (unNamedWayCount != 1) { 691 // Allow for street + house number nodes only to be selected (no address interpolation way). 692 if (houseNumberNodes.size() > 0) { 693 isValid = true; 694 } else { 695 JOptionPane.showMessageDialog( 696 Main.parent, 697 tr("Please select address interpolation way for this street"), 698 tr("Error"), 699 JOptionPane.ERROR_MESSAGE 700 ); 701 } 702 } else { 703 isValid = true; 704 } 705 } 706 707 708 return isValid; 709 } 710 711 712 /** 713 * Add rows of edit controls - with labels in the left column, and controls in the right 714 * column on the gridbag of the specified container. 715 */ 716 private void AddEditControlRows(JLabel[] labels, 717 Component[] editFields, 718 Container container) { 719 GridBagConstraints c = new GridBagConstraints(); 720 c.anchor = GridBagConstraints.EAST; 721 int numLabels = labels.length; 722 723 for (int i = 0; i < numLabels; i++) { 724 c.gridx = 0; 725 c.gridwidth = 1; //next-to-last 726 c.fill = GridBagConstraints.NONE; //reset to default 727 c.weightx = 0.0; //reset to default 728 container.add(labels[i], c); 729 730 c.gridx = 1; 731 c.gridwidth = GridBagConstraints.REMAINDER; //end row 732 c.fill = GridBagConstraints.HORIZONTAL; 733 c.weightx = 1.0; 734 container.add(editFields[i], c); 735 } 736 } 737 738 739 740 public void actionPerformed(ActionEvent e) { 741 if ("ok".equals(e.getActionCommand())) { 742 if (ValidateAndSave()) { 743 dialog.dispose(); 744 } 745 746 } else if ("cancel".equals(e.getActionCommand())) { 747 dialog.dispose(); 748 749 } 750 } 751 752 753 754 // For Alpha interpolation, return base string 755 // For example: "22A" -> "22" 756 // For example: "A" -> "" 757 // Input string must not be empty 758 private String BaseAlpha(String strValue) { 759 if (strValue.length() > 0) { 760 return strValue.substring(0, strValue.length()-1); 761 } 762 else { 763 return ""; 764 } 765 } 766 767 768 private char LastChar(String strValue) { 769 if (strValue.length() > 0) { 770 return strValue.charAt(strValue.length()-1); 771 } 772 else { 773 return 0; 774 } 775 } 776 777 778 // Test for valid positive long int 779 private boolean isLong( String input ) 780 { 781 try 782 { 783 Long val = Long.parseLong( input ); 784 return (val > 0); 785 } 786 catch( Exception e) 787 { 788 return false; 789 } 790 } 791 792 private boolean isEven( Long input ) 793 { 794 return ((input %2) == 0); 795 } 796 797 private static Pattern p = Pattern.compile("^[0-9]+$"); 798 private static boolean IsNumeric(String s) { 799 return p.matcher(s).matches(); 800 } 801 802 803 private void InterpolateAlphaSection(int startNodeIndex, int endNodeIndex, String endValueString, 804 char startingChar, char endingChar) { 805 806 807 String baseAlpha = BaseAlpha(endValueString); 808 int nSegments =endNodeIndex - startNodeIndex; 809 810 double[] segmentLengths = new double[nSegments]; 811 // Total length of address interpolation way section 812 double totalLength= CalculateSegmentLengths(startNodeIndex, endNodeIndex, segmentLengths); 813 814 815 int nHouses = endingChar - startingChar-1; // # of house number nodes to create 816 if (nHouses > 0) { 817 818 double houseSpacing = totalLength / (nHouses+1); 819 820 Node lastHouseNode = addrInterpolationWay.getNode(startNodeIndex); 821 int currentSegment = 0; // Segment being used to place new house # node 822 char currentChar= startingChar; 823 while (nHouses > 0) { 824 double distanceNeeded = houseSpacing; 825 826 // Move along segments until we can place the new house number 827 while (distanceNeeded > segmentLengths[currentSegment]) { 828 distanceNeeded -= segmentLengths[currentSegment]; 829 currentSegment++; 830 lastHouseNode = addrInterpolationWay.getNode(startNodeIndex + currentSegment); 831 } 832 833 // House number is to be positioned in current segment. 834 double proportion = distanceNeeded / segmentLengths[currentSegment]; 835 Node toNode = addrInterpolationWay.getNode(startNodeIndex + 1 + currentSegment); 836 LatLon newHouseNumberPosition = lastHouseNode.getCoor().interpolate(toNode.getCoor(), proportion); 837 838 839 840 Node newHouseNumberNode = new Node(newHouseNumberPosition); 841 currentChar++; 842 if ( (currentChar >'Z') && (currentChar <'a')) { 843 // Wraparound past uppercase Z: go directly to lower case a 844 currentChar = 'a'; 845 846 } 847 String newHouseNumber = baseAlpha + currentChar; 848 newHouseNumberNode.put("addr:housenumber", newHouseNumber); 849 850 commandGroup.add(new AddCommand(newHouseNumberNode)); 851 houseNumberNodes.add(newHouseNumberNode); // Street, etc information to be added later 852 853 lastHouseNode = newHouseNumberNode; 854 855 856 segmentLengths[currentSegment] -= distanceNeeded; // Track amount used 857 nHouses -- ; 858 } 859 } 860 861 862 } 863 864 865 private void CreateAlphaInterpolation(String startValueString, String endValueString) { 866 char startingChar = LastChar(startValueString); 867 char endingChar = LastChar(endValueString); 868 869 if (isLong(startValueString)) { 870 // Special case of numeric first value, followed by 'A' 871 startingChar = 'A'-1; 872 } 873 874 // Search for possible anchors from the 2nd node to 2nd from last, interpolating between each anchor 875 int startIndex = 0; // Index into first interpolation zone of address interpolation way 876 for (int i=1; i<addrInterpolationWay.getNodesCount()-1; i++) { 877 Node testNode = addrInterpolationWay.getNode(i); 878 String endNodeNumber = testNode.get("addr:housenumber"); 879 if (endNodeNumber != null) { 880 // This is a potential anchor node 881 if (endNodeNumber != "") { 882 char anchorChar = LastChar(endNodeNumber); 883 if ( (anchorChar >startingChar) && (anchorChar < endingChar) ) { 884 // Lies within the expected range 885 InterpolateAlphaSection(startIndex, i, endNodeNumber, startingChar, anchorChar); 886 887 // For next interpolation section 888 startingChar = anchorChar; 889 startValueString = endNodeNumber; 890 startIndex = i; 891 } 892 } 893 894 } 895 } 896 897 // End nodes do not actually contain housenumber value yet (command has not executed), so use user-entered value 898 InterpolateAlphaSection(startIndex, addrInterpolationWay.getNodesCount()-1, endValueString, startingChar, endingChar); 899 900 } 901 902 903 private double CalculateSegmentLengths(int startNodeIndex, int endNodeIndex, double segmentLengths[]) { 904 Node fromNode = addrInterpolationWay.getNode(startNodeIndex); 905 double totalLength = 0.0; 906 int nSegments = segmentLengths.length; 907 for (int segment = 0; segment < nSegments; segment++) { 908 Node toNode = addrInterpolationWay.getNode(startNodeIndex + 1 + segment); 909 segmentLengths[segment]= fromNode.getCoor().greatCircleDistance(toNode.getCoor()); 910 totalLength += segmentLengths[segment]; 911 912 fromNode = toNode; 913 } 914 return totalLength; 915 916 } 917 918 919 private void InterpolateNumericSection(int startNodeIndex, int endNodeIndex, 920 long startingAddr, long endingAddr, 921 long increment) { 922 923 924 int nSegments =endNodeIndex - startNodeIndex; 925 926 double[] segmentLengths = new double[nSegments]; 927 928 // Total length of address interpolation way section 929 double totalLength= CalculateSegmentLengths(startNodeIndex, endNodeIndex, segmentLengths); 930 931 932 int nHouses = (int)((endingAddr - startingAddr) / increment) -1; 933 if (nHouses > 0) { 934 935 double houseSpacing = totalLength / (nHouses+1); 936 937 Node lastHouseNode = addrInterpolationWay.getNode(startNodeIndex); 938 int currentSegment = 0; // Segment being used to place new house # node 939 long currentHouseNumber = startingAddr; 940 while (nHouses > 0) { 941 double distanceNeeded = houseSpacing; 942 943 // Move along segments until we can place the new house number 944 while (distanceNeeded > segmentLengths[currentSegment]) { 945 distanceNeeded -= segmentLengths[currentSegment]; 946 currentSegment++; 947 lastHouseNode = addrInterpolationWay.getNode(startNodeIndex + currentSegment); 948 } 949 950 // House number is to be positioned in current segment. 951 double proportion = distanceNeeded / segmentLengths[currentSegment]; 952 Node toNode = addrInterpolationWay.getNode(startNodeIndex + 1 + currentSegment); 953 LatLon newHouseNumberPosition = lastHouseNode.getCoor().interpolate(toNode.getCoor(), proportion); 954 955 956 Node newHouseNumberNode = new Node(newHouseNumberPosition); 957 currentHouseNumber += increment; 958 String newHouseNumber = Long.toString(currentHouseNumber); 959 newHouseNumberNode.put("addr:housenumber", newHouseNumber); 960 961 commandGroup.add(new AddCommand(newHouseNumberNode)); 962 houseNumberNodes.add(newHouseNumberNode); // Street, etc information to be added later 963 964 lastHouseNode = newHouseNumberNode; 965 966 967 segmentLengths[currentSegment] -= distanceNeeded; // Track amount used 968 nHouses -- ; 969 } 970 } 971 972 973 } 974 975 976 private void CreateNumericInterpolation(String startValueString, String endValueString, long increment) { 977 978 long startingAddr = Long.parseLong( startValueString ); 979 long endingAddr = Long.parseLong( endValueString ); 980 981 982 // Search for possible anchors from the 2nd node to 2nd from last, interpolating between each anchor 983 int startIndex = 0; // Index into first interpolation zone of address interpolation way 984 for (int i=1; i<addrInterpolationWay.getNodesCount()-1; i++) { 985 Node testNode = addrInterpolationWay.getNode(i); 986 String strEndNodeNumber = testNode.get("addr:housenumber"); 987 if (strEndNodeNumber != null) { 988 // This is a potential anchor node 989 if (isLong(strEndNodeNumber)) { 990 991 long anchorAddrNumber = Long.parseLong( strEndNodeNumber ); 992 if ( (anchorAddrNumber >startingAddr) && (anchorAddrNumber < endingAddr) ) { 993 // Lies within the expected range 994 InterpolateNumericSection(startIndex, i, startingAddr, anchorAddrNumber, increment); 995 996 // For next interpolation section 997 startingAddr = anchorAddrNumber; 998 startValueString = strEndNodeNumber; 999 startIndex = i; 1000 } 1001 } 1002 1003 } 1004 } 1005 1006 // End nodes do not actually contain housenumber value yet (command has not executed), so use user-entered value 1007 InterpolateNumericSection(startIndex, addrInterpolationWay.getNodesCount()-1, startingAddr, endingAddr, increment); 1008 } 1009 1010 1011 // Called if user has checked "Convert to House Numbers" checkbox. 1012 private void ConvertWayToHousenumbers(String selectedMethod, String startValueString, String endValueString, 1013 String incrementString) { 1014 // - Use nodes labeled with 'same type' as interim anchors in the middle of the way to identify unequal spacing. 1015 // - Ignore nodes of different type; for example '25b' is ignored in sequence 5..15 1016 1017 // Calculate required number of house numbers to create 1018 if (selectedMethod.equals("alphabetic")) { 1019 1020 CreateAlphaInterpolation(startValueString, endValueString); 1021 1022 1023 } else { 1024 long increment = 1; 1025 if (selectedMethod.equals("odd") || selectedMethod.equals("even")) { 1026 increment = 2; 1027 } else if (selectedMethod.equals("Numeric")) { 1028 increment = Long.parseLong(incrementString); 1029 } 1030 CreateNumericInterpolation(startValueString, endValueString, increment); 1031 1032 } 1033 1034 1035 RemoveAddressInterpolationWay(); 1036 1037 } 1038 1039 1040 private void RemoveAddressInterpolationWay() { 1041 1042 // Remove untagged nodes 1043 for (int i=1; i<addrInterpolationWay.getNodesCount()-1; i++) { 1044 Node testNode = addrInterpolationWay.getNode(i); 1045 if (!testNode.hasKeys()) { 1046 commandGroup.add(new DeleteCommand(testNode)); 1047 } 1048 } 1049 1050 // Remove way 1051 commandGroup.add(new DeleteCommand(addrInterpolationWay)); 1052 addrInterpolationWay = null; 1053 1054 } 1055 1056 1057 1058 private boolean ValidateAndSave() { 1059 1060 String startValueString = ReadTextField(startTextField); 1061 String endValueString = ReadTextField(endTextField); 1062 String incrementString = ReadTextField(incrementTextField); 1063 String city = ReadTextField(cityTextField); 1064 String state = ReadTextField(stateTextField); 1065 String postCode = ReadTextField(postCodeTextField); 1066 String country = ReadTextField(countryTextField); 1067 String fullAddress = ReadTextField(fullTextField); 1068 1069 String selectedMethod = GetInterpolationMethod(); 1070 if (addrInterpolationWay != null) { 1071 Long startAddr=0L, endAddr=0L; 1072 if (!selectedMethod.equals("alphabetic")) { 1073 Long[] addrArray = {startAddr, endAddr}; 1074 if (!ValidAddressNumbers(startValueString, endValueString, addrArray )) { 1075 return false; 1076 } 1077 startAddr = addrArray[0]; 1078 endAddr = addrArray[1]; 1079 } 1080 1081 String errorMessage = ""; 1082 if (selectedMethod.equals("odd")) { 1083 if (isEven(startAddr) || isEven(endAddr)) { 1084 errorMessage = tr("Expected odd numbers for addresses"); 1085 } 1086 1087 } else if (selectedMethod.equals("even")) { 1088 if (!isEven(startAddr) || !isEven(endAddr)) { 1089 errorMessage = tr("Expected even numbers for addresses"); 1090 } 1091 } else if (selectedMethod.equals("all")) { 1092 1093 }else if (selectedMethod.equals("alphabetic")) { 1094 errorMessage = ValidateAlphaAddress(startValueString, endValueString); 1095 1096 }else if (selectedMethod.equals("Numeric")) { 1097 1098 if (!ValidNumericIncrementString(incrementString, startAddr, endAddr)) { 1099 errorMessage = tr("Expected valid number for address increment"); 1100 } 1101 1102 } 1103 if (!errorMessage.equals("")) { 1104 JOptionPane.showMessageDialog(Main.parent, errorMessage, tr("Error"), JOptionPane.ERROR_MESSAGE); 1105 return false; 1106 } 1107 } 1108 1109 if (country != null) { 1110 if (country.length() != 2) { 1111 JOptionPane.showMessageDialog(Main.parent, 1112 tr("Country code must be 2 letters"), tr("Error"), JOptionPane.ERROR_MESSAGE); 1113 return false; 1114 } 1115 } 1116 1117 // Entries are valid ... save in map 1118 1119 commandGroup = new LinkedList<Command>(); 1120 1121 String streetName = selectedStreet.get("name"); 1122 1123 if (addrInterpolationWay != null) { 1124 1125 Node firstNode = addrInterpolationWay.getNode(0); 1126 Node lastNode = addrInterpolationWay.getNode(addrInterpolationWay.getNodesCount()-1); 1127 1128 // De-select address interpolation way; leave street selected 1129 DataSet currentDataSet = Main.main.getCurrentDataSet(); 1130 if (currentDataSet != null) { 1131 currentDataSet.clearSelection(addrInterpolationWay); 1132 currentDataSet.clearSelection(lastNode); // Workaround for JOSM Bug #3838 1133 } 1134 1135 1136 String interpolationTagValue = selectedMethod; 1137 if (selectedMethod.equals("Numeric")) { 1138 // The interpolation method is the number for 'Numeric' case 1139 interpolationTagValue = incrementString; 1140 } 1141 1142 if (cbConvertToHouseNumbers.getState()) { 1143 // Convert way to house numbers is checked. 1144 // Create individual nodes and delete interpolation way 1145 ConvertWayToHousenumbers(selectedMethod, startValueString, endValueString, incrementString); 1146 } else { 1147 // Address interpolation way will remain 1148 commandGroup.add(new ChangePropertyCommand(addrInterpolationWay, "addr:interpolation", interpolationTagValue)); 1149 commandGroup.add(new ChangePropertyCommand(addrInterpolationWay, "addr:inclusion", GetInclusionMethod())); 1150 } 1151 1152 commandGroup.add(new ChangePropertyCommand(firstNode, "addr:housenumber", startValueString)); 1153 commandGroup.add(new ChangePropertyCommand(lastNode, "addr:housenumber", endValueString)); 1154 // Add address interpolation house number nodes to main house number node list for common processing 1155 houseNumberNodes.add(firstNode); 1156 houseNumberNodes.add(lastNode); 1157 1158 } 1159 1160 1161 1162 if (streetRelationButton.isSelected()) { 1163 1164 // Relation button was selected 1165 if (associatedStreetRelation == null) { 1166 CreateRelation(streetName); 1167 // relationChanged = true; (not changed since it was created) 1168 } 1169 // Make any additional changes only to the copy 1170 editedRelation = new Relation(associatedStreetRelation); 1171 1172 if (addrInterpolationWay != null) { 1173 AddToRelation(associatedStreetRelation, addrInterpolationWay, "house"); 1174 } 1175 } 1176 1177 1178 // For all nodes, add to relation and 1179 // Add optional text fields to all nodes if specified 1180 for (Node node : houseNumberNodes) { 1181 1182 if (streetRelationButton.isSelected()) { 1183 AddToRelation(associatedStreetRelation, node, "house"); 1184 } 1185 if ((city != null) || (streetNameButton.isSelected()) ) { 1186 // Include street unconditionally if adding nodes only or city name specified 1187 commandGroup.add(new ChangePropertyCommand(node, "addr:street", streetName)); 1188 } 1189 // Set or remove remaining optional fields 1190 commandGroup.add(new ChangePropertyCommand(node, "addr:city", city)); 1191 commandGroup.add(new ChangePropertyCommand(node, "addr:state", state)); 1192 commandGroup.add(new ChangePropertyCommand(node, "addr:postcode", postCode)); 1193 commandGroup.add(new ChangePropertyCommand(node, "addr:country", country)); 1194 commandGroup.add(new ChangePropertyCommand(node, "addr:full", fullAddress)); 1195 } 1196 1197 if (relationChanged) { 1198 commandGroup.add(new ChangeCommand(associatedStreetRelation, editedRelation)); 1199 } 1200 1201 1202 Main.main.undoRedo.add(new SequenceCommand(tr("Address Interpolation"), commandGroup)); 1203 Main.map.repaint(); 1204 1205 return true; 1206 } 1207 1208 1209 private boolean ValidNumericIncrementString(String incrementString, long startingAddr, long endingAddr) { 1210 1211 if (!isLong(incrementString)) { 1212 return false; 1213 } 1214 long testIncrement = Long.parseLong(incrementString); 1215 if ( (testIncrement <=0) || (testIncrement > endingAddr ) ) { 1216 return false; 1217 } 1218 1219 if ( ((endingAddr - startingAddr) % testIncrement) != 0) { 1220 return false; 1221 } 1222 return true; 1223 } 1224 1225 1226 1227 // Create Associated Street relation, add street, and add to list of commands to perform 1228 private void CreateRelation(String streetName) { 1229 associatedStreetRelation = new Relation(); 1230 associatedStreetRelation.put("name", streetName); 1231 associatedStreetRelation.put("type", "associatedStreet"); 1232 RelationMember newStreetMember = new RelationMember("street", selectedStreet); 1233 associatedStreetRelation.addMember(newStreetMember); 1234 commandGroup.add(new AddCommand(associatedStreetRelation)); 1235 } 1236 1237 1238 1239 // Read from dialog text box, removing leading and trailing spaces 1240 // Return the string, or null for a zero length string 1241 private String ReadTextField(JTextField field) { 1242 String value = field.getText(); 1243 if (value != null) { 1244 value = value.trim(); 1245 if (value.equals("")) { 1246 value = null; 1247 } 1248 } 1249 return value; 1250 } 1251 1252 1253 // Test if relation contains specified member 1254 // If not already present, it is added 1255 private void AddToRelation(Relation relation, OsmPrimitive testMember, String role) { 1256 boolean isFound = false; 1257 for (RelationMember relationMember : relation.getMembers()) { 1258 1259 if (testMember == relationMember.getMember()) { 1260 isFound = true; 1261 break; 1262 } 1263 } 1264 1265 if (!isFound) { 1266 RelationMember newMember = new RelationMember(role, testMember); 1267 editedRelation.addMember(newMember); 1268 1269 relationChanged = true; 1270 } 1271 } 1272 1273 1274 1275 // Check alphabetic style address 1276 // Last character of both must be alpha 1277 // Last character of ending must be greater than starting 1278 // Return empty error message if OK 1279 private String ValidateAlphaAddress(String startValueString, 1280 String endValueString) { 1281 String errorMessage=""; 1282 1283 if (startValueString.equals("") || endValueString.equals("")) { 1284 errorMessage = tr("Please enter valid number for starting and ending address"); 1285 } else { 1286 char startingChar = LastChar(startValueString); 1287 char endingChar = LastChar(endValueString); 1288 1289 1290 boolean isOk = false; 1291 if ( (IsNumeric("" + startingChar)) && (!IsNumeric("" + endingChar)) ) { 1292 endingChar = Character.toUpperCase(endingChar); 1293 if ( (endingChar >= 'A') && (endingChar <= 'Z') ) { 1294 // First is a number, last is Latin alpha 1295 isOk = true; 1296 } 1297 } else if ( (!IsNumeric("" + startingChar)) && (!IsNumeric("" + endingChar)) ) { 1298 // Both are alpha 1299 isOk = true; 1300 } 1301 if (!isOk) { 1302 errorMessage = tr("Alphabetic address must end with a letter"); 1303 } 1304 1305 1306 // if a number is included, validate that it is the same number 1307 if (endValueString.length() > 1) { 1308 1309 // Get number portion of first item: may or may not have letter suffix 1310 String numStart = BaseAlpha(startValueString); 1311 if (IsNumeric(startValueString)) { 1312 numStart = startValueString; 1313 } 1314 1315 String numEnd = BaseAlpha(endValueString); 1316 if (!numStart.equals(numEnd)) { 1317 errorMessage = tr("Starting and ending numbers must be the same for alphabetic addresses"); 1318 } 1319 } 1320 1321 // ?? Character collation in all languages ?? 1322 if (startingChar >= endingChar) { 1323 errorMessage = tr("Starting address letter must be less than ending address letter"); 1324 } 1325 1326 } 1327 1328 return errorMessage; 1329 } 1330 1331 1332 1333 // Convert string addresses to numeric, with error check 1334 private boolean ValidAddressNumbers(String startValueString, 1335 String endValueString, Long[] addrArray) { 1336 String errorMessage = ""; 1337 1338 if (!isLong(startValueString)) { 1339 errorMessage = tr("Please enter valid number for starting address"); 1340 } 1341 if (!isLong(endValueString)) { 1342 errorMessage = tr("Please enter valid number for ending address"); 1343 } 1344 if (errorMessage.equals("")) { 1345 addrArray[0] = Long.parseLong( startValueString ); 1346 addrArray[1] = Long.parseLong( endValueString ); 1347 1348 if (addrArray[1] <= addrArray[0]) { 1349 errorMessage = tr("Starting address number must be less than ending address number"); 1350 } 1351 } 1352 1353 if (errorMessage.equals("")) { 1354 return true; 1355 1356 } else { 1357 JOptionPane.showMessageDialog(Main.parent, errorMessage, tr("Error"), JOptionPane.ERROR_MESSAGE); 1358 return false; 1359 } 1360 } 1361 1362 1363 1364 private String GetInterpolationMethod() { 1365 int selectedIndex = addrInterpolationList.getSelectedIndex(); 1366 return addrInterpolationTags[selectedIndex]; 1367 } 1368 1369 1370 private String GetInclusionMethod() { 1371 int selectedIndex = addrInclusionList.getSelectedIndex(); 1372 lastAccuracyIndex = selectedIndex; 1373 return addrInclusionTags[selectedIndex]; 1374 } 1375 1375 1376 1376 -
TabularUnified applications/editors/josm/plugins/addrinterpolation/src/org/openstreetmap/josm/plugins/AddrInterpolation/AddrInterpolationPlugin.java ¶
r19422 r23191 9 9 public class AddrInterpolationPlugin extends Plugin { 10 10 11 AddrInterpolationAction action = null;11 AddrInterpolationAction action = null; 12 12 13 /**14 * constructor15 */16 public AddrInterpolationPlugin(PluginInformation info) {17 super(info);18 action = new AddrInterpolationAction();19 Main.main.menu.toolsMenu.add(action);20 }13 /** 14 * constructor 15 */ 16 public AddrInterpolationPlugin(PluginInformation info) { 17 super(info); 18 action = new AddrInterpolationAction(); 19 Main.main.menu.toolsMenu.add(action); 20 } 21 21 } -
TabularUnified applications/editors/josm/plugins/addrinterpolation/src/org/openstreetmap/josm/plugins/AddrInterpolation/EscapeDialog.java ¶
r17721 r23191 15 15 16 16 public class EscapeDialog extends JDialog { 17 public EscapeDialog() {18 this((Frame)null, false);19 }20 public EscapeDialog(Frame owner) {21 this(owner, false);22 }23 public EscapeDialog(Frame owner, boolean modal) {24 this(owner, null, modal);25 }26 public EscapeDialog(Frame owner, String title) {27 this(owner, title, false);28 }29 public EscapeDialog(Frame owner, String title, boolean modal) {30 super(owner, title, modal);31 }32 public EscapeDialog(Dialog owner) {33 this(owner, false);34 }35 public EscapeDialog(Dialog owner, boolean modal) {36 this(owner, null, modal);37 }38 public EscapeDialog(Dialog owner, String title) {39 this(owner, title, false);40 }41 public EscapeDialog(Dialog owner, String title, boolean modal) {42 super(owner, title, modal);43 }17 public EscapeDialog() { 18 this((Frame)null, false); 19 } 20 public EscapeDialog(Frame owner) { 21 this(owner, false); 22 } 23 public EscapeDialog(Frame owner, boolean modal) { 24 this(owner, null, modal); 25 } 26 public EscapeDialog(Frame owner, String title) { 27 this(owner, title, false); 28 } 29 public EscapeDialog(Frame owner, String title, boolean modal) { 30 super(owner, title, modal); 31 } 32 public EscapeDialog(Dialog owner) { 33 this(owner, false); 34 } 35 public EscapeDialog(Dialog owner, boolean modal) { 36 this(owner, null, modal); 37 } 38 public EscapeDialog(Dialog owner, String title) { 39 this(owner, title, false); 40 } 41 public EscapeDialog(Dialog owner, String title, boolean modal) { 42 super(owner, title, modal); 43 } 44 44 45 45 46 @Override47 protected JRootPane createRootPane() {48 ActionListener escapeActionListener = new ActionListener() {49 public void actionPerformed(ActionEvent actionEvent) {50 dispose();51 // setVisible(false);52 }53 };54 JRootPane rootPane = new JRootPane();55 KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);56 rootPane.registerKeyboardAction(escapeActionListener, stroke, JComponent.WHEN_IN_FOCUSED_WINDOW);46 @Override 47 protected JRootPane createRootPane() { 48 ActionListener escapeActionListener = new ActionListener() { 49 public void actionPerformed(ActionEvent actionEvent) { 50 dispose(); 51 // setVisible(false); 52 } 53 }; 54 JRootPane rootPane = new JRootPane(); 55 KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); 56 rootPane.registerKeyboardAction(escapeActionListener, stroke, JComponent.WHEN_IN_FOCUSED_WINDOW); 57 57 58 return rootPane;59 }58 return rootPane; 59 } 60 60 } 61 61
Note:
See TracChangeset
for help on using the changeset viewer.
