Index: src/org/openstreetmap/josm/actions/AlignInLineAction.java
===================================================================
--- src/org/openstreetmap/josm/actions/AlignInLineAction.java	(révision 6950)
+++ src/org/openstreetmap/josm/actions/AlignInLineAction.java	(copie de travail)
@@ -28,6 +28,11 @@
  * Aligns all selected nodes into a straight line (useful for
  * roads that should be straight, but have side roads and
  * therefore need multiple nodes)
+ * 
+ * Case 1: Only ways selected, align each ways taking care of intersection.
+ * Case 2: Single node selected, align this node relative to the surrounding nodes.
+ * Case 3: Single node and ways selected, align this node relative to the surrounding nodes only parts of selected ways.
+ * Case 4: Only nodes selected, align these nodes respect to the line passing through the most distant nodes.
  *
  * @author Matthew Newton
  */
@@ -43,6 +48,27 @@
     }
 
     /**
+     * InvalidSelection exception has to be raised when action can't be perform
+     */
+    private class InvalidSelection extends Exception {
+
+        /**
+         * Create an InvalidSelection exception with default message
+         */
+        public InvalidSelection() {
+            super(tr("Please select at least three nodes."));
+        }
+
+        /**
+         * Create an InvalidSelection exception with specific message
+         * @param msg Message that will be display to the user
+         */
+        public InvalidSelection(String msg) {
+            super(msg);
+        }
+    }
+    
+    /**
      * Compute 2 anchor points to align a set of nodes.
      * If all nodes are part of a same way anchor points are choose farthest relative to this way,
      * else choose farthest nodes.
@@ -99,32 +125,6 @@
         resultOut[1] = nodeb;
     }
 
-    private void showWarning() {
-        showWarning(tr("Please select at least three nodes."));
-    }
-
-    private void showWarning(String msg) {
-        new Notification(msg)
-            .setIcon(JOptionPane.INFORMATION_MESSAGE)
-            .show();
-    }
-
-    private static int indexWrap(int size, int i) {
-        i = i % size; // -2 % 5 = -2, -7 % 5 = -2, -5 % 5 = 0
-        if (i < 0) {
-            i = size + i;
-        }
-        return i;
-    }
-    // get the node in w at index i relative to refI
-    private static Node getNodeRelative(Way w, int refI, int i) {
-        int absI = indexWrap(w.getNodesCount(), refI + i);
-        if(w.isClosed() && refI + i < 0) {
-            absI--;  // node duplicated in closed ways
-        }
-        return w.getNode(absI);
-    }
-
     /**
      * The general algorithm here is to find the two selected nodes
      * that are furthest apart, and then to align all other selected
@@ -140,131 +140,83 @@
         if (!isEnabled())
             return;
 
-        Node[] anchors = new Node[2]; // oh, java I love you so much..
-
         List<Node> selectedNodes = new ArrayList<Node>(getCurrentDataSet().getSelectedNodes());
-        Collection<Way> selectedWays = getCurrentDataSet().getSelectedWays();
-        List<Node> nodes = new ArrayList<Node>();
+        List<Way> selectedWays = new ArrayList<Way>(getCurrentDataSet().getSelectedWays());
 
-        //// Decide what to align based on selection:
+        try {
+            Command cmd = null;
+            //// Decide what to align based on selection:
 
-        /// Only ways selected -> For each way align their nodes taking care of intersection
-        if(selectedNodes.isEmpty() && !selectedWays.isEmpty()) {
-            alignMultiWay(selectedWays);
-            return;
-        }
-        /// More than 3 nodes selected -> align those nodes
-        else if(selectedNodes.size() >= 3) {
-            nodes.addAll(selectedNodes);
-            // use the nodes furthest apart as anchors
-            nodePairFurthestApart(nodes, anchors);
-        }
-        /// One node selected -> align that node to the relevant neighbors
-        else if (selectedNodes.size() == 1) {
-            Node n = selectedNodes.iterator().next();
-
-            Way w = null;
-            if(selectedWays.size() == 1) {
-                w = selectedWays.iterator().next();
-                if (!w.containsNode(n))
-                    // warning
-                    return;
-            } else {
-                List<Way> refWays = OsmPrimitive.getFilteredList(n.getReferrers(), Way.class);
-                if (refWays.size() == 1) { // node used in only one way
-                    w = refWays.iterator().next();
-                }
+            /// Only ways selected -> For each way align their nodes taking care of intersection
+            if(selectedNodes.isEmpty() && !selectedWays.isEmpty()) {
+                cmd = alignMultiWay(selectedWays);
             }
-            if (w == null || w.getNodesCount() < 3)
-                // warning, need at least 3 nodes
-                return;
-
-            // Find anchors
-            int nodeI = w.getNodes().indexOf(n);
-            // End-node in non-circular way selected: align this node with the two neighbors.
-            if ((nodeI == 0 || nodeI == w.getNodesCount()-1) && !w.isClosed()) {
-                int direction = nodeI == 0 ? 1 : -1;
-                anchors[0] = w.getNode(nodeI + direction);
-                anchors[1] = w.getNode(nodeI + direction*2);
-            } else {
-                // o---O---o
-                anchors[0] = getNodeRelative(w, nodeI, 1);
-                anchors[1] = getNodeRelative(w, nodeI, -1);
+            /// Only 1 node selected -> align this node relative to referers way
+            else if(selectedNodes.size() == 1) {
+                Node selectedNode = selectedNodes.get(0);
+                List<Way> involvedWays = null;
+                if(selectedWays.isEmpty())
+                    /// No selected way, all way containing this node are used
+                    involvedWays = OsmPrimitive.getFilteredList(selectedNode.getReferrers(), Way.class);
+                else
+                    /// Selected way, use only these ways
+                    involvedWays = selectedWays;
+                List<Line> lines = getInvolvedLines(selectedNode, involvedWays);
+                if(lines.size() > 2 || lines.isEmpty())
+                    throw new InvalidSelection();
+                cmd = alignSingleNode(selectedNodes.get(0), lines);
             }
-            nodes.add(n);
-        }
+            /// More than 3 nodes selected -> align those nodes
+            else if(selectedNodes.size() >= 3) {
+                cmd = alignOnlyNodes(selectedNodes);
+            }
+            /// All others cases are invalid
+            else {
+                throw new InvalidSelection();
+            }
 
-        if (anchors[0] == null || anchors[1] == null) {
-            showWarning();
-            return;
-        }
+            // Do it!
+            Main.main.undoRedo.add(cmd);
+            Main.map.repaint();
 
-
-        Collection<Command> cmds = new ArrayList<Command>(nodes.size());
-
-        createAlignNodesCommands(anchors, nodes, cmds);
-
-        // Do it!
-        Main.main.undoRedo.add(new SequenceCommand(tr("Align Nodes in Line"), cmds));
-        Main.map.repaint();
-    }
-
-    private void createAlignNodesCommands(Node[] anchors, Collection<Node> nodes, Collection<Command> cmds) {
-        Node nodea = anchors[0];
-        Node nodeb = anchors[1];
-
-        // The anchors are aligned per definition
-        nodes.remove(nodea);
-        nodes.remove(nodeb);
-
-        // Find out co-ords of A and B
-        double ax = nodea.getEastNorth().east();
-        double ay = nodea.getEastNorth().north();
-        double bx = nodeb.getEastNorth().east();
-        double by = nodeb.getEastNorth().north();
-
-        // OK, for each node to move, work out where to move it!
-        for (Node n : nodes) {
-            // Get existing co-ords of node to move
-            double nx = n.getEastNorth().east();
-            double ny = n.getEastNorth().north();
-
-            if (ax == bx) {
-                // Special case if AB is vertical...
-                nx = ax;
-            } else if (ay == by) {
-                // ...or horizontal
-                ny = ay;
-            } else {
-                // Otherwise calculate position by solving y=mx+c
-                double m1 = (by - ay) / (bx - ax);
-                double c1 = ay - (ax * m1);
-                double m2 = (-1) / m1;
-                double c2 = n.getEastNorth().north() - (n.getEastNorth().east() * m2);
-
-                nx = (c2 - c1) / (m1 - m2);
-                ny = (m1 * nx) + c1;
-            }
-            double newX = nx - n.getEastNorth().east();
-            double newY = ny - n.getEastNorth().north();
-            // Add the command to move the node to its new position.
-            cmds.add(new MoveCommand(n, newX, newY));
+        } catch (InvalidSelection except) {
+            new Notification(except.getMessage())
+                .setIcon(JOptionPane.INFORMATION_MESSAGE)
+                .show();
         }
     }
 
     /**
+     * Align nodes in case that only nodes are selected
+     * @param nodes Nodes to be aligned
+     * @return Command that perform action
+     * @throws InvalidSelection 
+     */
+    private Command alignOnlyNodes(List<Node> nodes) throws InvalidSelection {
+        Node[] anchors = new Node[2]; // oh, java I love you so much..
+        // use the nodes furthest apart as anchors
+        nodePairFurthestApart(nodes, anchors);
+        Collection<Command> cmds = new ArrayList<Command>(nodes.size());
+        Line line = new Line(anchors[0], anchors[1]);
+        for(Node node: nodes)
+            if(node != anchors[0] && node != anchors[1])
+                cmds.add(line.projectionCommand(node));
+        return new SequenceCommand(tr("Align Nodes in Line"), cmds);
+    }
+    
+    /**
      * Align way in case of multiple way #6819
      * @param ways Collection of way to align
+     * @return Command that perform action
+     * @throws InvalidSelection 
      */
-    private void alignMultiWay(Collection<Way> ways) {
+    private Command alignMultiWay(Collection<Way> ways) throws InvalidSelection {
         // Collect all nodes and compute line equation
         HashSet<Node> nodes = new HashSet<Node>();
         HashMap<Way, Line> lines = new HashMap<Way, Line>();
         for(Way w: ways) {
-            if(w.firstNode() == w.lastNode()) {
-                showWarning(tr("Can not align a polygon. Abort."));
-                return;
-            }
+            if(w.firstNode() == w.lastNode())
+                throw new InvalidSelection(tr("Can not align a polygon. Abort."));
             nodes.addAll(w.getNodes());
             lines.put(w, new Line(w));
         }
@@ -282,22 +234,48 @@
             }
             else if(referers.size() == 2) {
                 Command cmd = lines.get(referers.get(0)).intersectionCommand(n, lines.get(referers.get(1)));
-                if(cmd == null) {
-                    showWarning(tr("Two parallels ways found. Abort."));
-                    return;
-                }
                 cmds.add(cmd);
             }
-            else {
-                showWarning(tr("Intersection of three or more ways can not be solved. Abort."));
-                return;
-            }
+            else
+                throw new InvalidSelection(tr("Intersection of three or more ways can not be solved. Abort."));
         }
-        Main.main.undoRedo.add(new SequenceCommand(tr("Align Nodes in Line"), cmds));
-        Main.map.repaint();
+        return new SequenceCommand(tr("Align Nodes in Line"), cmds);
     }
 
     /**
+     * Get lines useful to do alignment of a single node
+     * @param node Node to be aligned
+     * @param refWays Ways where useful lines will be searched
+     * @return List of useful lines
+     * @throws InvalidSelection 
+     */
+    private List<Line> getInvolvedLines(Node node, List<Way> refWays) throws InvalidSelection {
+        ArrayList<Line> lines = new ArrayList<Line>();
+        for(Way way: refWays) {
+            List<Node> nodes = way.getNodes();
+            for(int i = 1; i < nodes.size()-1; i++)
+                if(nodes.get(i) == node)
+                    lines.add(new Line(nodes.get(i-1), nodes.get(i+1)));
+        }
+        return lines;
+    }
+
+    /**
+     * Align a single node relative to a set of lines #9081
+     * @param node Node to be aligned
+     * @param lines Lines to align node on
+     * @return Command that perform action
+     * @throws InvalidSelection 
+     */
+    private Command alignSingleNode(Node node, List<Line> lines) throws InvalidSelection {
+        if(lines.size() == 1)
+            return lines.get(0).projectionCommand(node);
+        else if(lines.size() == 2)
+            return lines.get(0).intersectionCommand(node,  lines.get(1));
+        throw new InvalidSelection();
+    }
+
+    /**
      * Class that describe a line
      */
     private class Line {
@@ -306,31 +284,40 @@
          * Line equation ax + by + c = 0
          * Such as a^2 + b^2 = 1, ie (-b, a) is a unit vector of line
          */
-        private double a, b, c; // Line equation ax+by+c=0
+        private double a, b, c;
         /**
-         * (xM, yM) are coordinate of a point of the line
+         * (x1, y1) (x2, y2) are coordinates of extremity, considering line as segment
          */
-        private double xM, yM; // Coordinate of a point of the line 
-
+        private double x1, y1, x2, y2;
         /**
-         * Init a line equation from a way.
-         * @param way
+         * Length of segment used to build this line
          */
-        public Line(Way way) {
-            xM = way.firstNode().getEastNorth().getX();
-            yM = way.firstNode().getEastNorth().getY();
-            double xB = way.lastNode().getEastNorth().getX();
-            double yB = way.lastNode().getEastNorth().getY();
-            a = yB - yM;
-            b = xM - xB;
-            double norm = Math.sqrt(a*a + b*b);
-            if (norm == 0) {
-                norm = 1;
-            }
+        private double norm;
+
+        public Line(Node first, Node last) throws InvalidSelection {
+            x1 = first.getEastNorth().getX();
+            y1 = first.getEastNorth().getY();
+            x2 = last.getEastNorth().getX();
+            y2 = last.getEastNorth().getY();
+            a = y2 - y1;
+            b = x1 - x2;
+            norm = Math.sqrt(a*a + b*b);
+            if (norm == 0)
+                // Nodes have same coordinates !
+                throw new InvalidSelection();
             a /= norm;
             b /= norm;
-            c = -(a*xM + b*yM);
+            c = -(a*x1 + b*y1);
         }
+        
+        /**
+         * Init a line equation from a way using its extremities.
+         * @param way
+         * @throws InvalidSelection 
+         */
+        public Line(Way way) throws InvalidSelection {
+            this(way.firstNode(), way.lastNode());
+        }
 
         /**
          * Orthogonal projection of a node N along this line.
@@ -338,7 +325,7 @@
          * @return The command that do the projection of this node
          */
         public Command projectionCommand(Node n) {
-            double s = (xM - n.getEastNorth().getX()) * a + (yM - n.getEastNorth().getY()) * b;
+            double s = (x1 - n.getEastNorth().getX()) * a + (y1 - n.getEastNorth().getY()) * b;
             return new MoveCommand(n, a*s, b*s);
         }
 
@@ -347,14 +334,26 @@
          * @param n Node to move to the intersection
          * @param other Second line for intersection
          * @return The command that move the node or null if line are parallels
+         * @throws InvalidSelection 
          */
-        public Command intersectionCommand(Node n, Line other) {
+        public Command intersectionCommand(Node n, Line other) throws InvalidSelection {
             double d = this.a * other.b - other.a * this.b;
-            if(d == 0) return null;
+            if(Math.abs(d) < 10e-6) throw new InvalidSelection(tr("Two parallels ways found. Abort."));
             double x = (this.b * other.c - other.b * this.c) / d;
             double y = (other.a * this.c - this.a * other.c) / d;
+            if(!this.isValidPoint(x, y) || !other.isValidPoint(x, y))
+                // To prevent strange phenomena with self crossing way
+                throw new InvalidSelection();
             return new MoveCommand(n, x - n.getEastNorth().getX(), y - n.getEastNorth().getY());
         }
+        
+        /**
+         * @return true if point (x, y) is "reasonably" close to the segment
+         */
+        private boolean isValidPoint(double x, double y) {
+            double abs = (-b*(x-x1) + a*(y-y1)) / norm;
+            return (abs > -1 && abs < 2);
+        }
     }
 
     @Override
