Index: trunk/src/org/openstreetmap/josm/actions/AlignInCircleAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/AlignInCircleAction.java	(revision 17385)
+++ trunk/src/org/openstreetmap/josm/actions/AlignInCircleAction.java	(revision 17386)
@@ -28,6 +28,10 @@
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon;
+import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon.JoinedWay;
+import org.openstreetmap.josm.data.validation.tests.SelfIntersectingWay;
 import org.openstreetmap.josm.gui.Notification;
 import org.openstreetmap.josm.tools.Geometry;
+import org.openstreetmap.josm.tools.Logging;
 import org.openstreetmap.josm.tools.Shortcut;
 
@@ -55,13 +59,39 @@
 
     /**
-     * Create a {@link MoveCommand} to move a node to a PolarCoor.
+     * InvalidSelection exception has to be raised when action can't be performed
+     */
+    public static class InvalidSelection extends Exception {
+
+        /**
+         * Create an InvalidSelection exception with default message
+         */
+        InvalidSelection() {
+            super(tr("Selection could not be used to align in circle."));
+        }
+
+        /**
+         * Create an InvalidSelection exception with specific message
+         * @param msg Message that will be displayed to the user
+         */
+        InvalidSelection(String msg) {
+            super(msg);
+        }
+    }
+
+    /**
+     * Add a {@link MoveCommand} to move a node to a PolarCoor if there is a significant move.
      * @param n Node to move
      * @param coor polar coordinate where to move the node
-     * @return new MoveCommand
-     * @since 13107
-     */
-    public static MoveCommand createMoveCommand(Node n, PolarCoor coor) {
+     * @param cmds list of commands
+     * @since 17386
+     */
+    public static void addMoveCommandIfNeeded(Node n, PolarCoor coor, List<Command> cmds) {
         EastNorth en = coor.toEastNorth();
-        return new MoveCommand(n, en.east() - n.getEastNorth().east(), en.north() - n.getEastNorth().north());
+        double deltaEast = en.east() - n.getEastNorth().east();
+        double deltaNorth = en.north() - n.getEastNorth().north();
+
+        if (Math.abs(deltaEast) > 5e-6 || Math.abs(deltaNorth) > 5e-6) {
+            cmds.add(new MoveCommand(n, deltaEast, deltaNorth));
+        }
     }
 
@@ -69,12 +99,33 @@
      * Perform AlignInCircle action.
      *
+     */
+    @Override
+    public void actionPerformed(ActionEvent e) {
+        if (!isEnabled())
+            return;
+
+        try {
+            Command cmd = buildCommand(getLayerManager().getEditDataSet());
+            UndoRedoHandler.getInstance().add(cmd);
+        } catch (InvalidSelection except) {
+            Logging.debug(except);
+            new Notification(except.getMessage())
+                .setIcon(JOptionPane.INFORMATION_MESSAGE)
+                .setDuration(Notification.TIME_SHORT)
+                .show();
+        }
+
+    }
+
+    /**
+     * Builds "align in circle" command depending on the selected objects.
      * A fixed node is a node for which it is forbidden to change the angle relative to center of the circle.
      * All other nodes are uniformly distributed.
-     *
+     * <p>
      * Case 1: One unclosed way.
      * --&gt; allow action, and align selected way nodes
      * If nodes contained by this way are selected, there are fix.
      * If nodes outside from the way are selected there are ignored.
-     *
+     * <p>
      * Case 2: One or more ways are selected and can be joined into a polygon
      * --&gt; allow action, and align selected ways nodes
@@ -85,14 +136,15 @@
      * In all cases, selected nodes are fix, nodes with more than one referrers are fix
      * (first referrer is the selected way)
-     *
+     * <p>
      * Case 3: Only nodes are selected
      * --&gt; Align these nodes, all are fix
-     */
-    @Override
-    public void actionPerformed(ActionEvent e) {
-        if (!isEnabled())
-            return;
-
-        Collection<OsmPrimitive> sel = getLayerManager().getEditDataSet().getSelected();
+     * @param ds data set in which the command operates
+     * @return the resulting command to execute to perform action
+     * @throws InvalidSelection if selection cannot be used
+     * @since 17386
+     *
+     */
+    public static Command buildCommand(DataSet ds) throws InvalidSelection {
+        Collection<OsmPrimitive> sel = ds.getSelected();
         List<Node> nodes = new LinkedList<>();
         // fixNodes: All nodes for which the angle relative to center should not be modified
@@ -113,4 +165,8 @@
             // Case 1
             Way w = ways.get(0);
+            if (SelfIntersectingWay.isSelfIntersecting(w)) {
+                throw new InvalidSelection(tr("Self-intersecting way"));
+            }
+
             fixNodes.add(w.firstNode());
             fixNodes.add(w.lastNode());
@@ -121,5 +177,5 @@
             closedWay.addNode(w.firstNode());
             nodes = collectNodesAnticlockwise(Collections.singletonList(closedWay));
-            closedWay.setNodes(null);
+            closedWay.setNodes(null); // see #19885
         } else if (!ways.isEmpty() && checkWaysArePolygon(ways)) {
             // Case 2
@@ -152,10 +208,5 @@
             nodes = collectNodesAnticlockwise(ways);
             if (nodes.size() < 4) {
-                new Notification(
-                        tr("Not enough nodes in selected ways."))
-                .setIcon(JOptionPane.INFORMATION_MESSAGE)
-                .setDuration(Notification.TIME_SHORT)
-                .show();
-                return;
+                throw new InvalidSelection(tr("Not enough nodes in selected ways."));
             }
         } else if (ways.isEmpty() && nodes.size() > 3) {
@@ -164,12 +215,12 @@
             // No need to reorder nodes since all are fix
         } else {
-            // Invalid action
-            new Notification(
-                    tr("Please select at least four nodes."))
-                    .setIcon(JOptionPane.INFORMATION_MESSAGE)
-                    .setDuration(Notification.TIME_SHORT)
-                    .show();
-            return;
-        }
+            if (ways.isEmpty() && nodes.size() <= 3)
+                throw new InvalidSelection(tr("Please select at least four nodes."));
+            throw new InvalidSelection();
+        }
+
+        // Check if one or more nodes are outside of download area
+        if (nodes.stream().anyMatch(Node::isOutsideDownloadArea))
+            throw new InvalidSelection(tr("One or more nodes involved in this action is outside of the downloaded area."));
 
         if (center == null) {
@@ -177,9 +228,5 @@
             center = Geometry.getCenter(nodes);
             if (center == null) {
-                new Notification(tr("Cannot determine center of selected nodes."))
-                    .setIcon(JOptionPane.INFORMATION_MESSAGE)
-                    .setDuration(Notification.TIME_SHORT)
-                    .show();
-                return;
+                throw new InvalidSelection(tr("Cannot determine center of selected nodes."));
             }
         }
@@ -195,7 +242,5 @@
         }
 
-        if (!actionAllowed(nodes)) return;
-
-        Collection<Command> cmds = new LinkedList<>();
+        List<Command> cmds = new LinkedList<>();
 
         // Move each node to that distance from the center.
@@ -217,5 +262,5 @@
             Node first = nodes.get(i % nodeCount);
             PolarCoor pcFirst = new PolarCoor(radius, PolarCoor.computeAngle(first.getEastNorth(), center), center);
-            cmds.add(createMoveCommand(first, pcFirst));
+            addMoveCommandIfNeeded(first, pcFirst, cmds);
             if (j > i + 1) {
                 double delta;
@@ -231,11 +276,12 @@
                 for (int k = i+1; k < j; k++) {
                     PolarCoor p = new PolarCoor(radius, pcFirst.angle + (k-i)*delta, center);
-                    cmds.add(createMoveCommand(nodes.get(k % nodeCount), p));
+                    addMoveCommandIfNeeded(nodes.get(k % nodeCount), p, cmds);
                 }
             }
             i = j; // Update start point for next iteration
         }
-
-        UndoRedoHandler.getInstance().add(new SequenceCommand(tr("Align Nodes in Circle"), cmds));
+        if (cmds.isEmpty())
+            throw new InvalidSelection(tr("nothing changed"));
+        return new SequenceCommand(tr("Align Nodes in Circle"), cmds);
     }
 
@@ -253,61 +299,17 @@
      * @param ways List of ways to be joined
      * @return Nodes anticlockwise ordered
-     */
-    private static List<Node> collectNodesAnticlockwise(List<Way> ways) {
-        List<Node> nodes = new ArrayList<>();
-        Node firstNode = ways.get(0).firstNode();
-        Node lastNode = null;
-        Way lastWay = null;
-        while (firstNode != lastNode) {
-            if (lastNode == null) lastNode = firstNode;
-            for (Way way: ways) {
-                if (way == lastWay) continue;
-                if (way.firstNode() == lastNode) {
-                    List<Node> wayNodes = way.getNodes();
-                    for (int i = 0; i < wayNodes.size() - 1; i++) {
-                        nodes.add(wayNodes.get(i));
-                    }
-                    lastNode = way.lastNode();
-                    lastWay = way;
-                    break;
-                }
-                if (way.lastNode() == lastNode) {
-                    List<Node> wayNodes = way.getNodes();
-                    for (int i = wayNodes.size() - 1; i > 0; i--) {
-                        nodes.add(wayNodes.get(i));
-                    }
-                    lastNode = way.firstNode();
-                    lastWay = way;
-                    break;
-                }
-            }
-        }
-        // Check if nodes are in anticlockwise order
-        int nc = nodes.size();
-        double area = 0;
-        for (int i = 0; i < nc; i++) {
-            EastNorth p1 = nodes.get(i).getEastNorth();
-            EastNorth p2 = nodes.get((i+1) % nc).getEastNorth();
-            area += p1.east()*p2.north() - p2.east()*p1.north();
-        }
-        if (area < 0)
+     * @throws InvalidSelection if selection cannot be used
+     */
+    private static List<Node> collectNodesAnticlockwise(List<Way> ways) throws InvalidSelection {
+        Collection<JoinedWay> rings = Multipolygon.joinWays(ways);
+        if (rings.size() != 1)
+            throw new InvalidSelection();
+        List<Node> nodes = new ArrayList<>(rings.iterator().next().getNodes());
+        if (nodes.get(0) != nodes.get(nodes.size()-1))
+            throw new InvalidSelection();
+        if (Geometry.isClockwise(nodes))
             Collections.reverse(nodes);
+        nodes.remove(nodes.size() - 1);
         return nodes;
-    }
-
-    /**
-     * Check if one or more nodes are outside of download area
-     * @param nodes Nodes to check
-     * @return true if action can be done
-     */
-    private static boolean actionAllowed(Collection<Node> nodes) {
-        boolean outside = nodes.stream().anyMatch(Node::isOutsideDownloadArea);
-        if (outside)
-            new Notification(
-                    tr("One or more nodes involved in this action is outside of the downloaded area."))
-                    .setIcon(JOptionPane.WARNING_MESSAGE)
-                    .setDuration(Notification.TIME_SHORT)
-                    .show();
-        return true;
     }
 
@@ -324,9 +326,11 @@
 
     /**
-     * Determines if ways can be joined into a polygon.
+     * Determines if ways can be joined into a single polygon.
      * @param ways The ways collection to check
-     * @return true if all ways can be joined into a polygon
+     * @return true if all ways can be joined into a single polygon
      */
     private static boolean checkWaysArePolygon(Collection<Way> ways) {
+        if (Multipolygon.joinWays(ways).size() != 1)
+            return false;
         // For each way, nodes strictly between first and last should't be reference by an other way
         for (Way way: ways) {
@@ -338,35 +342,6 @@
             }
         }
-        // Test if ways can be joined
-        Way currentWay = null;
-        Node startNode = null, endNode = null;
-        int used = 0;
-        while (true) {
-            Way nextWay = null;
-            for (Way w: ways) {
-                if (w.isClosed()) return ways.size() == 1;
-                if (w == currentWay) continue;
-                if (currentWay == null) {
-                    nextWay = w;
-                    startNode = w.firstNode();
-                    endNode = w.lastNode();
-                    break;
-                }
-                if (w.firstNode() == endNode) {
-                    nextWay = w;
-                    endNode = w.lastNode();
-                    break;
-                }
-                if (w.lastNode() == endNode) {
-                    nextWay = w;
-                    endNode = w.firstNode();
-                    break;
-                }
-            }
-            if (nextWay == null) return false;
-            used += 1;
-            currentWay = nextWay;
-            if (endNode == startNode) return used == ways.size();
-        }
-    }
+        return true;
+    }
+
 }
Index: trunk/src/org/openstreetmap/josm/data/validation/tests/SelfIntersectingWay.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/validation/tests/SelfIntersectingWay.java	(revision 17385)
+++ trunk/src/org/openstreetmap/josm/data/validation/tests/SelfIntersectingWay.java	(revision 17386)
@@ -12,4 +12,5 @@
 import org.openstreetmap.josm.data.validation.Test;
 import org.openstreetmap.josm.data.validation.TestError;
+import org.openstreetmap.josm.data.validation.tests.CrossingWays.SelfCrossing;
 
 /**
@@ -62,3 +63,16 @@
         }
     }
+
+    /**
+     * Check if the given way is self-intersecting
+     * @param way the way to check
+     * @return {@code true} if way contains some nodes more than once
+     * @see SelfCrossing
+     * @since 17386
+     */
+    public static boolean isSelfIntersecting(Way way) {
+        SelfIntersectingWay test = new SelfIntersectingWay();
+        test.visit(way);
+        return !test.errors.isEmpty();
+    }
 }
Index: trunk/test/data/alignCircleAfter1.osm
===================================================================
--- trunk/test/data/alignCircleAfter1.osm	(revision 17386)
+++ trunk/test/data/alignCircleAfter1.osm	(revision 17386)
@@ -0,0 +1,171 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version='0.6' generator='JOSM'>
+  <node id='21657940' action='modify' visible='true' version='11' lat='52.89258327894' lon='8.43040435661' />
+  <node id='21658035' action='modify' visible='true' version='9' lat='52.89253705241' lon='8.43065601774'>
+    <tag k='converted_by' v='Track2osm' />
+  </node>
+  <node id='21658036' visible='true' version='5' lat='52.8922741' lon='8.4295676'>
+    <tag k='converted_by' v='Track2osm' />
+  </node>
+  <node id='21658081' visible='true' version='6' lat='52.8931418' lon='8.4330586'>
+    <tag k='converted_by' v='Track2osm' />
+  </node>
+  <node id='73227374' action='modify' visible='true' version='11' lat='52.89252894605' lon='8.43051443214' />
+  <node id='76465657' visible='true' version='7' lat='52.8930039' lon='8.434235' />
+  <node id='76474542' visible='true' version='4' lat='52.8930507' lon='8.4304979' />
+  <node id='76474543' visible='true' version='3' lat='52.8928728' lon='8.4304707' />
+  <node id='76478545' visible='true' version='6' lat='52.8947806' lon='8.4321263'>
+    <tag k='highway' v='traffic_signals' />
+  </node>
+  <node id='77636315' action='modify' visible='true' version='7' lat='52.89252455997' lon='8.43058655242' />
+  <node id='77636748' action='modify' visible='true' version='9' lat='52.89274722575' lon='8.43053030077' />
+  <node id='77636749' action='modify' visible='true' version='7' lat='52.89271321643' lon='8.43043127806' />
+  <node id='77636750' action='modify' visible='true' version='7' lat='52.89265084589' lon='8.43038336491' />
+  <node id='78037253' visible='true' version='5' lat='52.894249' lon='8.4316544' />
+  <node id='81224584' visible='true' version='6' lat='52.8910536' lon='8.4274468' />
+  <node id='82466180' visible='true' version='6' lat='52.8913547' lon='8.4279641' />
+  <node id='92811392' visible='true' version='5' lat='52.891013' lon='8.4315072' />
+  <node id='92811394' visible='true' version='4' lat='52.8906098' lon='8.4318617' />
+  <node id='92811395' visible='true' version='6' lat='52.8893739' lon='8.4330256' />
+  <node id='105304730' visible='true' version='5' lat='52.8938347' lon='8.4311831' />
+  <node id='305468587' visible='true' version='5' lat='52.8889607' lon='8.4332039' />
+  <node id='305492177' visible='true' version='5' lat='52.8916124' lon='8.4311284' />
+  <node id='305492179' visible='true' version='5' lat='52.891801' lon='8.4310449' />
+  <node id='305492181' action='modify' visible='true' version='5' lat='52.89272411646' lon='8.43068736529' />
+  <node id='307388519' visible='true' version='3' lat='52.891443' lon='8.428123'>
+    <tag k='railway' v='level_crossing' />
+  </node>
+  <node id='440346289' visible='true' version='3' lat='52.8914858' lon='8.4281925' />
+  <node id='1133854864' visible='true' version='2' lat='52.8924889' lon='8.4300422' />
+  <node id='1133854897' visible='true' version='4' lat='52.8930884' lon='8.433848' />
+  <node id='1133854908' action='modify' visible='true' version='3' lat='52.89274644248' lon='8.43061318715' />
+  <node id='1133854931' action='modify' visible='true' version='3' lat='52.8926009117' lon='8.43074611006' />
+  <node id='1168240918' visible='true' version='2' lat='52.8946236' lon='8.4320619' />
+  <node id='1227909460' visible='true' version='3' lat='52.8923367' lon='8.4297234' />
+  <node id='1227909479' visible='true' version='2' lat='52.892313' lon='8.4308154' />
+  <node id='1577447735' visible='true' version='1' lat='52.8892172' lon='8.4331186' />
+  <node id='1601720771' visible='true' version='1' lat='52.8895817' lon='8.4328553' />
+  <node id='1601721365' visible='true' version='1' lat='52.8912922' lon='8.4313004' />
+  <node id='1601722162' visible='true' version='1' lat='52.8924023' lon='8.4307688' />
+  <node id='1601722860' visible='true' version='2' lat='52.8930766' lon='8.4325158' />
+  <node id='1601722886' visible='true' version='2' lat='52.8931176' lon='8.4327778' />
+  <node id='1601722905' visible='true' version='2' lat='52.89313' lon='8.4335832' />
+  <node id='1601722913' visible='true' version='2' lat='52.8931452' lon='8.4333054' />
+  <node id='1601723000' visible='true' version='1' lat='52.8932146' lon='8.4306242' />
+  <node id='1601723432' visible='true' version='1' lat='52.893986' lon='8.43134' />
+  <node id='1601723541' visible='true' version='1' lat='52.894126' lon='8.4315038' />
+  <node id='1601723720' visible='true' version='1' lat='52.8943366' lon='8.431761' />
+  <node id='1601723784' visible='true' version='1' lat='52.8945035' lon='8.4319646' />
+  <node id='2310189579' visible='true' version='1' lat='52.8924511' lon='8.4299149' />
+  <node id='2310189580' action='modify' visible='true' version='3' lat='52.89264323099' lon='8.43075555715' />
+  <node id='2440942588' visible='true' version='2' lat='52.8924465' lon='8.4307308' />
+  <node id='2440942594' visible='true' version='1' lat='52.89254' lon='8.4302435' />
+  <node id='2440942604' visible='true' version='1' lat='52.8928214' lon='8.4304945' />
+  <node id='2507291974' visible='true' version='1' lat='52.8901024' lon='8.432352' />
+  <node id='2517859485' visible='true' version='1' lat='52.8912381' lon='8.4277637' />
+  <node id='3625401978' action='modify' visible='true' version='1' lat='52.89256374333' lon='8.43071126577' />
+  <node id='3625401979' action='modify' visible='true' version='1' lat='52.89268428687' lon='8.43039925423' />
+  <node id='3625401980' action='modify' visible='true' version='1' lat='52.89254955141' lon='8.43045049694' />
+  <node id='3625401981' action='modify' visible='true' version='1' lat='52.89261607416' lon='8.4303851214' />
+  <node id='3625401982' action='modify' visible='true' version='1' lat='52.89273488298' lon='8.43047639051' />
+  <node id='5504817312' action='modify' visible='true' version='1' lat='52.89268463651' lon='8.43073825321' />
+  <way id='8325782' visible='true' version='14'>
+    <nd ref='77636748' />
+    <nd ref='3625401982' />
+    <nd ref='77636749' />
+    <nd ref='3625401979' />
+    <nd ref='77636750' />
+    <nd ref='3625401981' />
+    <nd ref='21657940' />
+    <nd ref='3625401980' />
+    <nd ref='73227374' />
+    <nd ref='77636315' />
+    <nd ref='21658035' />
+    <nd ref='3625401978' />
+    <nd ref='1133854931' />
+    <nd ref='2310189580' />
+    <nd ref='5504817312' />
+    <nd ref='305492181' />
+    <nd ref='1133854908' />
+    <nd ref='77636748' />
+    <tag k='converted_by' v='Track2osm' />
+    <tag k='highway' v='tertiary' />
+    <tag k='junction' v='roundabout' />
+    <tag k='maxspeed' v='50' />
+    <tag k='name' v='Visbeker Straße' />
+    <tag k='oneway' v='yes' />
+    <tag k='ref' v='K 248' />
+  </way>
+  <way id='9703493' visible='true' version='16'>
+    <nd ref='76478545' />
+    <nd ref='1168240918' />
+    <nd ref='1601723784' />
+    <nd ref='1601723720' />
+    <nd ref='78037253' />
+    <nd ref='1601723541' />
+    <nd ref='1601723432' />
+    <nd ref='105304730' />
+    <nd ref='1601723000' />
+    <nd ref='76474542' />
+    <nd ref='76474543' />
+    <nd ref='2440942604' />
+    <nd ref='77636748' />
+    <tag k='highway' v='tertiary' />
+    <tag k='maxspeed' v='50' />
+    <tag k='name' v='Visbeker Straße' />
+  </way>
+  <way id='10611560' visible='true' version='21'>
+    <nd ref='21658035' />
+    <nd ref='2440942588' />
+    <nd ref='1601722162' />
+    <nd ref='1227909479' />
+    <nd ref='305492179' />
+    <nd ref='305492177' />
+    <nd ref='1601721365' />
+    <nd ref='92811392' />
+    <nd ref='92811394' />
+    <nd ref='2507291974' />
+    <nd ref='1601720771' />
+    <nd ref='92811395' />
+    <nd ref='1577447735' />
+    <nd ref='305468587' />
+    <tag k='highway' v='unclassified' />
+    <tag k='maxspeed' v='50' />
+    <tag k='name' v='Goldenstedter Straße' />
+    <tag k='old_ref' v='L 882' />
+    <tag k='surface' v='asphalt' />
+  </way>
+  <way id='27822921' visible='true' version='11'>
+    <nd ref='5504817312' />
+    <nd ref='1601722860' />
+    <nd ref='1601722886' />
+    <nd ref='21658081' />
+    <nd ref='1601722913' />
+    <nd ref='1601722905' />
+    <nd ref='1133854897' />
+    <nd ref='76465657' />
+    <tag k='converted_by' v='Track2osm' />
+    <tag k='highway' v='tertiary' />
+    <tag k='maxspeed' v='50' />
+    <tag k='name' v='Feldstraße' />
+    <tag k='ref' v='K 248' />
+  </way>
+  <way id='495863846' visible='true' version='1'>
+    <nd ref='81224584' />
+    <nd ref='2517859485' />
+    <nd ref='82466180' />
+    <nd ref='307388519' />
+    <nd ref='440346289' />
+    <nd ref='21658036' />
+    <nd ref='1227909460' />
+    <nd ref='2310189579' />
+    <nd ref='1133854864' />
+    <nd ref='2440942594' />
+    <nd ref='21657940' />
+    <tag k='highway' v='tertiary' />
+    <tag k='maxspeed' v='50' />
+    <tag k='name' v='Visbeker Straße' />
+    <tag k='old_ref' v='L 837' />
+    <tag k='ref' v='K 248' />
+  </way>
+</osm>
Index: trunk/test/data/alignCircleAfter2.osm
===================================================================
--- trunk/test/data/alignCircleAfter2.osm	(revision 17386)
+++ trunk/test/data/alignCircleAfter2.osm	(revision 17386)
@@ -0,0 +1,171 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version='0.6' generator='JOSM'>
+  <node id='21657940' action='modify' visible='true' version='11' lat='52.89258327894' lon='8.43040435661' />
+  <node id='21658035' action='modify' visible='true' version='9' lat='52.89253705241' lon='8.43065601774'>
+    <tag k='converted_by' v='Track2osm' />
+  </node>
+  <node id='21658036' visible='true' version='5' lat='52.8922741' lon='8.4295676'>
+    <tag k='converted_by' v='Track2osm' />
+  </node>
+  <node id='21658081' visible='true' version='6' lat='52.8931418' lon='8.4330586'>
+    <tag k='converted_by' v='Track2osm' />
+  </node>
+  <node id='73227374' action='modify' visible='true' version='11' lat='52.89253607777' lon='8.43048489819' />
+  <node id='76465657' visible='true' version='7' lat='52.8930039' lon='8.434235' />
+  <node id='76474542' visible='true' version='4' lat='52.8930507' lon='8.4304979' />
+  <node id='76474543' visible='true' version='3' lat='52.8928728' lon='8.4304707' />
+  <node id='76478545' visible='true' version='6' lat='52.8947806' lon='8.4321263'>
+    <tag k='highway' v='traffic_signals' />
+  </node>
+  <node id='77636315' action='modify' visible='true' version='7' lat='52.89252406992' lon='8.43057188486' />
+  <node id='77636748' action='modify' visible='true' version='9' lat='52.89274722575' lon='8.43053030077' />
+  <node id='77636749' action='modify' visible='true' version='7' lat='52.8927050515' lon='8.43041996106' />
+  <node id='77636750' action='modify' visible='true' version='7' lat='52.89264366489' lon='8.4303822622' />
+  <node id='78037253' visible='true' version='5' lat='52.894249' lon='8.4316544' />
+  <node id='81224584' visible='true' version='6' lat='52.8910536' lon='8.4274468' />
+  <node id='82466180' visible='true' version='6' lat='52.8913547' lon='8.4279641' />
+  <node id='92811392' visible='true' version='5' lat='52.891013' lon='8.4315072' />
+  <node id='92811394' visible='true' version='4' lat='52.8906098' lon='8.4318617' />
+  <node id='92811395' visible='true' version='6' lat='52.8893739' lon='8.4330256' />
+  <node id='105304730' visible='true' version='5' lat='52.8938347' lon='8.4311831' />
+  <node id='305468587' visible='true' version='5' lat='52.8889607' lon='8.4332039' />
+  <node id='305492177' visible='true' version='5' lat='52.8916124' lon='8.4311284' />
+  <node id='305492179' visible='true' version='5' lat='52.891801' lon='8.4310449' />
+  <node id='305492181' action='modify' visible='true' version='5' lat='52.89272214462' lon='8.4306912467' />
+  <node id='307388519' visible='true' version='3' lat='52.891443' lon='8.428123'>
+    <tag k='railway' v='level_crossing' />
+  </node>
+  <node id='440346289' visible='true' version='3' lat='52.8914858' lon='8.4281925' />
+  <node id='1133854864' visible='true' version='2' lat='52.8924889' lon='8.4300422' />
+  <node id='1133854897' visible='true' version='4' lat='52.8930884' lon='8.433848' />
+  <node id='1133854908' action='modify' visible='true' version='3' lat='52.89274416343' lon='8.43062651187' />
+  <node id='1133854931' action='modify' visible='true' version='3' lat='52.89258456672' lon='8.43073455494' />
+  <node id='1168240918' visible='true' version='2' lat='52.8946236' lon='8.4320619' />
+  <node id='1227909460' visible='true' version='3' lat='52.8923367' lon='8.4297234' />
+  <node id='1227909479' visible='true' version='2' lat='52.892313' lon='8.4308154' />
+  <node id='1577447735' visible='true' version='1' lat='52.8892172' lon='8.4331186' />
+  <node id='1601720771' visible='true' version='1' lat='52.8895817' lon='8.4328553' />
+  <node id='1601721365' visible='true' version='1' lat='52.8912922' lon='8.4313004' />
+  <node id='1601722162' visible='true' version='1' lat='52.8924023' lon='8.4307688' />
+  <node id='1601722860' visible='true' version='2' lat='52.8930766' lon='8.4325158' />
+  <node id='1601722886' visible='true' version='2' lat='52.8931176' lon='8.4327778' />
+  <node id='1601722905' visible='true' version='2' lat='52.89313' lon='8.4335832' />
+  <node id='1601722913' visible='true' version='2' lat='52.8931452' lon='8.4333054' />
+  <node id='1601723000' visible='true' version='1' lat='52.8932146' lon='8.4306242' />
+  <node id='1601723432' visible='true' version='1' lat='52.893986' lon='8.43134' />
+  <node id='1601723541' visible='true' version='1' lat='52.894126' lon='8.4315038' />
+  <node id='1601723720' visible='true' version='1' lat='52.8943366' lon='8.431761' />
+  <node id='1601723784' visible='true' version='1' lat='52.8945035' lon='8.4319646' />
+  <node id='2310189579' visible='true' version='1' lat='52.8924511' lon='8.4299149' />
+  <node id='2310189580' action='modify' visible='true' version='3' lat='52.8926388894' lon='8.43075582567' />
+  <node id='2440942588' visible='true' version='2' lat='52.8924465' lon='8.4307308' />
+  <node id='2440942594' visible='true' version='1' lat='52.89254' lon='8.4302435' />
+  <node id='2440942604' visible='true' version='1' lat='52.8928214' lon='8.4304945' />
+  <node id='2507291974' visible='true' version='1' lat='52.8901024' lon='8.432352' />
+  <node id='2517859485' visible='true' version='1' lat='52.8912381' lon='8.4277637' />
+  <node id='3625401978' action='modify' visible='true' version='1' lat='52.89256108549' lon='8.43070739533' />
+  <node id='3625401979' action='modify' visible='true' version='1' lat='52.89267023291' lon='8.43039029405' />
+  <node id='3625401980' action='modify' visible='true' version='1' lat='52.89255255198' lon='8.43044466601' />
+  <node id='3625401981' action='modify' visible='true' version='1' lat='52.8926047691' lon='8.43038964467' />
+  <node id='3625401982' action='modify' visible='true' version='1' lat='52.89273262085' lon='8.4304700943' />
+  <node id='5504817312' action='modify' visible='true' version='1' lat='52.89268463651' lon='8.43073825321' />
+  <way id='8325782' visible='true' version='14'>
+    <nd ref='77636748' />
+    <nd ref='3625401982' />
+    <nd ref='77636749' />
+    <nd ref='3625401979' />
+    <nd ref='77636750' />
+    <nd ref='3625401981' />
+    <nd ref='21657940' />
+    <nd ref='3625401980' />
+    <nd ref='73227374' />
+    <nd ref='77636315' />
+    <nd ref='21658035' />
+    <nd ref='3625401978' />
+    <nd ref='1133854931' />
+    <nd ref='2310189580' />
+    <nd ref='5504817312' />
+    <nd ref='305492181' />
+    <nd ref='1133854908' />
+    <nd ref='77636748' />
+    <tag k='converted_by' v='Track2osm' />
+    <tag k='highway' v='tertiary' />
+    <tag k='junction' v='roundabout' />
+    <tag k='maxspeed' v='50' />
+    <tag k='name' v='Visbeker Straße' />
+    <tag k='oneway' v='yes' />
+    <tag k='ref' v='K 248' />
+  </way>
+  <way id='9703493' visible='true' version='16'>
+    <nd ref='76478545' />
+    <nd ref='1168240918' />
+    <nd ref='1601723784' />
+    <nd ref='1601723720' />
+    <nd ref='78037253' />
+    <nd ref='1601723541' />
+    <nd ref='1601723432' />
+    <nd ref='105304730' />
+    <nd ref='1601723000' />
+    <nd ref='76474542' />
+    <nd ref='76474543' />
+    <nd ref='2440942604' />
+    <nd ref='77636748' />
+    <tag k='highway' v='tertiary' />
+    <tag k='maxspeed' v='50' />
+    <tag k='name' v='Visbeker Straße' />
+  </way>
+  <way id='10611560' visible='true' version='21'>
+    <nd ref='21658035' />
+    <nd ref='2440942588' />
+    <nd ref='1601722162' />
+    <nd ref='1227909479' />
+    <nd ref='305492179' />
+    <nd ref='305492177' />
+    <nd ref='1601721365' />
+    <nd ref='92811392' />
+    <nd ref='92811394' />
+    <nd ref='2507291974' />
+    <nd ref='1601720771' />
+    <nd ref='92811395' />
+    <nd ref='1577447735' />
+    <nd ref='305468587' />
+    <tag k='highway' v='unclassified' />
+    <tag k='maxspeed' v='50' />
+    <tag k='name' v='Goldenstedter Straße' />
+    <tag k='old_ref' v='L 882' />
+    <tag k='surface' v='asphalt' />
+  </way>
+  <way id='27822921' visible='true' version='11'>
+    <nd ref='5504817312' />
+    <nd ref='1601722860' />
+    <nd ref='1601722886' />
+    <nd ref='21658081' />
+    <nd ref='1601722913' />
+    <nd ref='1601722905' />
+    <nd ref='1133854897' />
+    <nd ref='76465657' />
+    <tag k='converted_by' v='Track2osm' />
+    <tag k='highway' v='tertiary' />
+    <tag k='maxspeed' v='50' />
+    <tag k='name' v='Feldstraße' />
+    <tag k='ref' v='K 248' />
+  </way>
+  <way id='495863846' visible='true' version='1'>
+    <nd ref='81224584' />
+    <nd ref='2517859485' />
+    <nd ref='82466180' />
+    <nd ref='307388519' />
+    <nd ref='440346289' />
+    <nd ref='21658036' />
+    <nd ref='1227909460' />
+    <nd ref='2310189579' />
+    <nd ref='1133854864' />
+    <nd ref='2440942594' />
+    <nd ref='21657940' />
+    <tag k='highway' v='tertiary' />
+    <tag k='maxspeed' v='50' />
+    <tag k='name' v='Visbeker Straße' />
+    <tag k='old_ref' v='L 837' />
+    <tag k='ref' v='K 248' />
+  </way>
+</osm>
Index: trunk/test/data/alignCircleBefore.osm
===================================================================
--- trunk/test/data/alignCircleBefore.osm	(revision 17386)
+++ trunk/test/data/alignCircleBefore.osm	(revision 17386)
@@ -0,0 +1,171 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version='0.6' generator='JOSM'>
+  <node id='21657940' visible='true' version='11' lat='52.892583' lon='8.4304035' />
+  <node id='21658035' visible='true' version='9' lat='52.8925365' lon='8.4306565'>
+    <tag k='converted_by' v='Track2osm' />
+  </node>
+  <node id='21658036' visible='true' version='5' lat='52.8922741' lon='8.4295676'>
+    <tag k='converted_by' v='Track2osm' />
+  </node>
+  <node id='21658081' visible='true' version='6' lat='52.8931418' lon='8.4330586'>
+    <tag k='converted_by' v='Track2osm' />
+  </node>
+  <node id='73227374' visible='true' version='11' lat='52.8925356' lon='8.4304845' />
+  <node id='76465657' visible='true' version='7' lat='52.8930039' lon='8.434235' />
+  <node id='76474542' visible='true' version='4' lat='52.8930507' lon='8.4304979' />
+  <node id='76474543' visible='true' version='3' lat='52.8928728' lon='8.4304707' />
+  <node id='76478545' visible='true' version='6' lat='52.8947806' lon='8.4321263'>
+    <tag k='highway' v='traffic_signals' />
+  </node>
+  <node id='77636315' visible='true' version='7' lat='52.8925235' lon='8.4305719' />
+  <node id='77636748' visible='true' version='9' lat='52.8927478' lon='8.4305301' />
+  <node id='77636749' visible='true' version='7' lat='52.8927054' lon='8.4304192' />
+  <node id='77636750' visible='true' version='7' lat='52.8926437' lon='8.4303813' />
+  <node id='78037253' visible='true' version='5' lat='52.894249' lon='8.4316544' />
+  <node id='81224584' visible='true' version='6' lat='52.8910536' lon='8.4274468' />
+  <node id='82466180' visible='true' version='6' lat='52.8913547' lon='8.4279641' />
+  <node id='92811392' visible='true' version='5' lat='52.891013' lon='8.4315072' />
+  <node id='92811394' visible='true' version='4' lat='52.8906098' lon='8.4318617' />
+  <node id='92811395' visible='true' version='6' lat='52.8893739' lon='8.4330256' />
+  <node id='105304730' visible='true' version='5' lat='52.8938347' lon='8.4311831' />
+  <node id='305468587' visible='true' version='5' lat='52.8889607' lon='8.4332039' />
+  <node id='305492177' visible='true' version='5' lat='52.8916124' lon='8.4311284' />
+  <node id='305492179' visible='true' version='5' lat='52.891801' lon='8.4310449' />
+  <node id='305492181' visible='true' version='5' lat='52.8927226' lon='8.4306919' />
+  <node id='307388519' visible='true' version='3' lat='52.891443' lon='8.428123'>
+    <tag k='railway' v='level_crossing' />
+  </node>
+  <node id='440346289' visible='true' version='3' lat='52.8914858' lon='8.4281925' />
+  <node id='1133854864' visible='true' version='2' lat='52.8924889' lon='8.4300422' />
+  <node id='1133854897' visible='true' version='4' lat='52.8930884' lon='8.433848' />
+  <node id='1133854908' visible='true' version='3' lat='52.8927447' lon='8.4306268' />
+  <node id='1133854931' visible='true' version='3' lat='52.8925843' lon='8.4307354' />
+  <node id='1168240918' visible='true' version='2' lat='52.8946236' lon='8.4320619' />
+  <node id='1227909460' visible='true' version='3' lat='52.8923367' lon='8.4297234' />
+  <node id='1227909479' visible='true' version='2' lat='52.892313' lon='8.4308154' />
+  <node id='1577447735' visible='true' version='1' lat='52.8892172' lon='8.4331186' />
+  <node id='1601720771' visible='true' version='1' lat='52.8895817' lon='8.4328553' />
+  <node id='1601721365' visible='true' version='1' lat='52.8912922' lon='8.4313004' />
+  <node id='1601722162' visible='true' version='1' lat='52.8924023' lon='8.4307688' />
+  <node id='1601722860' visible='true' version='2' lat='52.8930766' lon='8.4325158' />
+  <node id='1601722886' visible='true' version='2' lat='52.8931176' lon='8.4327778' />
+  <node id='1601722905' visible='true' version='2' lat='52.89313' lon='8.4335832' />
+  <node id='1601722913' visible='true' version='2' lat='52.8931452' lon='8.4333054' />
+  <node id='1601723000' visible='true' version='1' lat='52.8932146' lon='8.4306242' />
+  <node id='1601723432' visible='true' version='1' lat='52.893986' lon='8.43134' />
+  <node id='1601723541' visible='true' version='1' lat='52.894126' lon='8.4315038' />
+  <node id='1601723720' visible='true' version='1' lat='52.8943366' lon='8.431761' />
+  <node id='1601723784' visible='true' version='1' lat='52.8945035' lon='8.4319646' />
+  <node id='2310189579' visible='true' version='1' lat='52.8924511' lon='8.4299149' />
+  <node id='2310189580' visible='true' version='3' lat='52.8926389' lon='8.4307568' />
+  <node id='2440942588' visible='true' version='2' lat='52.8924465' lon='8.4307308' />
+  <node id='2440942594' visible='true' version='1' lat='52.89254' lon='8.4302435' />
+  <node id='2440942604' visible='true' version='1' lat='52.8928214' lon='8.4304945' />
+  <node id='2507291974' visible='true' version='1' lat='52.8901024' lon='8.432352' />
+  <node id='2517859485' visible='true' version='1' lat='52.8912381' lon='8.4277637' />
+  <node id='3625401978' visible='true' version='1' lat='52.8925607' lon='8.4307081' />
+  <node id='3625401979' visible='true' version='1' lat='52.8926704' lon='8.4303894' />
+  <node id='3625401980' visible='true' version='1' lat='52.8925521' lon='8.430444' />
+  <node id='3625401981' visible='true' version='1' lat='52.8926046' lon='8.4303887' />
+  <node id='3625401982' visible='true' version='1' lat='52.8927331' lon='8.4304696' />
+  <node id='5504817312' visible='true' version='1' lat='52.8926807' lon='8.4307243' />
+  <way id='8325782' visible='true' version='14'>
+    <nd ref='77636748' />
+    <nd ref='3625401982' />
+    <nd ref='77636749' />
+    <nd ref='3625401979' />
+    <nd ref='77636750' />
+    <nd ref='3625401981' />
+    <nd ref='21657940' />
+    <nd ref='3625401980' />
+    <nd ref='73227374' />
+    <nd ref='77636315' />
+    <nd ref='21658035' />
+    <nd ref='3625401978' />
+    <nd ref='1133854931' />
+    <nd ref='2310189580' />
+    <nd ref='5504817312' />
+    <nd ref='305492181' />
+    <nd ref='1133854908' />
+    <nd ref='77636748' />
+    <tag k='converted_by' v='Track2osm' />
+    <tag k='highway' v='tertiary' />
+    <tag k='junction' v='roundabout' />
+    <tag k='maxspeed' v='50' />
+    <tag k='name' v='Visbeker Straße' />
+    <tag k='oneway' v='yes' />
+    <tag k='ref' v='K 248' />
+  </way>
+  <way id='9703493' visible='true' version='16'>
+    <nd ref='76478545' />
+    <nd ref='1168240918' />
+    <nd ref='1601723784' />
+    <nd ref='1601723720' />
+    <nd ref='78037253' />
+    <nd ref='1601723541' />
+    <nd ref='1601723432' />
+    <nd ref='105304730' />
+    <nd ref='1601723000' />
+    <nd ref='76474542' />
+    <nd ref='76474543' />
+    <nd ref='2440942604' />
+    <nd ref='77636748' />
+    <tag k='highway' v='tertiary' />
+    <tag k='maxspeed' v='50' />
+    <tag k='name' v='Visbeker Straße' />
+  </way>
+  <way id='10611560' visible='true' version='21'>
+    <nd ref='21658035' />
+    <nd ref='2440942588' />
+    <nd ref='1601722162' />
+    <nd ref='1227909479' />
+    <nd ref='305492179' />
+    <nd ref='305492177' />
+    <nd ref='1601721365' />
+    <nd ref='92811392' />
+    <nd ref='92811394' />
+    <nd ref='2507291974' />
+    <nd ref='1601720771' />
+    <nd ref='92811395' />
+    <nd ref='1577447735' />
+    <nd ref='305468587' />
+    <tag k='highway' v='unclassified' />
+    <tag k='maxspeed' v='50' />
+    <tag k='name' v='Goldenstedter Straße' />
+    <tag k='old_ref' v='L 882' />
+    <tag k='surface' v='asphalt' />
+  </way>
+  <way id='27822921' visible='true' version='11'>
+    <nd ref='5504817312' />
+    <nd ref='1601722860' />
+    <nd ref='1601722886' />
+    <nd ref='21658081' />
+    <nd ref='1601722913' />
+    <nd ref='1601722905' />
+    <nd ref='1133854897' />
+    <nd ref='76465657' />
+    <tag k='converted_by' v='Track2osm' />
+    <tag k='highway' v='tertiary' />
+    <tag k='maxspeed' v='50' />
+    <tag k='name' v='Feldstraße' />
+    <tag k='ref' v='K 248' />
+  </way>
+  <way id='495863846' visible='true' version='1'>
+    <nd ref='81224584' />
+    <nd ref='2517859485' />
+    <nd ref='82466180' />
+    <nd ref='307388519' />
+    <nd ref='440346289' />
+    <nd ref='21658036' />
+    <nd ref='1227909460' />
+    <nd ref='2310189579' />
+    <nd ref='1133854864' />
+    <nd ref='2440942594' />
+    <nd ref='21657940' />
+    <tag k='highway' v='tertiary' />
+    <tag k='maxspeed' v='50' />
+    <tag k='name' v='Visbeker Straße' />
+    <tag k='old_ref' v='L 837' />
+    <tag k='ref' v='K 248' />
+  </way>
+</osm>
Index: trunk/test/data/alignCircleTwoWaysAfter.osm
===================================================================
--- trunk/test/data/alignCircleTwoWaysAfter.osm	(revision 17386)
+++ trunk/test/data/alignCircleTwoWaysAfter.osm	(revision 17386)
@@ -0,0 +1,181 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version='0.6' upload='never' generator='JOSM'>
+  <node id='21657940' version='11' lat='52.8925832' lon='8.4304043' />
+  <node id='21658035' version='9' lat='52.892537' lon='8.430656'>
+    <tag k='converted_by' v='Track2osm' />
+  </node>
+  <node id='21658036' version='5' lat='52.8922741' lon='8.4295676'>
+    <tag k='converted_by' v='Track2osm' />
+  </node>
+  <node id='21658081' version='6' lat='52.8931418' lon='8.4330586'>
+    <tag k='converted_by' v='Track2osm' />
+  </node>
+  <node id='73227374' version='11' lat='52.892536' lon='8.4304848' />
+  <node id='76465657' version='7' lat='52.8930039' lon='8.434235' />
+  <node id='76474542' version='4' lat='52.8930507' lon='8.4304979' />
+  <node id='76474543' version='3' lat='52.8928728' lon='8.4304707' />
+  <node id='76478545' version='6' lat='52.8947806' lon='8.4321263'>
+    <tag k='highway' v='traffic_signals' />
+  </node>
+  <node id='77636315' version='7' lat='52.892524' lon='8.4305706' />
+  <node id='77636748' version='9' lat='52.8927472' lon='8.4305303' />
+  <node id='77636749' version='7' lat='52.8927132' lon='8.4304312' />
+  <node id='77636750' version='7' lat='52.8926508' lon='8.4303833' />
+  <node id='78037253' version='5' lat='52.894249' lon='8.4316544' />
+  <node id='81224584' version='6' lat='52.8910536' lon='8.4274468' />
+  <node id='82466180' version='6' lat='52.8913547' lon='8.4279641' />
+  <node id='92811392' version='5' lat='52.891013' lon='8.4315072' />
+  <node id='92811394' version='4' lat='52.8906098' lon='8.4318617' />
+  <node id='92811395' version='6' lat='52.8893739' lon='8.4330256' />
+  <node id='105304730' version='5' lat='52.8938347' lon='8.4311831' />
+  <node id='305468587' version='5' lat='52.8889607' lon='8.4332039' />
+  <node id='305492177' version='5' lat='52.8916124' lon='8.4311284' />
+  <node id='305492179' version='5' lat='52.891801' lon='8.4310449' />
+  <node id='305492181' version='5' lat='52.8927214' lon='8.4306926' />
+  <node id='307388519' version='3' lat='52.891443' lon='8.428123'>
+    <tag k='railway' v='level_crossing' />
+  </node>
+  <node id='440346289' version='3' lat='52.8914858' lon='8.4281925' />
+  <node id='1133854864' version='2' lat='52.8924889' lon='8.4300422' />
+  <node id='1133854897' version='4' lat='52.8930884' lon='8.433848' />
+  <node id='1133854908' version='3' lat='52.8927441' lon='8.4306265' />
+  <node id='1133854931' version='3' lat='52.8926009' lon='8.4307461' />
+  <node id='1168240918' version='2' lat='52.8946236' lon='8.4320619' />
+  <node id='1227909460' version='3' lat='52.8923367' lon='8.4297234' />
+  <node id='1227909479' version='2' lat='52.892313' lon='8.4308154' />
+  <node id='1577447735' version='1' lat='52.8892172' lon='8.4331186' />
+  <node id='1601720771' version='1' lat='52.8895817' lon='8.4328553' />
+  <node id='1601721365' version='1' lat='52.8912922' lon='8.4313004' />
+  <node id='1601722162' version='1' lat='52.8924023' lon='8.4307688' />
+  <node id='1601722860' version='2' lat='52.8930766' lon='8.4325158' />
+  <node id='1601722886' version='2' lat='52.8931176' lon='8.4327778' />
+  <node id='1601722905' version='2' lat='52.89313' lon='8.4335832' />
+  <node id='1601722913' version='2' lat='52.8931452' lon='8.4333054' />
+  <node id='1601723000' version='1' lat='52.8932146' lon='8.4306242' />
+  <node id='1601723432' version='1' lat='52.893986' lon='8.43134' />
+  <node id='1601723541' version='1' lat='52.894126' lon='8.4315038' />
+  <node id='1601723720' version='1' lat='52.8943366' lon='8.431761' />
+  <node id='1601723784' version='1' lat='52.8945035' lon='8.4319646' />
+  <node id='2310189579' version='1' lat='52.8924511' lon='8.4299149' />
+  <node id='2310189580' version='3' lat='52.8926432' lon='8.4307555' />
+  <node id='2440942588' version='2' lat='52.8924465' lon='8.4307308' />
+  <node id='2440942594' version='1' lat='52.89254' lon='8.4302435' />
+  <node id='2440942604' version='1' lat='52.8928214' lon='8.4304945' />
+  <node id='2507291974' version='1' lat='52.8901024' lon='8.432352' />
+  <node id='2517859485' version='1' lat='52.8912381' lon='8.4277637' />
+  <node id='3625401978' version='1' lat='52.8925637' lon='8.4307112' />
+  <node id='3625401979' version='1' lat='52.8926842' lon='8.4303992' />
+  <node id='3625401980' version='1' lat='52.8925559' lon='8.4304386' />
+  <node id='3625401981' version='1' lat='52.892616' lon='8.4303851' />
+  <node id='3625401982' version='1' lat='52.8927348' lon='8.4304763' />
+  <node id='5504817312' version='1' lat='52.8926846' lon='8.4307382' />
+  <way id='-104551'>
+    <nd ref='73227374' />
+    <nd ref='77636315' />
+    <nd ref='21658035' />
+    <nd ref='3625401978' />
+    <nd ref='1133854931' />
+    <nd ref='2310189580' />
+    <nd ref='5504817312' />
+    <nd ref='305492181' />
+    <nd ref='1133854908' />
+    <tag k='converted_by' v='Track2osm' />
+    <tag k='highway' v='tertiary' />
+    <tag k='junction' v='roundabout' />
+    <tag k='maxspeed' v='50' />
+    <tag k='name' v='Visbeker Straße' />
+    <tag k='oneway' v='yes' />
+    <tag k='ref' v='K 248' />
+  </way>
+  <way id='8325782' version='14'>
+    <nd ref='1133854908' />
+    <nd ref='77636748' />
+    <nd ref='3625401982' />
+    <nd ref='77636749' />
+    <nd ref='3625401979' />
+    <nd ref='77636750' />
+    <nd ref='3625401981' />
+    <nd ref='21657940' />
+    <nd ref='3625401980' />
+    <nd ref='73227374' />
+    <tag k='converted_by' v='Track2osm' />
+    <tag k='highway' v='tertiary' />
+    <tag k='junction' v='roundabout' />
+    <tag k='maxspeed' v='50' />
+    <tag k='name' v='Visbeker Straße' />
+    <tag k='oneway' v='yes' />
+    <tag k='ref' v='K 248' />
+  </way>
+  <way id='9703493' version='16'>
+    <nd ref='76478545' />
+    <nd ref='1168240918' />
+    <nd ref='1601723784' />
+    <nd ref='1601723720' />
+    <nd ref='78037253' />
+    <nd ref='1601723541' />
+    <nd ref='1601723432' />
+    <nd ref='105304730' />
+    <nd ref='1601723000' />
+    <nd ref='76474542' />
+    <nd ref='76474543' />
+    <nd ref='2440942604' />
+    <nd ref='77636748' />
+    <tag k='highway' v='tertiary' />
+    <tag k='maxspeed' v='50' />
+    <tag k='name' v='Visbeker Straße' />
+  </way>
+  <way id='10611560' version='21'>
+    <nd ref='21658035' />
+    <nd ref='2440942588' />
+    <nd ref='1601722162' />
+    <nd ref='1227909479' />
+    <nd ref='305492179' />
+    <nd ref='305492177' />
+    <nd ref='1601721365' />
+    <nd ref='92811392' />
+    <nd ref='92811394' />
+    <nd ref='2507291974' />
+    <nd ref='1601720771' />
+    <nd ref='92811395' />
+    <nd ref='1577447735' />
+    <nd ref='305468587' />
+    <tag k='highway' v='unclassified' />
+    <tag k='maxspeed' v='50' />
+    <tag k='name' v='Goldenstedter Straße' />
+    <tag k='old_ref' v='L 882' />
+    <tag k='surface' v='asphalt' />
+  </way>
+  <way id='27822921' version='11'>
+    <nd ref='5504817312' />
+    <nd ref='1601722860' />
+    <nd ref='1601722886' />
+    <nd ref='21658081' />
+    <nd ref='1601722913' />
+    <nd ref='1601722905' />
+    <nd ref='1133854897' />
+    <nd ref='76465657' />
+    <tag k='converted_by' v='Track2osm' />
+    <tag k='highway' v='tertiary' />
+    <tag k='maxspeed' v='50' />
+    <tag k='name' v='Feldstraße' />
+    <tag k='ref' v='K 248' />
+  </way>
+  <way id='495863846' version='1'>
+    <nd ref='81224584' />
+    <nd ref='2517859485' />
+    <nd ref='82466180' />
+    <nd ref='307388519' />
+    <nd ref='440346289' />
+    <nd ref='21658036' />
+    <nd ref='1227909460' />
+    <nd ref='2310189579' />
+    <nd ref='1133854864' />
+    <nd ref='2440942594' />
+    <nd ref='21657940' />
+    <tag k='highway' v='tertiary' />
+    <tag k='maxspeed' v='50' />
+    <tag k='name' v='Visbeker Straße' />
+    <tag k='old_ref' v='L 837' />
+    <tag k='ref' v='K 248' />
+  </way>
+</osm>
Index: trunk/test/data/alignCircleTwoWaysBefore.osm
===================================================================
--- trunk/test/data/alignCircleTwoWaysBefore.osm	(revision 17386)
+++ trunk/test/data/alignCircleTwoWaysBefore.osm	(revision 17386)
@@ -0,0 +1,181 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version='0.6' upload='never' generator='JOSM'>
+  <node id='21657940' version='11' lat='52.892583' lon='8.4304035' />
+  <node id='21658035' version='9' lat='52.8925365' lon='8.4306565'>
+    <tag k='converted_by' v='Track2osm' />
+  </node>
+  <node id='21658036' version='5' lat='52.8922741' lon='8.4295676'>
+    <tag k='converted_by' v='Track2osm' />
+  </node>
+  <node id='21658081' version='6' lat='52.8931418' lon='8.4330586'>
+    <tag k='converted_by' v='Track2osm' />
+  </node>
+  <node id='73227374' version='11' lat='52.8925356' lon='8.4304845' />
+  <node id='76465657' version='7' lat='52.8930039' lon='8.434235' />
+  <node id='76474542' version='4' lat='52.8930507' lon='8.4304979' />
+  <node id='76474543' version='3' lat='52.8928728' lon='8.4304707' />
+  <node id='76478545' version='6' lat='52.8947806' lon='8.4321263'>
+    <tag k='highway' v='traffic_signals' />
+  </node>
+  <node id='77636315' version='7' lat='52.8925235' lon='8.4305719' />
+  <node id='77636748' version='9' lat='52.8927478' lon='8.4305301' />
+  <node id='77636749' version='7' lat='52.8927054' lon='8.4304192' />
+  <node id='77636750' version='7' lat='52.8926437' lon='8.4303813' />
+  <node id='78037253' version='5' lat='52.894249' lon='8.4316544' />
+  <node id='81224584' version='6' lat='52.8910536' lon='8.4274468' />
+  <node id='82466180' version='6' lat='52.8913547' lon='8.4279641' />
+  <node id='92811392' version='5' lat='52.891013' lon='8.4315072' />
+  <node id='92811394' version='4' lat='52.8906098' lon='8.4318617' />
+  <node id='92811395' version='6' lat='52.8893739' lon='8.4330256' />
+  <node id='105304730' version='5' lat='52.8938347' lon='8.4311831' />
+  <node id='305468587' version='5' lat='52.8889607' lon='8.4332039' />
+  <node id='305492177' version='5' lat='52.8916124' lon='8.4311284' />
+  <node id='305492179' version='5' lat='52.891801' lon='8.4310449' />
+  <node id='305492181' version='5' lat='52.8927226' lon='8.4306919' />
+  <node id='307388519' version='3' lat='52.891443' lon='8.428123'>
+    <tag k='railway' v='level_crossing' />
+  </node>
+  <node id='440346289' version='3' lat='52.8914858' lon='8.4281925' />
+  <node id='1133854864' version='2' lat='52.8924889' lon='8.4300422' />
+  <node id='1133854897' version='4' lat='52.8930884' lon='8.433848' />
+  <node id='1133854908' version='3' lat='52.8927447' lon='8.4306268' />
+  <node id='1133854931' version='3' lat='52.8925843' lon='8.4307354' />
+  <node id='1168240918' version='2' lat='52.8946236' lon='8.4320619' />
+  <node id='1227909460' version='3' lat='52.8923367' lon='8.4297234' />
+  <node id='1227909479' version='2' lat='52.892313' lon='8.4308154' />
+  <node id='1577447735' version='1' lat='52.8892172' lon='8.4331186' />
+  <node id='1601720771' version='1' lat='52.8895817' lon='8.4328553' />
+  <node id='1601721365' version='1' lat='52.8912922' lon='8.4313004' />
+  <node id='1601722162' version='1' lat='52.8924023' lon='8.4307688' />
+  <node id='1601722860' version='2' lat='52.8930766' lon='8.4325158' />
+  <node id='1601722886' version='2' lat='52.8931176' lon='8.4327778' />
+  <node id='1601722905' version='2' lat='52.89313' lon='8.4335832' />
+  <node id='1601722913' version='2' lat='52.8931452' lon='8.4333054' />
+  <node id='1601723000' version='1' lat='52.8932146' lon='8.4306242' />
+  <node id='1601723432' version='1' lat='52.893986' lon='8.43134' />
+  <node id='1601723541' version='1' lat='52.894126' lon='8.4315038' />
+  <node id='1601723720' version='1' lat='52.8943366' lon='8.431761' />
+  <node id='1601723784' version='1' lat='52.8945035' lon='8.4319646' />
+  <node id='2310189579' version='1' lat='52.8924511' lon='8.4299149' />
+  <node id='2310189580' version='3' lat='52.8926389' lon='8.4307568' />
+  <node id='2440942588' version='2' lat='52.8924465' lon='8.4307308' />
+  <node id='2440942594' version='1' lat='52.89254' lon='8.4302435' />
+  <node id='2440942604' version='1' lat='52.8928214' lon='8.4304945' />
+  <node id='2507291974' version='1' lat='52.8901024' lon='8.432352' />
+  <node id='2517859485' version='1' lat='52.8912381' lon='8.4277637' />
+  <node id='3625401978' version='1' lat='52.8925607' lon='8.4307081' />
+  <node id='3625401979' version='1' lat='52.8926704' lon='8.4303894' />
+  <node id='3625401980' version='1' lat='52.8925521' lon='8.430444' />
+  <node id='3625401981' version='1' lat='52.8926046' lon='8.4303887' />
+  <node id='3625401982' version='1' lat='52.8927331' lon='8.4304696' />
+  <node id='5504817312' version='1' lat='52.8926807' lon='8.4307243' />
+  <way id='-104550'>
+    <nd ref='73227374' />
+    <nd ref='77636315' />
+    <nd ref='21658035' />
+    <nd ref='3625401978' />
+    <nd ref='1133854931' />
+    <nd ref='2310189580' />
+    <nd ref='5504817312' />
+    <nd ref='305492181' />
+    <nd ref='1133854908' />
+    <tag k='converted_by' v='Track2osm' />
+    <tag k='highway' v='tertiary' />
+    <tag k='junction' v='roundabout' />
+    <tag k='maxspeed' v='50' />
+    <tag k='name' v='Visbeker Straße' />
+    <tag k='oneway' v='yes' />
+    <tag k='ref' v='K 248' />
+  </way>
+  <way id='8325782' version='14'>
+    <nd ref='1133854908' />
+    <nd ref='77636748' />
+    <nd ref='3625401982' />
+    <nd ref='77636749' />
+    <nd ref='3625401979' />
+    <nd ref='77636750' />
+    <nd ref='3625401981' />
+    <nd ref='21657940' />
+    <nd ref='3625401980' />
+    <nd ref='73227374' />
+    <tag k='converted_by' v='Track2osm' />
+    <tag k='highway' v='tertiary' />
+    <tag k='junction' v='roundabout' />
+    <tag k='maxspeed' v='50' />
+    <tag k='name' v='Visbeker Straße' />
+    <tag k='oneway' v='yes' />
+    <tag k='ref' v='K 248' />
+  </way>
+  <way id='9703493' version='16'>
+    <nd ref='76478545' />
+    <nd ref='1168240918' />
+    <nd ref='1601723784' />
+    <nd ref='1601723720' />
+    <nd ref='78037253' />
+    <nd ref='1601723541' />
+    <nd ref='1601723432' />
+    <nd ref='105304730' />
+    <nd ref='1601723000' />
+    <nd ref='76474542' />
+    <nd ref='76474543' />
+    <nd ref='2440942604' />
+    <nd ref='77636748' />
+    <tag k='highway' v='tertiary' />
+    <tag k='maxspeed' v='50' />
+    <tag k='name' v='Visbeker Straße' />
+  </way>
+  <way id='10611560' version='21'>
+    <nd ref='21658035' />
+    <nd ref='2440942588' />
+    <nd ref='1601722162' />
+    <nd ref='1227909479' />
+    <nd ref='305492179' />
+    <nd ref='305492177' />
+    <nd ref='1601721365' />
+    <nd ref='92811392' />
+    <nd ref='92811394' />
+    <nd ref='2507291974' />
+    <nd ref='1601720771' />
+    <nd ref='92811395' />
+    <nd ref='1577447735' />
+    <nd ref='305468587' />
+    <tag k='highway' v='unclassified' />
+    <tag k='maxspeed' v='50' />
+    <tag k='name' v='Goldenstedter Straße' />
+    <tag k='old_ref' v='L 882' />
+    <tag k='surface' v='asphalt' />
+  </way>
+  <way id='27822921' version='11'>
+    <nd ref='5504817312' />
+    <nd ref='1601722860' />
+    <nd ref='1601722886' />
+    <nd ref='21658081' />
+    <nd ref='1601722913' />
+    <nd ref='1601722905' />
+    <nd ref='1133854897' />
+    <nd ref='76465657' />
+    <tag k='converted_by' v='Track2osm' />
+    <tag k='highway' v='tertiary' />
+    <tag k='maxspeed' v='50' />
+    <tag k='name' v='Feldstraße' />
+    <tag k='ref' v='K 248' />
+  </way>
+  <way id='495863846' version='1'>
+    <nd ref='81224584' />
+    <nd ref='2517859485' />
+    <nd ref='82466180' />
+    <nd ref='307388519' />
+    <nd ref='440346289' />
+    <nd ref='21658036' />
+    <nd ref='1227909460' />
+    <nd ref='2310189579' />
+    <nd ref='1133854864' />
+    <nd ref='2440942594' />
+    <nd ref='21657940' />
+    <tag k='highway' v='tertiary' />
+    <tag k='maxspeed' v='50' />
+    <tag k='name' v='Visbeker Straße' />
+    <tag k='old_ref' v='L 837' />
+    <tag k='ref' v='K 248' />
+  </way>
+</osm>
Index: trunk/test/unit/org/openstreetmap/josm/actions/AlignInCircleActionTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/actions/AlignInCircleActionTest.java	(revision 17386)
+++ trunk/test/unit/org/openstreetmap/josm/actions/AlignInCircleActionTest.java	(revision 17386)
@@ -0,0 +1,152 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.actions;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.openstreetmap.josm.TestUtils;
+import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.io.OsmReader;
+import org.openstreetmap.josm.testutils.JOSMTestRules;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+
+/**
+ * Unit tests for class {@link AlignInLineAction}.
+ */
+final class AlignInCircleActionTest {
+
+    /**
+     * Setup test.
+     */
+    @RegisterExtension
+    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
+    public JOSMTestRules test = new JOSMTestRules().projection();
+
+
+    /**
+     * Test case: way with several nodes selected
+     * @throws Exception if an error occurs
+     */
+    @Test
+    void testWaySelected() throws Exception {
+        DataSet ds = OsmReader.parseDataSet(Files.newInputStream(Paths.get(TestUtils.getTestDataRoot(), "alignCircleBefore.osm")), null);
+        DataSet ds2 = OsmReader.parseDataSet(Files.newInputStream(Paths.get(TestUtils.getTestDataRoot(), "alignCircleAfter1.osm")), null);
+
+        Way roundabout = null;
+        for (Way w : ds.getWays()) {
+            if ("roundabout".equals(w.get("junction"))) {
+                roundabout = w;
+                break;
+            }
+        }
+        assertNotNull(roundabout);
+        if (roundabout != null) {
+            ds.setSelected(roundabout);
+            Command c = AlignInCircleAction.buildCommand(ds);
+            c.executeCommand();
+            Way expected = (Way) ds2.getPrimitiveById(roundabout);
+            assertNotNull(expected);
+            assertEquals(expected, roundabout);
+            assertEquals(expected.getNodesCount(), roundabout.getNodesCount());
+            for (Node n1 : roundabout.getNodes()) {
+                Node n2 = (Node) ds2.getPrimitiveById(n1);
+                assertEquals(n1.lat(), n2.lat(), 1e-5);
+                assertEquals(n1.lon(), n2.lon(), 1e-5);
+            }
+        }
+    }
+
+    /**
+     * Non-regression test for <a href="https://josm.openstreetmap.de/ticket/20041">Bug #20041</a>.
+     * Don't create move commands when no node is visibly moved
+     * @throws Exception if an error occurs
+     */
+    @Test
+    void testTicket20041() throws Exception {
+        DataSet ds = OsmReader.parseDataSet(Files.newInputStream(Paths.get(TestUtils.getTestDataRoot(), "alignCircleAfter1.osm")), null);
+
+        Way roundabout = null;
+        for (Way w : ds.getWays()) {
+            if ("roundabout".equals(w.get("junction"))) {
+                roundabout = w;
+                break;
+            }
+        }
+        assertNotNull(roundabout);
+        if (roundabout != null) {
+            ds.setSelected(roundabout);
+            assertThrows(AlignInCircleAction.InvalidSelection.class, () -> AlignInCircleAction.buildCommand(ds));
+        }
+    }
+
+    /**
+     * Test case: way with several nodes selected
+     * @throws Exception if an error occurs
+     */
+    @Test
+    void testNodesSelected() throws Exception {
+        DataSet ds = OsmReader.parseDataSet(Files.newInputStream(Paths.get(TestUtils.getTestDataRoot(), "alignCircleBefore.osm")), null);
+        DataSet ds2 = OsmReader.parseDataSet(Files.newInputStream(Paths.get(TestUtils.getTestDataRoot(), "alignCircleAfter2.osm")), null);
+
+        Way circularWay = null;
+        for (Way w : ds.getWays()) {
+            if ("roundabout".equals(w.get("junction"))) {
+                circularWay = w;
+                break;
+            }
+        }
+        assertNotNull(circularWay);
+        if (circularWay != null) {
+            ds.setSelected(circularWay.getNodes());
+            Command c = AlignInCircleAction.buildCommand(ds);
+            assertNotNull(c);
+            c.executeCommand();
+            Way expected = (Way) ds2.getPrimitiveById(circularWay);
+            assertNotNull(expected);
+            assertEquals(expected, circularWay);
+            assertEquals(expected.getNodesCount(), circularWay.getNodesCount());
+            for (Node n1 : circularWay.getNodes()) {
+                Node n2 = (Node) ds2.getPrimitiveById(n1);
+                assertEquals(n1.lat(), n2.lat(), 1e-5);
+                assertEquals(n1.lon(), n2.lon(), 1e-5);
+            }
+        }
+    }
+
+    /**
+     * Test case: original roundabout was split, two ways selected, they build a closed ring
+     * @throws Exception if an error occurs
+     */
+    @Test
+    void testOpenWaysSelected() throws Exception {
+        DataSet ds = OsmReader.parseDataSet(Files.newInputStream(Paths.get(TestUtils.getTestDataRoot(), "alignCircleTwoWaysBefore.osm")), null);
+        DataSet ds2 = OsmReader.parseDataSet(Files.newInputStream(Paths.get(TestUtils.getTestDataRoot(), "alignCircleTwoWaysAfter.osm")), null);
+
+        Set<Way> junctions = ds.getWays().stream().filter(w -> "roundabout".equals(w.get("junction"))).collect(Collectors.toSet());
+        assertEquals(2, junctions.size());
+        ds.setSelected(junctions);
+        Command c = AlignInCircleAction.buildCommand(ds);
+        assertNotNull(c);
+        c.executeCommand();
+        for (Way way : junctions) {
+            for (Node n1 : way.getNodes()) {
+                Node n2 = (Node) ds2.getPrimitiveById(n1);
+                assertEquals(n1.lat(), n2.lat(), 1e-5);
+                assertEquals(n1.lon(), n2.lon(), 1e-5);
+            }
+        }
+    }
+
+}
