Ignore:
Timestamp:
2010-12-12T11:34:30+01:00 (14 years ago)
Author:
bastik
Message:

'applied #j5729 (patch by robome) - make terracing with existing address nodes easier'

File:
1 edited

Legend:

Unmodified
Added
Removed
  • applications/editors/josm/plugins/terracer/src/terracer/TerracerAction.java

    r24697 r24713  
    1616import java.util.Collection;
    1717import java.util.Collections;
     18import java.util.Comparator;
     19import java.util.Iterator;
    1820import java.util.LinkedList;
    1921import java.util.List;
     22import java.util.Map;
     23import java.util.Set;
     24import java.util.Map.Entry;
     25import java.util.regex.Matcher;
     26import java.util.regex.Pattern;
    2027
    2128import javax.swing.JOptionPane;
     
    2936import org.openstreetmap.josm.command.DeleteCommand;
    3037import org.openstreetmap.josm.command.SequenceCommand;
     38import org.openstreetmap.josm.data.osm.DataSet;
    3139import org.openstreetmap.josm.data.osm.Node;
    3240import org.openstreetmap.josm.data.osm.OsmPrimitive;
     
    7684        Way outline = null;
    7785        Way street = null;
     86        String streetname = null;
     87        ArrayList<Node> housenumbers = new ArrayList<Node>();
    7888        Node init = null;
    7989
     
    8292                super(message);
    8393            }
     94
    8495            InvalidUserInputException() {
    8596                super();
     
    8899
    89100        try {
    90             for (OsmPrimitive osm : sel) {
    91                 if (osm instanceof Node) {
    92                     if (init != null)
     101            if (sel.size() == 1) {
     102                OsmPrimitive prim = sel.iterator().next();
     103
     104                if (!(prim instanceof Way))
     105                    throw new InvalidUserInputException();
     106
     107                outline = (Way) prim;
     108            } else if (sel.size() > 1) {
     109                List<Way> ways = OsmPrimitive.getFilteredList(sel, Way.class);
     110                Iterator<Way> wit = ways.iterator();
     111                while (wit.hasNext()) {
     112                    Way way = wit.next();
     113                    if (way.hasKey("building")) {
     114                        if (outline != null)
     115                            // already have a building
     116                            throw new InvalidUserInputException();
     117                        outline = way;
     118                    } else if (way.hasKey("highway")) {
     119                        if (street != null)
     120                            // already have a street
     121                            throw new InvalidUserInputException();
     122                        street = way;
     123
     124                        if ((streetname = street.get("name")) == null)
     125                            throw new InvalidUserInputException();
     126                    } else
    93127                        throw new InvalidUserInputException();
    94                     init = (Node) osm;
    95                 } else if (osm instanceof Way) {
    96                     if (osm.hasKey("highway")) {
    97                         if (street != null)
     128                }
     129
     130                if (outline == null)
     131                    throw new InvalidUserInputException();
     132
     133                List<Node> nodes = OsmPrimitive.getFilteredList(sel, Node.class);
     134                Iterator<Node> nit = nodes.iterator();
     135                // Actually this should test if the selected address nodes lie
     136                // within the selected outline. Any ideas how to do this?
     137                while (nit.hasNext()) {
     138                    Node node = nit.next();
     139                    if (node.hasKey("addr:housenumber")) {
     140                        String nodesstreetname = node.get("addr:street");
     141                        // if a node has a street name if must be equal
     142                        // to the one of the other address nodes
     143                        if (nodesstreetname != null) {
     144                            if (streetname == null)
     145                                streetname = nodesstreetname;
     146                            else if (!nodesstreetname.equals(streetname))
     147                                throw new InvalidUserInputException();
     148                        }
     149
     150                        housenumbers.add(node);
     151                    } else {
     152                        // A given node might not be an address node but then
     153                        // it has to be part of the building to help getting
     154                        // the number direction right.
     155                        if (!outline.containsNode(node) || init != null)
    98156                            throw new InvalidUserInputException();
    99                         street = (Way) osm;
    100                         if (!street.hasKey("name"))
    101                             throw new InvalidUserInputException();
    102                     } else {
    103                         if (outline != null)
    104                             throw new InvalidUserInputException();
    105                         outline = (Way) osm;
     157                        init = node;
    106158                    }
    107159                }
    108             }
    109             if (outline == null)
    110                 throw new InvalidUserInputException();
    111            
    112             if (init != null && !init.getReferrers().contains(outline))
    113                 throw new InvalidUserInputException();
    114 
    115             if (outline.getNodesCount() < 5)
    116                 throw new InvalidUserInputException();
    117 
    118             if (!outline.isClosed())
     160
     161                Collections.sort(housenumbers, new HousenumberNodeComparator());
     162            }
     163
     164            if (outline == null || !outline.isClosed() || outline.getNodesCount() < 5)
    119165                throw new InvalidUserInputException();
    120166        } catch (InvalidUserInputException ex) {
     
    128174        }
    129175
    130         // If we have a street, try to find a associatedStreet relation that could be reused.
     176        // If we have a street, try to find an associatedStreet relation that could be reused.
    131177        Relation associatedStreet = null;
    132178        if (street != null) {
     
    146192        }
    147193
    148         String title = trn("Change {0} object", "Change {0} objects", sel.size(), sel.size());
    149         // show input dialog.
    150         new HouseNumberInputHandler(this, outline, init, street, associatedStreet, title);
     194        if (housenumbers.size() == 1) {
     195            // Special case of one outline and one address node.
     196            // Don't open the dialogue, just copy the node keys
     197            // to the outline, set building just in case it isn't there
     198            // and remove the node.
     199            Collection<Command> commands = new LinkedList<Command>();
     200            Way newOutline = new Way(outline);
     201            for (Entry<String, String> entry : housenumbers.get(0).getKeys()
     202                    .entrySet()) {
     203                newOutline.put(entry.getKey(), entry.getValue());
     204            }
     205            newOutline.put("building", "yes");
     206            commands.add(new ChangeCommand(outline, newOutline));
     207            commands.add(DeleteCommand.delete(Main.main.getEditLayer(),
     208                    housenumbers, true, true));
     209            Main.main.undoRedo
     210                    .add(new SequenceCommand(tr("Terrace"), commands));
     211            Main.main.getCurrentDataSet().setSelected(newOutline);
     212        } else {
     213            String title = trn("Change {0} object", "Change {0} objects", sel
     214                    .size(), sel.size());
     215            // show input dialog.
     216            new HouseNumberInputHandler(this, outline, init, street, streetname,
     217                    associatedStreet, housenumbers, title);
     218        }
    151219    }
    152220
     
    160228
    161229    /**
     230     * Sorts the house number nodes according their numbers only
     231     *
     232     * @param house
     233     *            number nodes
     234     */
     235    class HousenumberNodeComparator implements Comparator<Node> {
     236        private final Pattern pat = Pattern.compile("^([0-9]+)");
     237
     238        /*
     239         * (non-Javadoc)
     240         *
     241         * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
     242         */
     243        @Override
     244        public int compare(Node node1, Node node2) {
     245            // It's necessary to strip off trailing non-numbers so we can
     246            // compare the numbers itself numerically since string comparison
     247            // doesn't work for numbers with different number of digits,
     248            // e.g. 9 is higher than 11
     249            String node1String = node1.get("addr:housenumber");
     250            String node2String = node2.get("addr:housenumber");
     251            Matcher mat = pat.matcher(node1String);
     252            if (mat.find()) {
     253                Integer node1Int = Integer.valueOf(mat.group(1));
     254                mat = pat.matcher(node2String);
     255                if (mat.find()) {
     256                    Integer node2Int = Integer.valueOf(mat.group(1));
     257
     258                    return node1Int.compareTo(node2Int);
     259                }
     260            }
     261
     262            return node1String.compareTo(node2String);
     263        }
     264    }
     265
     266    /**
    162267     * Terraces a single, closed, quadrilateral way.
    163268     *
     
    171276     * @param street The street, the buildings belong to (may be null)
    172277     * @param associatedStreet
    173      * @param From
    174      * @param To
    175      * @param streetName the name of a street (may be null). Used if not null and street is null.
    176      * @param handleRelations If the user likes to add a relation or extend an existing relation
    177      * @param deleteOutline If the outline way should be deleted, when done
     278     * @param segments The number of segments to generate
     279     * @param From Starting housenumber
     280     * @param To Ending housenumber
     281     * @param step The step width to use
     282     * @param housenumbers List of housenumbers to use. From and To are ignored
     283     *        if this is set.
     284     * @param streetName the name of the street, derived from the street line
     285     *        or the house numbers (may be null)
     286     * @param handleRelations If the user likes to add a relation or extend an
     287     *        existing relation
     288     * @param deleteOutline If the outline way should be deleted when done
    178289     */
    179290    public void terraceBuilding(Way outline,
     
    185296                String To,
    186297                int step,
     298                ArrayList<Node> housenumbers,
    187299                String streetName,
    188300                boolean handleRelations,
    189301                boolean deleteOutline) {
    190302        final int nb;
    191 
    192         Integer to, from;
    193         to = getNumber(To);
    194         from = getNumber(From);
    195         if (to != null && from != null) {
    196             nb = 1 + (to.intValue() - from.intValue()) / step;
    197         } else if (segments != null) {
    198             nb = segments.intValue();
     303        Integer to = null, from = null;
     304        if (housenumbers.isEmpty()) {
     305            to = getNumber(To);
     306            from = getNumber(From);
     307            if (to != null && from != null) {
     308                nb = 1 + (to.intValue() - from.intValue()) / step;
     309            } else if (segments != null) {
     310                nb = segments.intValue();
     311            } else {
     312                // if we get here, there is is a bug in the input validation.
     313                throw new TerracerRuntimeException(
     314                        "Could not determine segments from parameters, this is a bug. "
     315                                + "Parameters were: segments " + segments
     316                                + " from " + from + " to " + to + " step "
     317                                + step);
     318            }
    199319        } else {
    200             // if we get here, there is is a bug in the input validation.
    201             throw new TerracerRuntimeException(
    202                     "Could not determine segments from parameters, this is a bug. "
    203                             + "Parameters were: segments " + segments
    204                             + " from " + from + " to " + to + " step " + step);
    205         }
     320            nb = housenumbers.size();
     321        }
     322
     323        // now find which is the longest side connecting the first node
     324        Pair<Way, Way> interp = findFrontAndBack(outline);
     325
     326        boolean swap = false;
     327        if (init != null) {
     328            if (interp.a.lastNode().equals(init) || interp.b.lastNode().equals(init)) {
     329                swap = true;
     330            }
     331        }
     332
     333        final double frontLength = wayLength(interp.a);
     334        final double backLength = wayLength(interp.b);
    206335
    207336        // new nodes array to hold all intermediate nodes
     
    211340        Collection<Way> ways = new LinkedList<Way>();
    212341
    213         // Should this building be terraced (i.e. is there more then one section?)
    214342        if (nb > 1) {
    215             // create intermediate nodes by interpolating.
    216 
    217             // now find which is the longest side connecting the first node
    218             Pair<Way, Way> interp = findFrontAndBack(outline);
    219 
    220             boolean swap = false;
    221             if (init != null) {
    222                 if (interp.a.lastNode().equals(init) || interp.b.lastNode().equals(init)) {
    223                     swap = true;
    224                 }
    225             }
    226 
    227             final double frontLength = wayLength(interp.a);
    228             final double backLength = wayLength(interp.b);
    229 
    230343            for (int i = 0; i <= nb; ++i) {
    231344                int i_dir = swap ? nb - i : i;
     
    249362
    250363                String number = null;
    251                 if (from != null) {
    252                     number = Integer.toString(from + i * step);
    253                 }
    254                 terr = addressBuilding(terr, street, streetName, number);
     364                Set<Entry<String, String>> additionalKeys = null;
     365                if (housenumbers.isEmpty()) {
     366                    if (from != null) {
     367                        // only, if the user has specified house numbers
     368                        number = Integer.toString(from + i * step);
     369                    }
     370                } else {
     371                    number = housenumbers.get(i).get("addr:housenumber");
     372                    additionalKeys = housenumbers.get(i).getKeys().entrySet();
     373                }
     374
     375                terr = addressBuilding(terr, street, streetName, number,
     376                        additionalKeys);
    255377
    256378                ways.add(terr);
     
    262384            }
    263385        } else {
    264             // Single section, just add the address details
     386            // Single building, just add the address details
    265387            Way newOutline;
    266             newOutline = addressBuilding(outline, street, streetName, From);
     388            newOutline = addressBuilding(outline, street, streetName, From, null);
    267389            ways.add(newOutline);
    268390            this.commands.add(new ChangeCommand(outline, newOutline));
     
    283405                }
    284406                this.commands.add(new AddCommand(associatedStreet));
    285             }
    286             else { // relation exists already - add new members
     407            } else { // relation exists already - add new members
    287408                Relation newAssociatedStreet = new Relation(associatedStreet);
    288409                for (Way w : ways) {
     
    292413            }
    293414        }
     415
     416        // Remove the address node since their tags have been incorporated into
     417        // the terraces.
     418        // Or should removing them also be an option?
     419        if (!housenumbers.isEmpty())
     420            commands.add(DeleteCommand.delete(Main.main.getEditLayer(),
     421                    housenumbers, true, true));
     422
    294423        Main.main.undoRedo.add(new SequenceCommand(tr("Terrace"), commands));
    295424        if (nb > 1) {
     
    309438     * @param streetName the name of a street (may be null). Used if not null and street is null.
    310439     * @param number The house number
     440     * @param additionalKeys More keys to be copied onto the new outline
    311441     * @return the way with added address details
    312442     */
    313     private Way addressBuilding(Way outline, Way street, String streetName, String number) {
     443    private Way addressBuilding(Way outline, Way street, String streetName,
     444            String number, Set<Entry<String, String>> additionalKeys) {
    314445        Way changedOutline = outline;
    315446        if (number != null) {
    316447            // only, if the user has specified house numbers
    317448            this.commands.add(new ChangePropertyCommand(changedOutline, "addr:housenumber", number));
     449        }
     450        if (additionalKeys != null) {
     451            for (Entry<String, String> entry : additionalKeys) {
     452                this.commands.add(new ChangePropertyCommand(changedOutline,
     453                        entry.getKey(), entry.getValue()));
     454            }
    318455        }
    319456        changedOutline.put("building", "yes");
     
    343480            Pair<Node,Node> p = pairs.get(i);
    344481            final double seg_length = p.a.getCoor().greatCircleDistance(p.b.getCoor());
    345             if (l <= seg_length ||
    346                     i == pairs.size() - 1) {    // be generous on the last segment (numerical roudoff can lead to a small overshoot)
     482            if (l <= seg_length || i == pairs.size() - 1) {
     483                // be generous on the last segment (numerical roudoff can lead to a small overshoot)
    347484                return interpolateNode(p.a, p.b, l / seg_length);
    348485            } else {
     
    442579     */
    443580    private int positiveModulus(int a, int n) {
    444         if (n <=0)
     581        if (n <= 0)
    445582            throw new IllegalArgumentException();
    446583        int res = a % n;
Note: See TracChangeset for help on using the changeset viewer.