Ticket #22799: josm22799_smart_distribute.patch

File josm22799_smart_distribute.patch, 4.9 KB (added by gaben, 3 years ago)

unit test needed

  • src/org/openstreetmap/josm/actions/DistributeAction.java

     
    66
    77import java.awt.event.ActionEvent;
    88import java.awt.event.KeyEvent;
     9import java.util.ArrayList;
    910import java.util.Collection;
    1011import java.util.HashSet;
    1112import java.util.Iterator;
     
    4950     * Select method according to user selection.
    5051     * Case 1: One Way (no self-crossing) and at most 2 nodes contains by this way:
    5152     *     Distribute nodes keeping order along the way
    52      * Case 2: Other
     53     * Case 2: One Node part of at least one way, not a start or end node
     54     *     Distribute the selected node relative to neighbors
     55     * Case 3: Other
    5356     *     Distribute nodes
    5457     */
    5558    @Override
     
    5962
    6063        // Collect user selected objects
    6164        Collection<OsmPrimitive> selected = getLayerManager().getEditDataSet().getSelected();
    62         Collection<Way> ways = new LinkedList<>();
    63         Collection<Node> nodes = new HashSet<>();
     65        Collection<Way> ways = new ArrayList<>();
     66        Collection<Node> nodes = new ArrayList<>();
    6467        for (OsmPrimitive osm : selected) {
    6568            if (osm instanceof Node) {
    6669                nodes.add((Node) osm);
     
    8184            cmds = distributeWay(ways, nodes);
    8285        } else if (checkDistributeNodes(ways, nodes)) {
    8386            cmds = distributeNodes(nodes);
     87        } else if (checkDistributeNode(nodes)){
     88            cmds = distributeNode(nodes);
    8489        } else {
    8590            new Notification(
    86                              tr("Please select :\n" +
     91                             tr("Please select:\n" +
    8792                                "* One no self-crossing way with at most two of its nodes;\n" +
    88                                 "* Three nodes."))
     93                                "* One node in the middle of a way;\n" +
     94                                "* A way with at least three nodes;\n" +
     95                                "* Three nodes"))
    8996                .setIcon(JOptionPane.INFORMATION_MESSAGE)
    9097                .setDuration(Notification.TIME_SHORT)
    9198                .show();
     
    185192    }
    186193
    187194    /**
     195     * Test if single node oriented algorithm applies to the selection.
     196     * @param nodes The selected node. Collection type and naming kept for compatibility with similar methods.
     197     * @return true in this case
     198     */
     199    private static boolean checkDistributeNode(Collection<Node> nodes) {
     200        if (nodes.size() == 1) {
     201            Node node = nodes.iterator().next();
     202            int goodWays = 0;
     203            for (Way way : node.getParentWays()) {
     204                // the algorithm is applicable only if there is one way which:
     205                //  - is open and the selected node is a middle node, or
     206                //  - is closed (and has at least 4 nodes as 3 doesn't make sense and error-prone)
     207                if (!way.isFirstLastNode(node) || (way.isClosed() && way.getRealNodesCount() > 3))
     208                    goodWays++;
     209            }
     210            return goodWays == 1;
     211        }
     212        return false;
     213    }
     214
     215    /**
     216     * Distribute a single node relative to way neighbours.
     217     * @see DistributeAction#distributeNodes(Collection)
     218     * @param nodes nodes to distribute
     219     * @return Commands to execute to perform action
     220     */
     221    private static Collection<Command> distributeNode(Collection<Node> nodes) {
     222        final Node node = nodes.iterator().next();
     223        Way parent = node.getParentWays().iterator().next();
     224
     225        // make the user selected node the middle in the output variable
     226        nodes.clear();
     227        List<Node> neighbours = new ArrayList<>(4);
     228        neighbours.addAll(parent.getNeighbours(node));
     229
     230        nodes.add(neighbours.get(0));
     231        nodes.add(node);
     232        nodes.add(neighbours.get(1));
     233
     234        // call the distribution method with 3 nodes
     235        return distributeNodes(nodes);
     236    }
     237
     238    /**
    188239     * Test if nodes oriented algorithm applies to the selection.
    189240     * @param ways Selected ways
    190241     * @param nodes Selected nodes
     
    197248    /**
    198249     * Distribute nodes when only nodes are selected.
    199250     * The general algorithm here is to find the two selected nodes
    200      * that are furthest apart, and then to distribute all other selected
     251     * that are the furthest apart, and then to distribute all other selected
    201252     * nodes along the straight line between these nodes.
    202253     * @param nodes nodes to distribute
    203254     * @return Commands to execute to perform action
     
    241292        // A list of commands to do
    242293        Collection<Command> cmds = new LinkedList<>();
    243294
    244         // Amount of nodes between A and B plus 1
     295        // Number of nodes between A and B plus 1
    245296        int num = nodes.size()+1;
    246297
    247298        // Current number of node