Changeset 7850 in josm for trunk/src/org/openstreetmap


Ignore:
Timestamp:
2014-12-20T03:37:42+01:00 (9 years ago)
Author:
Don-vip
Message:

fix #9605, fix #10050 - align nodes in line moves outer instead of inner node (patch by oligo)

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/actions/AlignInLineAction.java

    r7025 r7850  
    1212import java.util.HashSet;
    1313import java.util.List;
     14import java.util.Map;
     15import java.util.Set;
    1416
    1517import javax.swing.JOptionPane;
     
    2729
    2830/**
    29  * Aligns all selected nodes into a straight line (useful for
    30  * roads that should be straight, but have side roads and
     31 * Aligns all selected nodes into a straight line (useful for roads that should be straight, but have side roads and
    3132 * therefore need multiple nodes)
    3233 *
    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.
     34 * <pre>
     35 * Case 1: 1 or 2 ways selected and no nodes selected: align nodes of ways taking care of intersection.
     36 * Case 2: Single node selected and no ways selected: align this node relative to all referrer ways (2 at most).
     37 * Case 3: Single node and ways selected: align this node relative to selected ways.
     38 * Case 4.1: Only nodes selected, part of a non-closed way: align these nodes on the line passing through the
     39 *   extremity nodes (most distant in the way sequence). See https://josm.openstreetmap.de/ticket/9605#comment:3
     40 * Case 4.2: Only nodes selected, part of a closed way: align these nodes on the line passing through the most distant
     41 *   nodes.
     42 * Case 4.3: Only nodes selected, part of multiple ways: align these nodes on the line passing through the most distant
     43 *   nodes.
     44 * </pre>
    3745 *
    3846 * @author Matthew Newton
     
    7179
    7280    /**
    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
    78      */
    79     private void nodePairFurthestApart(List<Node> nodes, Node[] resultOut) {
    80         if(resultOut.length < 2)
    81             throw new IllegalArgumentException();
    82 
     81     * Return 2 nodes making up the line along which provided nodes must be aligned.
     82     *
     83     * @param nodes Nodes to be aligned.
     84     * @return A array of two nodes.
     85     */
     86    private Node[] nodePairFurthestApart(List<Node> nodes) {
    8387        Node nodea = null;
    8488        Node nodeb = null;
    8589
    86         // Intersection of all ways referred by each node
    87         HashSet<Way> waysRef = null;
     90        // Detect if selected nodes are on the same way.
     91
     92        // Get ways passing though all selected nodes.
     93        Set<Way> waysRef = null;
    8894        for(Node n: nodes) {
    8995            Collection<Way> ref = OsmPrimitive.getFilteredList(n.getReferrers(), Way.class);
     
    9399                waysRef.retainAll(ref);
    94100        }
    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;
     101
     102        // Nodes belongs to multiple ways, return most distant nodes.
     103        if (waysRef.size() != 1)
     104            return nodeFurthestAppart(nodes);
     105
     106        // All nodes are part of the same way. See #9605.
     107        Way way = waysRef.iterator().next();
     108
     109        if (way.isClosed()) {
     110            // Align these nodes on the line passing through the most distant nodes.
     111            return nodeFurthestAppart(nodes);
     112        }
     113
     114        // The way is open, align nodes on the line passing through the extremity nodes (most distant in the way
     115        // sequence). See #9605#comment:3.
     116        Set<Node> remainNodes = new HashSet<>(nodes);
     117        for (Node n : way.getNodes()) {
     118            if (!remainNodes.contains(n))
     119                continue;
     120            if (nodea == null)
     121                nodea = n;
     122            if (remainNodes.size() == 1) {
     123                nodeb = remainNodes.iterator().next();
     124                break;
     125            }
     126            remainNodes.remove(n);
     127        }
     128
     129        return new Node[] { nodea, nodeb };
     130    }
     131
     132    /**
     133     * Return the two nodes the most distant from the provided list.
     134     *
     135     * @param nodes List of nodes to analyze.
     136     * @return An array containing the two most distant nodes.
     137     */
     138    private Node[] nodeFurthestAppart(List<Node> nodes) {
     139        Node node1 = null, node2 = null;
     140        double minSqDistance = 0;
     141        int nb;
     142
     143        nb = nodes.size();
     144        for (int i = 0; i < nb - 1; i++) {
     145            Node n = nodes.get(i);
     146            for (int j = i + 1; j < nb; j++) {
     147                Node m = nodes.get(j);
     148                double sqDist = n.getEastNorth().distanceSq(m.getEastNorth());
     149                if (sqDist > minSqDistance) {
     150                    node1 = n;
     151                    node2 = m;
     152                    minSqDistance = sqDist;
    105153                }
    106                 remainNodes.remove(n);
    107             }
    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                     }
    122                 }
    123             }
    124         }
    125         resultOut[0] = nodea;
    126         resultOut[1] = nodeb;
     154            }
     155        }
     156
     157        return new Node[] { node1, node2 };
    127158    }
    128159
     
    161192                cmd = alignSingleNode(selectedNodes.get(0), lines);
    162193            }
    163             /// More than 3 nodes selected -> align those nodes
     194            // More than 3 nodes and way(s) selected -> align selected nodes. Don't care of way(s).
    164195            else if(selectedNodes.size() >= 3) {
    165196                cmd = alignOnlyNodes(selectedNodes);
     
    182213
    183214    /**
    184      * Align nodes in case that only nodes are selected
     215     * Align nodes in case 3 or more nodes are selected.
    185216     *
    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
     217     * @param nodes Nodes to be aligned.
     218     * @return Command that perform action.
     219     * @throws InvalidSelection If the nodes have same coordinates.
    193220     */
    194221    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);
     222        // Choose nodes used as anchor points for projection.
     223        Node[] anchors = nodePairFurthestApart(nodes);
    198224        Collection<Command> cmds = new ArrayList<>(nodes.size());
    199225        Line line = new Line(anchors[0], anchors[1]);
     
    212238    private Command alignMultiWay(Collection<Way> ways) throws InvalidSelection {
    213239        // Collect all nodes and compute line equation
    214         HashSet<Node> nodes = new HashSet<>();
    215         HashMap<Way, Line> lines = new HashMap<>();
     240        Set<Node> nodes = new HashSet<>();
     241        Map<Way, Line> lines = new HashMap<>();
    216242        for(Way w: ways) {
    217243            if(w.firstNode() == w.lastNode())
     
    250276     */
    251277    private List<Line> getInvolvedLines(Node node, List<Way> refWays) throws InvalidSelection {
    252         ArrayList<Line> lines = new ArrayList<>();
    253         ArrayList<Node> neighbors = new ArrayList<>();
     278        List<Line> lines = new ArrayList<>();
     279        List<Node> neighbors = new ArrayList<>();
    254280        for(Way way: refWays) {
    255281            List<Node> nodes = way.getNodes();
Note: See TracChangeset for help on using the changeset viewer.