Ticket #6966: ticket-6966-improve-align-nodes-in-line-v1.patch

File ticket-6966-improve-align-nodes-in-line-v1.patch, 11.2 KB (added by olejorgenb, 13 years ago)
  • src/org/openstreetmap/josm/actions/AlignInLineAction.java

    diff --git a/src/org/openstreetmap/josm/actions/AlignInLineAction.java b/src/org/openstreetmap/josm/actions/AlignInLineAction.java
    index a8ff579..448c467 100644
    a b  
    11// License: GPL. Copyright 2007 by Immanuel Scholz and others
    22package org.openstreetmap.josm.actions;
    33
    4 import static org.openstreetmap.josm.tools.I18n.tr;
    54import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
     5import static org.openstreetmap.josm.tools.I18n.tr;
    66
    77import java.awt.event.ActionEvent;
    88import java.awt.event.KeyEvent;
     9import java.util.ArrayList;
    910import java.util.Collection;
    10 import java.util.LinkedList;
     11import java.util.List;
    1112
    1213import javax.swing.JOptionPane;
    1314
    public final class AlignInLineAction extends JosmAction {  
    3536        putValue("help", ht("/Action/AlignInLine"));
    3637    }
    3738
    38     /**
    39      * The general algorithm here is to find the two selected nodes
    40      * that are furthest apart, and then to align all other selected
    41      * nodes onto the straight line between these nodes.
    42      */
    43     public void actionPerformed(ActionEvent e) {
    44         if (!isEnabled())
    45             return;
    46         Collection<OsmPrimitive> sel = getCurrentDataSet().getSelected();
    47         Collection<Node> nodes = new LinkedList<Node>();
    48         Collection<Node> itnodes = new LinkedList<Node>();
    49         for (OsmPrimitive osm : sel)
    50             if (osm instanceof Node) {
    51                 nodes.add((Node)osm);
    52                 itnodes.add((Node)osm);
    53             }
    54         // special case if no single nodes are selected and exactly one way is:
    55         // then use the way's nodes
    56         if ((nodes.size() == 0) && (sel.size() == 1)) {
    57             for (OsmPrimitive osm : sel)
    58                 if (osm instanceof Way) {
    59                     nodes.addAll(((Way)osm).getNodes());
    60                     itnodes.addAll(((Way)osm).getNodes());
    61                 }
    62         }
    63         if (nodes.size() < 3) {
    64             JOptionPane.showMessageDialog(
    65                     Main.parent,
    66                     tr("Please select at least three nodes."),
    67                     tr("Information"),
    68                     JOptionPane.INFORMATION_MESSAGE
    69             );
    70             return;
    71         }
    72 
     39    // the joy of single return values only...
     40    private void nodePairFurthestApart(ArrayList<Node> nodes, Node[] resultOut) {
     41        if(resultOut.length < 2)
     42            throw new IllegalArgumentException();
    7343        // Find from the selected nodes two that are the furthest apart.
    7444        // Let's call them A and B.
    7545        double distance = 0;
    public final class AlignInLineAction extends JosmAction {  
    7747        Node nodea = null;
    7848        Node nodeb = null;
    7949
    80         for (Node n : nodes) {
    81             itnodes.remove(n);
    82             for (Node m : itnodes) {
     50        for (int i = 0; i < nodes.size()-1; i++) {
     51            Node n = nodes.get(i);
     52            for (int j = i+1; j < nodes.size(); j++) {
     53                Node m = nodes.get(j);
    8354                double dist = Math.sqrt(n.getEastNorth().distance(m.getEastNorth()));
    8455                if (dist > distance) {
    8556                    nodea = n;
    public final class AlignInLineAction extends JosmAction {  
    8859                }
    8960            }
    9061        }
     62        resultOut[0] = nodea;
     63        resultOut[1] = nodeb;
     64    }
     65
     66    private void showWarning() {
     67        JOptionPane.showMessageDialog(
     68                Main.parent,
     69                tr("Please select at least three nodes."),
     70                tr("Information"),
     71                JOptionPane.INFORMATION_MESSAGE
     72        );
     73        return;
     74    }
    9175
    92         // Remove the nodes A and B from the list of nodes to move
     76    private static int indexWrap(int size, int i) {
     77        i = i % size; // -2 % 5 = -2, -7 % 5 = -2, -5 % 5 = 0
     78        if (i < 0) {
     79            i = size + i;
     80        }
     81        return i;
     82    }
     83    // get the node in w at index i relative to refI
     84    private static Node getNodeRelative(Way w, int refI, int i) {
     85        int absI = indexWrap(w.getNodesCount(), refI + i);
     86        if(w.isClosed() && refI + i < 0) {
     87            absI--;  // node duplicated in closed ways
     88        }
     89        return w.getNode(absI);
     90    }
     91
     92    /**
     93     * The general algorithm here is to find the two selected nodes
     94     * that are furthest apart, and then to align all other selected
     95     * nodes onto the straight line between these nodes.
     96     */
     97
     98
     99    /**
     100     * Operation depends on the selected objects:
     101     */
     102    public void actionPerformed(ActionEvent e) {
     103        if (!isEnabled())
     104            return;
     105
     106        Node[] anchors = new Node[2]; // oh, java I love you so much..
     107
     108        List<Node> selectedNodes = new ArrayList<Node>(getCurrentDataSet().getSelectedNodes());
     109        Collection<Way> selectedWays = getCurrentDataSet().getSelectedWays();
     110        ArrayList<Node> nodes = new ArrayList<Node>();
     111
     112        //// Decide what to align based on selection:
     113
     114        /// Only ways selected -> Align their nodes.
     115        if ((selectedNodes.size() == 0) && (selectedWays.size() == 1)) { // TODO: handle multiple ways
     116            for (Way way : selectedWays) {
     117                nodes.addAll(way.getNodes());
     118            }
     119            // use the nodes furthest apart as anchors
     120            nodePairFurthestApart(nodes, anchors);
     121        }
     122        /// More than 3 nodes selected -> align those nodes
     123        else if(selectedNodes.size() >= 3) {
     124            nodes.addAll(selectedNodes);
     125            // use the nodes furthest apart as anchors
     126            nodePairFurthestApart(nodes, anchors);
     127        }
     128        /// One node selected -> align that node to the relevant neighbors
     129        else if (selectedNodes.size() == 1) {
     130            Node n = selectedNodes.iterator().next();
     131
     132            Way w = null;
     133            if(selectedWays.size() == 1) {
     134                w = selectedWays.iterator().next();
     135                if(w.containsNode(n) == false)
     136                    // warning
     137                    return;
     138            } else if(n.isReferredByWays(2) == false) { // node used in only one way
     139                w = OsmPrimitive.getFilteredList(n.getReferrers(), Way.class).iterator().next();
     140            }
     141            if (w == null || w.getNodesCount() < 3)
     142                // warning, need at least 3 nodes
     143                return;
     144
     145            // Find anchors
     146            int nodeI = w.getNodes().indexOf(n);
     147            // End-node in non-circular way selected: align this node with the two neighbors.
     148            if ((nodeI == 0 || nodeI == w.getNodesCount()-1) && !w.isClosed()) {
     149                int direction = nodeI == 0 ? 1 : -1;
     150                anchors[0] = w.getNode(nodeI + direction);
     151                anchors[1] = w.getNode(nodeI + direction*2);
     152            } else {
     153                // o---O---o
     154                anchors[0] = getNodeRelative(w, nodeI, 1);
     155                anchors[1] = getNodeRelative(w, nodeI, -1);
     156            }
     157            nodes.add(n);
     158        }
     159
     160        if (anchors[0] == null || anchors[1] == null) {
     161            showWarning();
     162            return;
     163        }
     164
     165
     166        Collection<Command> cmds = new ArrayList<Command>(nodes.size());
     167
     168        createAlignNodesCommands(anchors, nodes, cmds);
     169
     170        // Do it!
     171        Main.main.undoRedo.add(new SequenceCommand(tr("Align Nodes in Line"), cmds));
     172        Main.map.repaint();
     173    }
     174
     175    private void createAlignNodesCommands(Node[] anchors, Collection<Node> nodes, Collection<Command> cmds) {
     176        Node nodea = anchors[0];
     177        Node nodeb = anchors[1];
     178
     179        // The anchors are aligned per definition
    93180        nodes.remove(nodea);
    94181        nodes.remove(nodeb);
    95182
    public final class AlignInLineAction extends JosmAction {  
    99186        double bx = nodeb.getEastNorth().east();
    100187        double by = nodeb.getEastNorth().north();
    101188
    102         // A list of commands to do
    103         Collection<Command> cmds = new LinkedList<Command>();
    104 
    105189        // OK, for each node to move, work out where to move it!
    106190        for (Node n : nodes) {
    107191            // Get existing co-ords of node to move
    public final class AlignInLineAction extends JosmAction {  
    124208                nx = (c2 - c1) / (m1 - m2);
    125209                ny = (m1 * nx) + c1;
    126210            }
    127 
     211            double newX = nx - n.getEastNorth().east();
     212            double newY = ny - n.getEastNorth().north();
    128213            // Add the command to move the node to its new position.
    129             cmds.add(new MoveCommand(n, nx - n.getEastNorth().east(), ny - n.getEastNorth().north() ));
     214            cmds.add(new MoveCommand(n, newX, newY));
    130215        }
    131 
    132         // Do it!
    133         Main.main.undoRedo.add(new SequenceCommand(tr("Align Nodes in Line"), cmds));
    134         Main.map.repaint();
    135216    }
    136217
    137218    @Override
  • src/org/openstreetmap/josm/actions/JoinNodeWayAction.java

    diff --git a/src/org/openstreetmap/josm/actions/JoinNodeWayAction.java b/src/org/openstreetmap/josm/actions/JoinNodeWayAction.java
    index afa7c9f..468c33f 100644
    a b public class JoinNodeWayAction extends JosmAction {  
    3535    public void actionPerformed(ActionEvent e) {
    3636        if (!isEnabled())
    3737            return;
    38         Collection<OsmPrimitive> sel = getCurrentDataSet().getSelected();
    39         if (sel.size() < 1) return;
     38        Collection<Node> selectedNodes = getCurrentDataSet().getSelectedNodes();
     39        // Allow multiple selected nodes too?
     40        if (selectedNodes.size() != 1) return;
     41
     42        Node node = selectedNodes.iterator().next();
    4043
    4144        Collection<Command> cmds = new LinkedList<Command>();
    4245
    43         for (OsmPrimitive osm : sel) {
    44             if (!(osm instanceof Node)) {
    45                 continue;
    46             }
    47             Node node = (Node) osm;
     46        // If the user has selected some ways, only join the node to these.
     47        boolean restrictToSelectedWays =
     48            getCurrentDataSet().getSelectedWays().size() > 0;
    4849
    4950            List<WaySegment> wss = Main.map.mapView.getNearestWaySegments(
    5051                    Main.map.mapView.getPoint(node), OsmPrimitive.isSelectablePredicate);
    5152            HashMap<Way, List<Integer>> insertPoints = new HashMap<Way, List<Integer>>();
    5253            for (WaySegment ws : wss) {
     54                // Maybe cleaner to pass a "isSelected" predicate to getNearestWaySegements, but this is atm. less invasive.
     55                if(restrictToSelectedWays && !ws.way.isSelected()) {
     56                    continue;
     57                }
     58
    5359                List<Integer> is;
    5460                if (insertPoints.containsKey(ws.way)) {
    5561                    is = insertPoints.get(ws.way);
    public class JoinNodeWayAction extends JosmAction {  
    8086                wnew.setNodes(nodesToAdd);
    8187                cmds.add(new ChangeCommand(w, wnew));
    8288            }
    83         }
    84         if (cmds.size() == 0) return;
    85         Main.main.undoRedo.add(new SequenceCommand(tr("Join Node and Line"), cmds));
    86         Main.map.repaint();
     89            if (cmds.size() == 0) return;
     90            Main.main.undoRedo.add(new SequenceCommand(tr("Join Node and Line"), cmds));
     91            Main.map.repaint();
    8792    }
    8893
    8994    private static void pruneSuccsAndReverse(List<Integer> is) {