Ticket #9605: bug9605_2.patch

File bug9605_2.patch, 8.0 KB (added by oligo, 9 years ago)
  • src/org/openstreetmap/josm/actions/AlignInLineAction.java

    ### Eclipse Workspace Patch 1.0
    #P JOSM
     
    1111import java.util.HashMap;
    1212import java.util.HashSet;
    1313import java.util.List;
     14import java.util.Set;
    1415
    1516import javax.swing.JOptionPane;
    1617
     
    2627import org.openstreetmap.josm.tools.Shortcut;
    2728
    2829/**
    29  * Aligns all selected nodes into a straight line (useful for
    30  * roads that should be straight, but have side roads and
     30 * Aligns all selected nodes into a straight line (useful for roads that should be straight, but have side roads and
    3131 * therefore need multiple nodes)
    3232 *
    33  * Case 1: Only ways selected, align each ways taking care of intersection.
    34  * Case 2: Single node selected, align this node relative to the surrounding nodes.
    35  * Case 3: Single node and ways selected, align this node relative to the surrounding nodes only parts of selected ways.
    36  * Case 4: Only nodes selected, align these nodes respect to the line passing through the most distant nodes.
     33 * <pre>
     34 * Case 1: 1 or 2 ways selected and no nodes selected: align nodes of ways taking care of intersection.
     35 * Case 2: Single node selected and no ways selected: align this node relative to all referrer ways (2 at most).
     36 * Case 3: Single node and ways selected: align this node relative to selected ways.
     37 * Case 4.1: Only nodes selected, part of a non-closed way: align these nodes on the line passing through the
     38 *   extremity nodes (most distant in the way sequence). See https://josm.openstreetmap.de/ticket/9605#comment:3
     39 * Case 4.2: Only nodes selected, part of a closed way: align these nodes on the line passing through the most distant
     40 *   nodes.
     41 * Case 4.3: Only nodes selected, part of multiple ways: align these nodes on the line passing through the most distant
     42 *   nodes.
     43 * </pre>
    3744 *
    3845 * @author Matthew Newton
    3946 */
     
    7077    }
    7178
    7279    /**
    73      * Compute 2 anchor points to align a set of nodes.
    74      * If all nodes are part of a same way anchor points are choose farthest relative to this way,
    75      * else choose farthest nodes.
    76      * @param nodes Nodes to be aligned
    77      * @param resultOut Array of size >= 2
     80     * Return 2 nodes making up the line along which provided nodes must be aligned.
     81     *
     82     * @param nodes Nodes to be aligned.
     83     * @return A array of two nodes.
    7884     */
    79     private void nodePairFurthestApart(List<Node> nodes, Node[] resultOut) {
    80         if(resultOut.length < 2)
    81             throw new IllegalArgumentException();
    82 
     85    private Node[] nodePairFurthestApart(List<Node> nodes) {
    8386        Node nodea = null;
    8487        Node nodeb = null;
    8588
    86         // Intersection of all ways referred by each node
    87         HashSet<Way> waysRef = null;
     89        // Detect if selected nodes are on the same way.
     90
     91        // Get ways passing though all selected nodes.
     92        Set<Way> waysRef = null;
    8893        for(Node n: nodes) {
    8994            Collection<Way> ref = OsmPrimitive.getFilteredList(n.getReferrers(), Way.class);
    9095            if(waysRef == null)
     
    9297            else
    9398                waysRef.retainAll(ref);
    9499        }
    95         if(waysRef.size() == 1) {
    96             // All nodes are part of the same way. See #9605
    97             HashSet<Node> remainNodes = new HashSet<>(nodes);
    98             Way way = waysRef.iterator().next();
    99             for(Node n: way.getNodes()) {
    100                 if(!remainNodes.contains(n)) continue;
    101                 if(nodea == null) nodea = n;
    102                 if(remainNodes.size() == 1) {
    103                     nodeb = remainNodes.iterator().next();
    104                     break;
    105                 }
    106                 remainNodes.remove(n);
     100
     101        // Nodes belongs to multiple ways, return most distant nodes.
     102        if (waysRef.size() != 1)
     103            return nodeFurthestAppart(nodes);
     104
     105        // All nodes are part of the same way. See #9605.
     106        Way way = waysRef.iterator().next();
     107
     108        if (way.isClosed()) {
     109            // Align these nodes on the line passing through the most distant nodes.
     110            return nodeFurthestAppart(nodes);
     111        }
     112
     113        // The way is open, align nodes on the line passing through the extremity nodes (most distant in the way
     114        // sequence). See #9605#comment:3.
     115        Set<Node> remainNodes = new HashSet<>(nodes);
     116        for (Node n : way.getNodes()) {
     117            if (!remainNodes.contains(n))
     118                continue;
     119            if (nodea == null)
     120                nodea = n;
     121            if (remainNodes.size() == 1) {
     122                nodeb = remainNodes.iterator().next();
     123                break;
    107124            }
    108         } else {
    109             // Find from the selected nodes two that are the furthest apart.
    110             // Let's call them A and B.
    111             double distance = 0;
    112             for (int i = 0; i < nodes.size()-1; i++) {
    113                 Node n = nodes.get(i);
    114                 for (int j = i+1; j < nodes.size(); j++) {
    115                     Node m = nodes.get(j);
    116                     double dist = Math.sqrt(n.getEastNorth().distance(m.getEastNorth()));
    117                     if (dist > distance) {
    118                         nodea = n;
    119                         nodeb = m;
    120                         distance = dist;
    121                     }
     125            remainNodes.remove(n);
     126        }
     127
     128        return new Node[] { nodea, nodeb };
     129    }
     130
     131    /**
     132     * Return the two nodes the most distant from the provided list.
     133     *
     134     * @param nodes List of nodes to analyze.
     135     * @return An array containing the two most distant nodes.
     136     */
     137    private Node[] nodeFurthestAppart(List<Node> nodes) {
     138        Node node1 = null, node2 = null;
     139        double minSqDistance = 0;
     140        int nb;
     141
     142        nb = nodes.size();
     143        for (int i = 0; i < nb - 1; i++) {
     144            Node n = nodes.get(i);
     145            for (int j = i + 1; j < nb; j++) {
     146                Node m = nodes.get(j);
     147                double sqDist = n.getEastNorth().distanceSq(m.getEastNorth());
     148                if (sqDist > minSqDistance) {
     149                    node1 = n;
     150                    node2 = m;
     151                    minSqDistance = sqDist;
    122152                }
    123153            }
    124154        }
    125         resultOut[0] = nodea;
    126         resultOut[1] = nodeb;
     155
     156        return new Node[] { node1, node2 };
    127157    }
    128158
    129159    /**
     
    160190                    throw new InvalidSelection();
    161191                cmd = alignSingleNode(selectedNodes.get(0), lines);
    162192            }
    163             /// More than 3 nodes selected -> align those nodes
     193            // More than 3 nodes and way(s) selected -> align selected nodes. Don't care of way(s).
    164194            else if(selectedNodes.size() >= 3) {
    165195                cmd = alignOnlyNodes(selectedNodes);
    166196            }
     
    181211    }
    182212
    183213    /**
    184      * Align nodes in case that only nodes are selected
    185      *
    186      * The general algorithm here is to find the two selected nodes
    187      * that are furthest apart, and then to align all other selected
    188      * nodes onto the straight line between these nodes.
    189 
    190      * @param nodes Nodes to be aligned
    191      * @return Command that perform action
    192      * @throws InvalidSelection
     214     * Align nodes in case 3 or more nodes are selected.
     215     *
     216     * @param nodes Nodes to be aligned.
     217     * @return Command that perform action.
     218     * @throws InvalidSelection If the nodes have same coordinates.
    193219     */
    194220    private Command alignOnlyNodes(List<Node> nodes) throws InvalidSelection {
    195         Node[] anchors = new Node[2]; // oh, java I love you so much..
    196         // use the nodes furthest apart as anchors
    197         nodePairFurthestApart(nodes, anchors);
     221        Node[] anchors;
     222
     223        // Choose nodes used as anchor points for projection.
     224        anchors = nodePairFurthestApart(nodes);
    198225        Collection<Command> cmds = new ArrayList<>(nodes.size());
    199226        Line line = new Line(anchors[0], anchors[1]);
    200227        for(Node node: nodes)