Index: /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/CollectionUtils.java
===================================================================
--- /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/CollectionUtils.java	(revision 25782)
+++ /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/CollectionUtils.java	(revision 25783)
@@ -1,7 +1,11 @@
 package org.openstreetmap.josm.plugins.turnlanes;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.ListIterator;
+import java.util.Set;
 
 public class CollectionUtils {
@@ -31,3 +35,23 @@
 		};
 	}
+	
+	public static <E> Set<E> toSet(Iterable<? extends E> iterable) {
+		final Set<E> set = new HashSet<E>();
+		
+		for (E e : iterable) {
+			set.add(e);
+		}
+		
+		return Collections.unmodifiableSet(set);
+	}
+	
+	public static <E> List<E> toList(Iterable<? extends E> iterable) {
+		final List<E> list = new ArrayList<E>();
+		
+		for (E e : iterable) {
+			list.add(e);
+		}
+		
+		return Collections.unmodifiableList(list);
+	}
 }
Index: /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/gui/GuiContainer.java
===================================================================
--- /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/gui/GuiContainer.java	(revision 25782)
+++ /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/gui/GuiContainer.java	(revision 25783)
@@ -1,3 +1,6 @@
 package org.openstreetmap.josm.plugins.turnlanes.gui;
+
+import static java.lang.Math.sqrt;
+import static org.openstreetmap.josm.plugins.turnlanes.gui.GuiUtil.locs;
 
 import java.awt.BasicStroke;
@@ -5,9 +8,17 @@
 import java.awt.Stroke;
 import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.plugins.turnlanes.model.Junction;
 import org.openstreetmap.josm.plugins.turnlanes.model.Lane;
+import org.openstreetmap.josm.plugins.turnlanes.model.ModelContainer;
 import org.openstreetmap.josm.plugins.turnlanes.model.Road;
 
@@ -16,4 +27,6 @@
 	static final Color GREEN = new Color(66, 234, 108);
 	
+	private final ModelContainer mc;
+	
 	private final Point2D translation;
 	/**
@@ -21,8 +34,4 @@
 	 */
 	private final double mpp;
-	/**
-	 * Meters per source unit.
-	 */
-	private final double mpsu;
 	private final double scale;
 	private final double laneWidth;
@@ -33,12 +42,36 @@
 	private final Stroke connectionStroke;
 	
-	public GuiContainer(Point2D origin, double mpsu) {
+	public GuiContainer(ModelContainer mc) {
+		final Point2D origin = avgOrigin(locs(mc.getPrimaryJunctions()));
+		
+		final LatLon originCoor = Main.proj.eastNorth2latlon(new EastNorth(origin.getX(), origin.getY()));
+		final LatLon relCoor = Main.proj.eastNorth2latlon(new EastNorth(origin.getX() + 1, origin.getY() + 1));
+		
+		// meters per source unit
+		final double mpsu = relCoor.greatCircleDistance(originCoor) / sqrt(2);
+		
+		this.mc = mc;
 		this.translation = new Point2D.Double(-origin.getX(), -origin.getY());
 		this.mpp = 0.2;
-		this.mpsu = mpsu;
 		this.scale = mpsu / mpp;
 		this.laneWidth = 2 / mpp;
 		
 		this.connectionStroke = new BasicStroke((float) (laneWidth / 4), BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
+		
+		for (Junction j : mc.getPrimaryJunctions()) {
+			getGui(j);
+		}
+	}
+	
+	private static Point2D avgOrigin(List<Point2D> locs) {
+		double x = 0;
+		double y = 0;
+		
+		for (Point2D l : locs) {
+			x += l.getX();
+			y += l.getY();
+		}
+		
+		return new Point2D.Double(x / locs.size(), y / locs.size());
 	}
 	
@@ -105,6 +138,43 @@
 	}
 	
-	public GuiContainer empty() {
-		return new GuiContainer(new Point2D.Double(-translation.getX(), -translation.getY()), mpsu);
+	public ModelContainer getModel() {
+		return mc;
+	}
+	
+	public Rectangle2D getBounds() {
+		final List<Junction> primaries = new ArrayList<Junction>(mc.getPrimaryJunctions());
+		final List<Double> top = new ArrayList<Double>();
+		final List<Double> left = new ArrayList<Double>();
+		final List<Double> right = new ArrayList<Double>();
+		final List<Double> bottom = new ArrayList<Double>();
+		
+		for (Junction j : primaries) {
+			final JunctionGui g = getGui(j);
+			final Rectangle2D b = g.getBounds();
+			
+			top.add(b.getMinY());
+			left.add(b.getMinX());
+			right.add(b.getMaxX());
+			bottom.add(b.getMaxY());
+		}
+		
+		final double t = Collections.min(top);
+		final double l = Collections.min(left);
+		final double r = Collections.max(right);
+		final double b = Collections.max(bottom);
+		
+		return new Rectangle2D.Double(l, t, r - l, b - t);
+	}
+	
+	public GuiContainer recalculate() {
+		return new GuiContainer(mc.recalculate());
+	}
+	
+	public Iterable<RoadGui> getRoads() {
+		return roads.values();
+	}
+	
+	public Iterable<JunctionGui> getJunctions() {
+		return junctions.values();
 	}
 }
Index: /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/gui/GuiUtil.java
===================================================================
--- /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/gui/GuiUtil.java	(revision 25782)
+++ /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/gui/GuiUtil.java	(revision 25783)
@@ -14,4 +14,5 @@
 import org.openstreetmap.josm.data.coor.EastNorth;
 import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.plugins.turnlanes.model.Junction;
 
 class GuiUtil {
@@ -131,9 +132,9 @@
 	}
 	
-	public static List<Point2D> locs(Iterable<Node> nodes) {
+	public static List<Point2D> locs(Iterable<Junction> junctions) {
 		final List<Point2D> locs = new ArrayList<Point2D>();
 		
-		for (Node n : nodes) {
-			locs.add(loc(n));
+		for (Junction j : junctions) {
+			locs.add(loc(j.getNode()));
 		}
 		
Index: /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/gui/InteractiveElement.java
===================================================================
--- /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/gui/InteractiveElement.java	(revision 25782)
+++ /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/gui/InteractiveElement.java	(revision 25783)
@@ -11,4 +11,5 @@
 		Type LANE_ADDER = new Type() {};
 		Type EXTENDER = new Type() {};
+		Type VIA_CONNECTOR = new Type() {};
 	}
 	
Index: /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/gui/JunctionGui.java
===================================================================
--- /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/gui/JunctionGui.java	(revision 25782)
+++ /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/gui/JunctionGui.java	(revision 25783)
@@ -3,4 +3,5 @@
 import static java.lang.Math.PI;
 import static java.lang.Math.abs;
+import static java.lang.Math.hypot;
 import static java.lang.Math.max;
 import static java.lang.Math.tan;
@@ -16,6 +17,8 @@
 import java.awt.Color;
 import java.awt.Graphics2D;
+import java.awt.geom.FlatteningPathIterator;
 import java.awt.geom.Line2D;
 import java.awt.geom.Path2D;
+import java.awt.geom.PathIterator;
 import java.awt.geom.Point2D;
 import java.awt.geom.Rectangle2D;
@@ -37,9 +40,7 @@
 		private final Turn turn;
 		
-		private final Line2D line = new Line2D.Double();
-		
 		private Point2D dragBegin;
-		
-		private Point2D dragOffset = new Point2D.Double();
+		private double dragOffsetX = 0;
+		private double dragOffsetY = 0;
 		
 		public TurnConnection(Turn turn) {
@@ -50,20 +51,44 @@
 		void paint(Graphics2D g2d, State state) {
 			if (isVisible(state)) {
-				final LaneGui laneGui = getContainer().getGui(turn.getFrom());
-				final RoadGui roadGui = getContainer().getGui(turn.getTo().getRoad());
+				g2d.setStroke(getContainer().getConnectionStroke());
+				g2d.setColor(isRemoveDragOffset() ? GuiContainer.RED : GuiContainer.GREEN);
+				g2d.translate(dragOffsetX, dragOffsetY);
+				g2d.draw(getPath());
+				g2d.translate(-dragOffsetX, -dragOffsetY);
+			}
+		}
+		
+		private Path2D getPath() {
+			final Path2D path = new Path2D.Double();
+			
+			final LaneGui laneGui = getContainer().getGui(turn.getFrom());
+			final RoadGui roadGui = getContainer().getGui(turn.getTo().getRoad());
+			
+			path.moveTo(laneGui.outgoing.getCenter().getX(), laneGui.outgoing.getCenter().getY());
+			
+			Junction j = laneGui.getModel().getOutgoingJunction();
+			for (Road v : turn.getVia()) {
+				final PathIterator it;
+				if (v.getFromEnd().getJunction().equals(j)) {
+					it = getContainer().getGui(v).getLaneMiddle(true).getIterator();
+					j = v.getToEnd().getJunction();
+				} else {
+					it = getContainer().getGui(v).getLaneMiddle(false).getIterator();
+					j = v.getFromEnd().getJunction();
+				}
 				
-				g2d.setStroke(getContainer().getConnectionStroke());
-				
-				g2d.setColor(isRemoveDragOffset() ? GuiContainer.RED : GuiContainer.GREEN);
-				
-				line.setLine(laneGui.outgoing.getCenter(), roadGui.getConnector(turn.getTo()).getCenter());
-				line.setLine(line.getX1() + dragOffset.getX(), line.getY1() + dragOffset.getY(),
-				    line.getX2() + dragOffset.getX(), line.getY2() + dragOffset.getY());
-				g2d.draw(line);
-			}
+				path.append(it, true);
+			}
+			
+			path.lineTo(roadGui.getConnector(turn.getTo()).getCenter().getX(), roadGui.getConnector(turn.getTo()).getCenter()
+			    .getY());
+			
+			return path;
 		}
 		
 		private boolean isVisible(State state) {
-			if (state instanceof State.OutgoingActive) {
+			if (state instanceof State.AllTurns) {
+				return true;
+			} else if (state instanceof State.OutgoingActive) {
 				return turn.getFrom().equals(((State.OutgoingActive) state).getLane().getModel());
 			} else if (state instanceof State.IncomingActive) {
@@ -76,6 +101,28 @@
 		@Override
 		boolean contains(Point2D p, State state) {
-			final Point2D closest = closest(line, p);
-			return p.distance(closest) <= strokeWidth() / 2;
+			if (!isVisible(state)) {
+				return false;
+			}
+			
+			final PathIterator it = new FlatteningPathIterator(getPath().getPathIterator(null), 0.05 / getContainer()
+			    .getMpp());
+			final double[] coords = new double[6];
+			double lastX = 0;
+			double lastY = 0;
+			while (!it.isDone()) {
+				if (it.currentSegment(coords) == PathIterator.SEG_LINETO) {
+					final Point2D closest = closest(new Line2D.Double(lastX, lastY, coords[0], coords[1]), p);
+					
+					if (p.distance(closest) <= strokeWidth() / 2) {
+						return true;
+					}
+				}
+				
+				lastX = coords[0];
+				lastY = coords[1];
+				it.next();
+			}
+			
+			return false;
 		}
 		
@@ -98,5 +145,6 @@
 		boolean beginDrag(double x, double y) {
 			dragBegin = new Point2D.Double(x, y);
-			dragOffset = new Point2D.Double();
+			dragOffsetX = 0;
+			dragOffsetY = 0;
 			return true;
 		}
@@ -104,5 +152,6 @@
 		@Override
 		State drag(double x, double y, InteractiveElement target, State old) {
-			dragOffset = new Point2D.Double(x - dragBegin.getX(), y - dragBegin.getY());
+			dragOffsetX = x - dragBegin.getX();
+			dragOffsetY = y - dragBegin.getY();
 			return old;
 		}
@@ -113,8 +162,10 @@
 			
 			if (isRemoveDragOffset()) {
-				getModel().removeTurn(turn);
-			}
-			
-			dragOffset = new Point2D.Double();
+				turn.remove();
+			}
+			
+			dragBegin = null;
+			dragOffsetX = 0;
+			dragOffsetY = 0;
 			return new State.Dirty(old);
 		}
@@ -123,5 +174,5 @@
 			final double r = getContainer().getGui(turn.getFrom().getRoad()).connectorRadius;
 			final double max = r - strokeWidth() / 2;
-			return dragOffset.distance(0, 0) > max;
+			return hypot(dragOffsetX, dragOffsetY) > max;
 		}
 	}
@@ -386,6 +437,10 @@
 		final List<InteractiveElement> result = new ArrayList<InteractiveElement>();
 		
-		for (Turn t : getModel().getTurns()) {
-			result.add(new TurnConnection(t));
+		if (getModel().isPrimary()) {
+			for (Road.End r : new HashSet<Road.End>(getModel().getRoadEnds())) {
+				for (Turn t : r.getTurns()) {
+					result.add(new TurnConnection(t));
+				}
+			}
 		}
 		
Index: /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/gui/JunctionPane.java
===================================================================
--- /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/gui/JunctionPane.java	(revision 25782)
+++ /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/gui/JunctionPane.java	(revision 25783)
@@ -6,4 +6,7 @@
 import java.awt.Graphics2D;
 import java.awt.RenderingHints;
+import java.awt.event.ActionEvent;
+import java.awt.event.InputEvent;
+import java.awt.event.KeyEvent;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
@@ -19,8 +22,7 @@
 import java.util.TreeMap;
 
+import javax.swing.AbstractAction;
 import javax.swing.JComponent;
-
-import org.openstreetmap.josm.data.osm.Node;
-import org.openstreetmap.josm.plugins.turnlanes.model.ModelContainer;
+import javax.swing.KeyStroke;
 
 class JunctionPane extends JComponent {
@@ -31,4 +33,5 @@
 		
 		public void mousePressed(MouseEvent e) {
+			setFocusable(true);
 			button = e.getButton();
 			
@@ -76,5 +79,4 @@
 					if (ie.contains(mouse, state)) {
 						setState(ie.click(state));
-						repaint();
 						break;
 					}
@@ -103,6 +105,4 @@
 					setState(dragging.drag(mouse.getX(), mouse.getY(), dropTarget(mouse), state));
 				}
-				
-				repaint();
 			} else if (button == MouseEvent.BUTTON3) {
 				translate(e.getX() - originX, e.getY() - originY);
@@ -133,5 +133,5 @@
 	private final MouseInputProcessor mip = new MouseInputProcessor();
 	
-	private JunctionGui junction;
+	private GuiContainer container;
 	
 	private int width = 0;
@@ -148,9 +148,61 @@
 	private InteractiveElement dragging;
 	
-	public JunctionPane(JunctionGui junction) {
-		setJunction(junction);
-	}
-	
-	public void setJunction(JunctionGui junction) {
+	public JunctionPane(GuiContainer container) {
+		setJunction(container);
+		
+		getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_F5, 0), "refresh");
+		getActionMap().put("refresh", new AbstractAction() {
+			private static final long serialVersionUID = 1L;
+			
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				setState(new State.Invalid(state));
+			}
+		});
+		
+		getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, 0), "zoomIn");
+		getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ADD, 0), "zoomIn");
+		getActionMap().put("zoomIn", new AbstractAction() {
+			private static final long serialVersionUID = 1L;
+			
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				scale(Math.pow(0.8, -1));
+			}
+		});
+		
+		getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, 0), "zoomOut");
+		getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_SUBTRACT, 0), "zoomOut");
+		getActionMap().put("zoomOut", new AbstractAction() {
+			private static final long serialVersionUID = 1L;
+			
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				scale(Math.pow(0.8, 1));
+			}
+		});
+		
+		getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), "center");
+		getActionMap().put("center", new AbstractAction() {
+			private static final long serialVersionUID = 1L;
+			
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				center();
+			}
+		});
+		
+		getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_A, InputEvent.CTRL_DOWN_MASK), "toggleAllTurns");
+		getActionMap().put("toggleAllTurns", new AbstractAction() {
+			private static final long serialVersionUID = 1L;
+			
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				toggleAllTurns();
+			}
+		});
+	}
+	
+	public void setJunction(GuiContainer container) {
 		removeMouseListener(mip);
 		removeMouseMotionListener(mip);
@@ -158,21 +210,12 @@
 		interactives.clear();
 		dragging = null;
-		this.junction = junction;
-		
-		dirty = true;
-		repaint();
-		
-		if (junction == null) {
+		this.container = container;
+		
+		if (container == null) {
 			this.state = null;
 		} else {
-			this.state = new State.Default(junction);
-			
-			final Rectangle2D bounds = junction.getBounds();
-			scale = Math.min(getHeight() / 2 / bounds.getHeight(), getWidth() / 2 / bounds.getWidth());
-			
-			translationX = -bounds.getCenterX();
-			translationY = -bounds.getCenterY();
-			
-			translate(getWidth() / 2d, getHeight() / 2d);
+			setState(new State.Dirty(new State.Default()));
+			
+			center();
 			
 			addMouseListener(mip);
@@ -182,14 +225,30 @@
 	}
 	
+	private void center() {
+		final Rectangle2D bounds = container.getBounds();
+		scale = Math.min(getHeight() / 2 / bounds.getHeight(), getWidth() / 2 / bounds.getWidth());
+		
+		translationX = -bounds.getCenterX();
+		translationY = -bounds.getCenterY();
+		
+		translate(getWidth() / 2d, getHeight() / 2d);
+	}
+	
+	private void toggleAllTurns() {
+		if (state instanceof State.AllTurns) {
+			setState(((State.AllTurns) state).unwrap());
+		} else {
+			setState(new State.AllTurns(state));
+		}
+	}
+	
 	private void setState(State state) {
-		if (state instanceof State.Invalid) {
-			final Node n = junction.getModel().getNode();
-			final ModelContainer m = ModelContainer.create(n);
-			
-			final GuiContainer c = junction.getContainer().empty();
-			junction = c.getGui(m.getJunction(n));
-			
+		if (state instanceof State.AllTurns) {
 			dirty = true;
-			this.state = new State.Default(junction);
+			this.state = state;
+		} else if (state instanceof State.Invalid) {
+			container = container.recalculate();
+			dirty = true;
+			this.state = new State.Default();
 		} else if (state instanceof State.Dirty) {
 			dirty = true;
@@ -198,4 +257,6 @@
 			this.state = state;
 		}
+		
+		repaint();
 	}
 	
@@ -211,4 +272,8 @@
 		dirty = true;
 		repaint();
+	}
+	
+	void scale(double scale) {
+		scale(getWidth() / 2, getHeight() / 2, scale);
 	}
 	
@@ -233,5 +298,5 @@
 		}
 		
-		if (junction == null) {
+		if (container == null) {
 			super.paintComponent(g);
 			return;
@@ -295,11 +360,12 @@
 		
 		g2d.setColor(Color.GRAY);
-		for (RoadGui r : junction.getRoads()) {
+		for (RoadGui r : container.getRoads()) {
 			addAllInteractives(r.paint(g2d));
 		}
 		
-		addAllInteractives(junction.paint(g2d));
-		
-		dot(g2d, new Point2D.Double(junction.x, junction.y), junction.getContainer().getLaneWidth() / 5);
+		for (JunctionGui j : container.getJunctions()) {
+			addAllInteractives(j.paint(g2d));
+			dot(g2d, new Point2D.Double(j.x, j.y), container.getLaneWidth() / 5);
+		}
 	}
 	
Index: /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/gui/LaneGui.java
===================================================================
--- /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/gui/LaneGui.java	(revision 25782)
+++ /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/gui/LaneGui.java	(revision 25783)
@@ -11,15 +11,16 @@
 import java.awt.Shape;
 import java.awt.geom.Ellipse2D;
-import java.awt.geom.Line2D;
 import java.awt.geom.Path2D;
+import java.awt.geom.PathIterator;
 import java.awt.geom.Point2D;
 import java.awt.geom.Rectangle2D;
 import java.text.DecimalFormat;
 import java.text.NumberFormat;
+import java.util.ArrayList;
 import java.util.List;
 
 import org.openstreetmap.josm.plugins.turnlanes.gui.RoadGui.IncomingConnector;
-import org.openstreetmap.josm.plugins.turnlanes.model.Junction;
 import org.openstreetmap.josm.plugins.turnlanes.model.Lane;
+import org.openstreetmap.josm.plugins.turnlanes.model.Road;
 
 final class LaneGui {
@@ -79,5 +80,5 @@
 			
 			final double offset = getRoad().getOffset(x, y);
-			final double newLength = getModel().isReverse() ? offset : getRoad().getLength() - offset;
+			final double newLength = getModel().getOutgoingRoadEnd().isFromEnd() ? offset : getRoad().getLength() - offset;
 			if (newLength > 0) {
 				getModel().setLength(newLength * getRoad().getContainer().getMpp());
@@ -125,7 +126,29 @@
 			
 			if (dragLocation != null) {
+				final State.Connecting s = (State.Connecting) state;
+				final Path2D path = new Path2D.Double();
+				path.moveTo(center.getX(), center.getY());
+				
+				final List<RoadGui.ViaConnector> vias = s.getViaConnectors();
+				for (int i = 0; i < vias.size() - 1; i += 2) {
+					final RoadGui.ViaConnector v = vias.get(i);
+					final PathIterator it = v.getRoad().getLaneMiddle(v.getRoadEnd().isFromEnd()).getIterator();
+					path.append(it, true);
+				}
+				if ((vias.size() & 1) != 0) {
+					final RoadGui.ViaConnector last = vias.get(vias.size() - 1);
+					path.lineTo(last.getCenter().getX(), last.getCenter().getY());
+				}
+				
+				if (dropTarget == null) {
+					g2d.setColor(GuiContainer.RED);
+					path.lineTo(dragLocation.getX(), dragLocation.getY());
+				} else {
+					g2d.setColor(GuiContainer.GREEN);
+					path.lineTo(dropTarget.getCenter().getX(), dropTarget.getCenter().getY());
+				}
+				
 				g2d.setStroke(getContainer().getConnectionStroke());
-				g2d.setColor(dropTarget == null ? GuiContainer.RED : GuiContainer.GREEN);
-				g2d.draw(new Line2D.Double(getCenter(), dropTarget == null ? dragLocation : dropTarget.getCenter()));
+				g2d.draw(path);
 			}
 		}
@@ -150,5 +173,9 @@
 		
 		private boolean isVisible(State state) {
-			return getModel().getOutgoingJunction().equals(state.getJunction().getModel());
+			if (state instanceof State.Connecting) {
+				return ((State.Connecting) state).getLane().equals(getModel());
+			}
+			
+			return !getRoad().getModel().isPrimary() && getModel().getOutgoingJunction().isPrimary();
 		}
 		
@@ -165,5 +192,5 @@
 		@Override
 		public State activate(State old) {
-			return new State.OutgoingActive(old.getJunction(), LaneGui.this);
+			return new State.OutgoingActive(LaneGui.this);
 		}
 		
@@ -174,25 +201,44 @@
 		
 		@Override
-		State drag(double x, double y, InteractiveElement target, State old) {
+		State.Connecting drag(double x, double y, InteractiveElement target, State old) {
 			dragLocation = new Point2D.Double(x, y);
-			dropTarget = target != null && target.getType() == Type.INCOMING_CONNECTOR ? (IncomingConnector) target : null;
-			return old;
+			dropTarget = null;
+			
+			if (!(old instanceof State.Connecting)) {
+				return new State.Connecting(getModel());
+			}
+			
+			final State.Connecting s = (State.Connecting) old;
+			if (target != null && target.getType() == Type.INCOMING_CONNECTOR) {
+				dropTarget = (IncomingConnector) target;
+				
+				return (s.getViaConnectors().size() & 1) == 0 ? s : s.pop();
+			} else if (target != null && target.getType() == Type.VIA_CONNECTOR) {
+				return s.next((RoadGui.ViaConnector) target);
+			}
+			
+			return s;
 		}
 		
 		@Override
 		State drop(double x, double y, InteractiveElement target, State old) {
-			drag(x, y, target, old);
+			final State.Connecting s = drag(x, y, target, old);
 			dragLocation = null;
-			
 			if (dropTarget == null) {
-				return old;
-			}
-			
-			final Junction j = (getModel().isReverse() ? getRoad().getA() : getRoad().getB()).getModel();
-			
-			j.addTurn(getModel(), dropTarget.getRoadEnd());
-			
+				return activate(old);
+			}
+			
+			final List<Road> via = new ArrayList<Road>();
+			assert (s.getViaConnectors().size() & 1) == 0;
+			for (int i = 0; i < s.getViaConnectors().size(); i += 2) {
+				final RoadGui.ViaConnector a = s.getViaConnectors().get(i);
+				final RoadGui.ViaConnector b = s.getViaConnectors().get(i + 1);
+				assert a.getRoadEnd().getOppositeEnd().equals(b);
+				via.add(a.getRoadEnd().getRoad());
+			}
+			
+			getModel().addTurn(via, dropTarget.getRoadEnd());
 			dropTarget = null;
-			return new State.Dirty(old);
+			return new State.Dirty(activate(old));
 		}
 		
@@ -256,10 +302,8 @@
 		final double WW = 3 / getContainer().getMpp();
 		
-		final List<LaneGui> lanes = getRoad().getLanes();
-		final int i = lanes.indexOf(this);
-		final LaneGui left = getModel().isReverse() ? (i < lanes.size() - 1 ? lanes.get(i + 1) : null) : (i > 0 ? lanes
-		    .get(i - 1) : null);
+		final LaneGui left = left();
 		final Lane leftModel = left == null ? null : left.getModel();
-		final double leftLength = leftModel == null || leftModel.isReverse() != getModel().isReverse() ? Double.NEGATIVE_INFINITY
+		final double leftLength = leftModel == null
+		    || !leftModel.getOutgoingRoadEnd().equals(getModel().getOutgoingRoadEnd()) ? Double.NEGATIVE_INFINITY
 		    : leftModel.getKind() == Lane.Kind.EXTRA_LEFT ? left.getLength() : L;
 		
@@ -276,5 +320,5 @@
 			if (L > leftLength) {
 				innerLine.append(inner.subpath(max(0, leftLength + WW), L).getIterator(), leftLength >= 0
-				    || getModel().isReverse());
+				    || getModel().getOutgoingRoadEnd().isFromEnd());
 				final Point2D op = outer.getPoint(L + WW);
 				innerLine.lineTo(op.getX(), op.getY());
@@ -291,9 +335,15 @@
 			if (leftLength < L) {
 				innerLine.append(inner.subpath(max(0, leftLength + WW), L).getIterator(), leftLength >= 0
-				    || getModel().isReverse());
+				    || getModel().getOutgoingRoadEnd().isFromEnd());
 			}
 		}
 		
 		return outer;
+	}
+	
+	private LaneGui left() {
+		final List<LaneGui> lanes = getRoad().getLanes(getModel().getOutgoingRoadEnd());
+		final int i = lanes.indexOf(this);
+		return i > 0 ? lanes.get(i - 1) : null;
 	}
 	
Index: /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/gui/RoadGui.java
===================================================================
--- /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/gui/RoadGui.java	(revision 25782)
+++ /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/gui/RoadGui.java	(revision 25783)
@@ -29,4 +29,5 @@
 import java.awt.geom.Rectangle2D;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
@@ -34,7 +35,5 @@
 import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
-import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
 import org.openstreetmap.josm.data.osm.Way;
-import org.openstreetmap.josm.plugins.turnlanes.CollectionUtils;
 import org.openstreetmap.josm.plugins.turnlanes.model.Lane;
 import org.openstreetmap.josm.plugins.turnlanes.model.Road;
@@ -42,10 +41,87 @@
 
 class RoadGui {
+	final class ViaConnector extends InteractiveElement {
+		private final Road.End end;
+		
+		private final Line2D line;
+		private final float strokeWidth;
+		
+		public ViaConnector(Road.End end) {
+			this.end = end;
+			this.line = new Line2D.Double(getLeftCorner(end), getRightCorner(end));
+			this.strokeWidth = (float) (3 * getContainer().getLaneWidth() / 4);
+		}
+		
+		@Override
+		void paint(Graphics2D g2d, State state) {
+			if (isVisible(state)) {
+				g2d.setStroke(new BasicStroke(strokeWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER));
+				g2d.setColor(Color.ORANGE);
+				g2d.draw(line);
+			}
+		}
+		
+		@Override
+		boolean contains(Point2D p, State state) {
+			if (!isVisible(state)) {
+				return false;
+			}
+			
+			final Point2D closest = closest(line, p);
+			return p.distance(closest) <= strokeWidth / 2;
+		}
+		
+		private boolean isVisible(State state) {
+			if (!(state instanceof State.Connecting)) {
+				return false;
+			}
+			
+			final State.Connecting s = (State.Connecting) state;
+			
+			if (s.getJunction().equals(end.getJunction()) || equals(s.getBacktrackViaConnector())) {
+				return true;
+			} else if (!s.getViaConnectors().isEmpty()
+			    && s.getViaConnectors().get(s.getViaConnectors().size() - 1).getRoadModel().equals(getRoadModel())) {
+				return true;
+			}
+			
+			return false;
+		}
+		
+		private Road getRoadModel() {
+			return getModel();
+		}
+		
+		public RoadGui getRoad() {
+			return RoadGui.this;
+		}
+		
+		@Override
+		Type getType() {
+			return Type.VIA_CONNECTOR;
+		}
+		
+		@Override
+		int getZIndex() {
+			return 1;
+		}
+		
+		public Road.End getRoadEnd() {
+			return end;
+		}
+		
+		public Point2D getCenter() {
+			return relativePoint(line.getP1(), line.getP1().distance(line.getP2()) / 2, angle(line.getP1(), line.getP2()));
+		}
+	}
+	
 	private final class Extender extends InteractiveElement {
+		private final Road.End end;
 		private final Way way;
 		
 		private final Line2D line;
 		
-		public Extender(Way way, double angle) {
+		public Extender(Road.End end, Way way, double angle) {
+			this.end = end;
 			this.way = way;
 			this.line = new Line2D.Double(a.getPoint(), relativePoint(a.getPoint(), getContainer().getLaneWidth() * 4, angle));
@@ -70,5 +146,5 @@
 		@Override
 		State click(State old) {
-			getModel().extend(way);
+			end.extend(way);
 			return new State.Invalid(old);
 		}
@@ -83,17 +159,16 @@
 			return 0;
 		}
-		
 	}
 	
 	private final class LaneAdder extends InteractiveElement {
 		private final Road.End end;
-		private final boolean left;
+		private final Lane.Kind kind;
 		
 		private final Point2D center;
 		private final Ellipse2D background;
 		
-		public LaneAdder(Road.End end, boolean left) {
+		public LaneAdder(Road.End end, Lane.Kind kind) {
 			this.end = end;
-			this.left = left;
+			this.kind = kind;
 			
 			final double a = getAngle(end) + PI;
@@ -104,5 +179,5 @@
 			final double cx;
 			final double cy;
-			if (left) {
+			if (kind == Lane.Kind.EXTRA_LEFT) {
 				final JunctionGui j = getContainer().getGui(end.getJunction());
 				final Point2D i = intersection(line(j.getPoint(), a), new Line2D.Double(lc, rc));
@@ -139,5 +214,5 @@
 		
 		private boolean isVisible(State state) {
-			return end.getJunction().equals(state.getJunction().getModel());
+			return end.getJunction().isPrimary();
 		}
 		
@@ -159,5 +234,5 @@
 		@Override
 		public State click(State old) {
-			end.addExtraLane(left);
+			end.addLane(kind);
 			return new State.Invalid(old);
 		}
@@ -165,12 +240,18 @@
 	
 	final class IncomingConnector extends InteractiveElement {
-		private final boolean forward;
+		private final Road.End end;
+		private final List<LaneGui> lanes;
+		
 		private final Point2D center = new Point2D.Double();
 		private final Ellipse2D circle = new Ellipse2D.Double();
 		
-		private final List<LaneGui> lanes = new ArrayList<LaneGui>();
-		
-		private IncomingConnector(boolean forward) {
-			this.forward = forward;
+		private IncomingConnector(Road.End end) {
+			this.end = end;
+			
+			final List<LaneGui> lanes = new ArrayList<LaneGui>(end.getLanes().size());
+			for (Lane l : end.getOppositeEnd().getLanes()) {
+				lanes.add(new LaneGui(RoadGui.this, l));
+			}
+			this.lanes = Collections.unmodifiableList(lanes);
 		}
 		
@@ -217,17 +298,14 @@
 		
 		private boolean isVisible(State state) {
-			if (!getRoadEnd().getJunction().equals(state.getJunction().getModel())) {
+			if (getModel().isPrimary() || !getRoadEnd().getJunction().isPrimary()
+			    || getRoadEnd().getOppositeEnd().getLanes().isEmpty()) {
 				return false;
 			}
 			
-			// must be at least one lane in that direction
-			final boolean reverse = getRoadEnd().isToEnd();
-			for (Lane l : getModel().getLanes()) {
-				if (l.isReverse() == reverse) {
-					return true;
-				}
-			}
-			
-			return false;
+			if (state instanceof State.Connecting) {
+				return ((State.Connecting) state).getJunction().equals(getRoadEnd().getJunction());
+			}
+			
+			return true;
 		}
 		
@@ -256,5 +334,5 @@
 		@Override
 		public State activate(State old) {
-			return new State.IncomingActive(old.getJunction(), getRoadEnd());
+			return new State.IncomingActive(getRoadEnd());
 		}
 		
@@ -270,6 +348,10 @@
 		}
 		
-		Road.End getRoadEnd() {
-			return forward ? getModel().getFromEnd() : getModel().getToEnd();
+		public Road.End getRoadEnd() {
+			return end;
+		}
+		
+		public List<LaneGui> getLanes() {
+			return lanes;
 		}
 		
@@ -400,23 +482,25 @@
 	private final double length;
 	
-	private final IncomingConnector incomingA = new IncomingConnector(true);
-	private final IncomingConnector incomingB = new IncomingConnector(false);
+	private final IncomingConnector incomingA;
+	private final IncomingConnector incomingB;
 	
 	private final Road road;
-	private final List<LaneGui> lanes = new ArrayList<LaneGui>();
 	private final List<Segment> segments = new ArrayList<Segment>();
 	
 	final double connectorRadius;
 	
-	public RoadGui(GuiContainer container, Road r) {
+	public RoadGui(GuiContainer container, Road road) {
 		this.container = container;
 		
-		this.a = container.getGui(r.getFromEnd().getJunction());
-		this.b = container.getGui(r.getToEnd().getJunction());
-		
-		this.road = r;
+		this.road = road;
+		
+		this.a = container.getGui(road.getFromEnd().getJunction());
+		this.b = container.getGui(road.getToEnd().getJunction());
+		
+		this.incomingA = new IncomingConnector(road.getFromEnd());
+		this.incomingB = new IncomingConnector(road.getToEnd());
 		
 		final List<Point2D> bends = new ArrayList<Point2D>();
-		final List<Node> nodes = r.getRoute().getNodes();
+		final List<Node> nodes = road.getRoute().getNodes();
 		for (int i = nodes.size() - 2; i > 0; --i) {
 			bends.add(container.translateAndScale(loc(nodes.get(i))));
@@ -425,19 +509,12 @@
 		// they add themselves to this.segments
 		new Segment(b, bends, a);
-		
 		double l = 0;
 		for (Segment s : segments) {
 			l += s.length;
 		}
-		
 		this.length = l;
 		
-		for (Lane lane : r.getLanes()) {
-			final LaneGui gui = new LaneGui(this, lane);
-			lanes.add(gui);
-			(lane.isReverse() ? incomingB : incomingA).add(gui);
-		}
-		
-		this.innerMargin = getLaneCount(true) > 0 && getLaneCount(false) > 0 ? 1 * container.getLaneWidth() / 15 : 0;
+		this.innerMargin = !incomingA.getLanes().isEmpty() && !incomingB.getLanes().isEmpty() ? 1 * container
+		    .getLaneWidth() / 15 : 0;
 		this.outerMargin = container.getLaneWidth() / 6;
 		this.connectorRadius = 3 * container.getLaneWidth() / 8;
@@ -457,12 +534,4 @@
 	}
 	
-	private int laneMiddle() {
-		int i = 0;
-		while (lanes.size() > i && lanes.get(i).getModel().isReverse()) {
-			++i;
-		}
-		return i;
-	}
-	
 	public Line2D getLeftCurb(Road.End end) {
 		return GuiUtil.line(getCorner(end, true), getAngle(end) + PI);
@@ -499,6 +568,6 @@
 		}
 		
-		final int lcForward = getLaneCount(false);
-		final int lcBackward = getLaneCount(true);
+		final int lcForward = incomingA.getLanes().size();
+		final int lcBackward = incomingB.getLanes().size();
 		
 		final double LW = getContainer().getLaneWidth();
@@ -512,22 +581,17 @@
 	}
 	
-	private int getLaneCount(boolean reverse) {
-		int result = 0;
-		
-		for (LaneGui l : lanes) {
-			if (l.getModel().isReverse() == reverse) {
-				++result;
-			}
-		}
-		
-		return result;
-	}
-	
 	List<InteractiveElement> paint(Graphics2D g2d) {
 		final List<InteractiveElement> result = new ArrayList<InteractiveElement>();
 		
 		result.addAll(paintLanes(g2d));
-		result.addAll(laneAdders());
-		result.addAll(extenders());
+		
+		if (getModel().isPrimary()) {
+			result.add(new ViaConnector(getModel().getFromEnd()));
+			result.add(new ViaConnector(getModel().getToEnd()));
+		} else {
+			result.addAll(laneAdders());
+			result.addAll(extenders(getModel().getFromEnd()));
+			result.addAll(extenders(getModel().getToEnd()));
+		}
 		
 		g2d.setColor(Color.RED);
@@ -542,19 +606,12 @@
 		final List<LaneAdder> result = new ArrayList<LaneAdder>(4);
 		
-		boolean to = false;
-		boolean from = false;
-		for (Lane l : getModel().getLanes()) {
-			to = to || (!l.isReverse() && !l.isExtra());
-			from = from || (l.isReverse() && !l.isExtra());
-		}
-		
-		if (to) {
-			result.add(new LaneAdder(getModel().getToEnd(), true));
-			result.add(new LaneAdder(getModel().getToEnd(), false));
-		}
-		
-		if (from) {
-			result.add(new LaneAdder(getModel().getFromEnd(), true));
-			result.add(new LaneAdder(getModel().getFromEnd(), false));
+		if (!incomingA.getLanes().isEmpty()) {
+			result.add(new LaneAdder(getModel().getToEnd(), Lane.Kind.EXTRA_LEFT));
+			result.add(new LaneAdder(getModel().getToEnd(), Lane.Kind.EXTRA_RIGHT));
+		}
+		
+		if (!incomingB.getLanes().isEmpty()) {
+			result.add(new LaneAdder(getModel().getFromEnd(), Lane.Kind.EXTRA_LEFT));
+			result.add(new LaneAdder(getModel().getFromEnd(), Lane.Kind.EXTRA_RIGHT));
 		}
 		
@@ -562,6 +619,6 @@
 	}
 	
-	private List<Extender> extenders() {
-		if (!getModel().isExtendable()) {
+	private List<Extender> extenders(Road.End end) {
+		if (!end.isExtendable()) {
 			return Collections.emptyList();
 		}
@@ -569,19 +626,10 @@
 		final List<Extender> result = new ArrayList<Extender>();
 		
-		final Node n = a.getModel().getNode();
-		for (OsmPrimitive p : n.getReferrers()) {
-			if (p.getType() != OsmPrimitiveType.WAY) {
-				continue;
-			}
-			
-			Way w = (Way) p;
-			if (w.isFirstLastNode(n) && w.getNodesCount() > 1 && Utils.isRoad(w)) {
-				if (getModel().getRoute().getFirstSegment().getWay().equals(w)) {
-					continue;
-				}
-				
+		final Node n = end.getJunction().getNode();
+		for (Way w : OsmPrimitive.getFilteredList(n.getReferrers(), Way.class)) {
+			if (w.getNodesCount() > 1 && !end.getWay().equals(w) && w.isFirstLastNode(n) && Utils.isRoad(w)) {
 				final Node nextNode = w.firstNode().equals(n) ? w.getNode(1) : w.getNode(w.getNodesCount() - 2);
 				final Point2D nextNodeLoc = getContainer().translateAndScale(loc(nextNode));
-				result.add(new Extender(w, angle(a.getPoint(), nextNodeLoc)));
+				result.add(new Extender(end, w, angle(a.getPoint(), nextNodeLoc)));
 			}
 		}
@@ -603,6 +651,6 @@
 		g2d.setStroke(regularStroke);
 		
-		final boolean forward = getLaneCount(false) > 0;
-		final boolean backward = getLaneCount(true) > 0;
+		final boolean forward = !incomingA.getLanes().isEmpty();
+		final boolean backward = !incomingB.getLanes().isEmpty();
 		
 		final Path2D middleArea;
@@ -630,24 +678,20 @@
 		g2d.draw(middleLines);
 		
+		final List<InteractiveElement> result = new ArrayList<InteractiveElement>();
+		
 		moveIncoming(getModel().getFromEnd());
 		moveIncoming(getModel().getToEnd());
-		
-		final int laneMiddle = laneMiddle();
-		for (int i = laneMiddle; i < lanes.size(); ++i) {
-			moveOutgoing(lanes.get(i), i - laneMiddle);
-		}
-		for (int i = laneMiddle - 1; i >= 0; --i) {
-			moveOutgoing(lanes.get(i), laneMiddle - i - 1);
-		}
-		
-		final List<InteractiveElement> result = new ArrayList<InteractiveElement>();
-		
 		result.add(incomingA);
 		result.add(incomingB);
-		for (LaneGui l : lanes) {
-			result.add(l.outgoing);
-			
-			if (l.getModel().isExtra()) {
-				result.add(l.lengthSlider);
+		
+		for (IncomingConnector c : Arrays.asList(incomingA, incomingB)) {
+			int offset = 0;
+			for (LaneGui l : c.getLanes()) {
+				moveOutgoing(l, offset++);
+				
+				result.add(l.outgoing);
+				if (l.getModel().isExtra()) {
+					result.add(l.lengthSlider);
+				}
 			}
 		}
@@ -666,5 +710,5 @@
 		linePaths.add(innerPath);
 		
-		for (LaneGui l : lanes(forward)) {
+		for (LaneGui l : forward ? incomingA.getLanes() : incomingB.getLanes()) {
 			l.setClip(clip);
 			innerPath = l.recalculate(innerPath, middleLines);
@@ -719,8 +763,11 @@
 		final double d = rc.getP1().distance(j.getPoint()) + lc.getP1().distance(j.getPoint());
 		
-		final Point2D r1 = relativePoint(rc.getP1(), 1, angle(lc.getP1(), rc.getP1()));
-		final Point2D r2 = relativePoint(r1, d, angle(rc) + PI);
-		final Point2D l1 = relativePoint(lc.getP1(), 1, angle(rc.getP1(), lc.getP1()));
-		final Point2D l2 = relativePoint(l1, d, angle(lc) + PI);
+		final double cm = 0.01 / getContainer().getMpp(); // 1 centimeter
+		final double rca = angle(rc) + PI;
+		final double lca = angle(lc) + PI;
+		final Point2D r1 = relativePoint(relativePoint(rc.getP1(), 1, angle(lc.getP1(), rc.getP1())), cm, rca);
+		final Point2D r2 = relativePoint(r1, d, rca);
+		final Point2D l1 = relativePoint(relativePoint(lc.getP1(), 1, angle(rc.getP1(), lc.getP1())), cm, lca);
+		final Point2D l2 = relativePoint(l1, d, lca);
 		
 		negativeClip.moveTo(r1.getX(), r1.getY());
@@ -733,8 +780,10 @@
 	}
 	
-	private Iterable<LaneGui> lanes(boolean forward) {
-		final int laneMiddle = laneMiddle();
-		
-		return forward ? lanes.subList(laneMiddle, lanes.size()) : CollectionUtils.reverse(lanes.subList(0, laneMiddle));
+	public Path getLaneMiddle(boolean forward) {
+		final Path mid = middlePath(!forward);
+		final double w = getWidth(forward ? getModel().getFromEnd() : getModel().getToEnd(), true);
+		final double o = (w - outerMargin) / 2;
+		
+		return o > 0 ? mid.offset(-o, -1, -1, -o) : mid;
 	}
 	
@@ -778,4 +827,12 @@
 		
 		lane.outgoing.move(loc.getX(), loc.getY());
+	}
+	
+	public JunctionGui getJunction(Road.End end) {
+		if (!getModel().equals(end.getRoad())) {
+			throw new IllegalArgumentException();
+		}
+		
+		return end.isFromEnd() ? getA() : getB();
 	}
 	
@@ -801,8 +858,4 @@
 	}
 	
-	public List<LaneGui> getLanes() {
-		return Collections.unmodifiableList(lanes);
-	}
-	
 	public double getOffset(double x, double y) {
 		return segments.get(0).getOffset(x, y);
@@ -812,3 +865,16 @@
 		return container;
 	}
+	
+	public List<LaneGui> getLanes() {
+		final List<LaneGui> result = new ArrayList<LaneGui>();
+		
+		result.addAll(incomingB.getLanes());
+		result.addAll(incomingA.getLanes());
+		
+		return Collections.unmodifiableList(result);
+	}
+	
+	public List<LaneGui> getLanes(Road.End end) {
+		return getConnector(end.getOppositeEnd()).getLanes();
+	}
 }
Index: /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/gui/State.java
===================================================================
--- /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/gui/State.java	(revision 25782)
+++ /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/gui/State.java	(revision 25783)
@@ -1,7 +1,94 @@
 package org.openstreetmap.josm.plugins.turnlanes.gui;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.openstreetmap.josm.plugins.turnlanes.gui.RoadGui.ViaConnector;
+import org.openstreetmap.josm.plugins.turnlanes.model.Junction;
+import org.openstreetmap.josm.plugins.turnlanes.model.Lane;
 import org.openstreetmap.josm.plugins.turnlanes.model.Road;
 
 interface State {
+	public class AllTurns implements State {
+		private final State wrapped;
+		
+		public AllTurns(State wrapped) {
+			this.wrapped = wrapped;
+		}
+		
+		public State unwrap() {
+			return wrapped;
+		}
+	}
+	
+	public class Connecting implements State {
+		private final Lane lane;
+		private final List<RoadGui.ViaConnector> vias;
+		
+		public Connecting(Lane lane) {
+			this(lane, Collections.<RoadGui.ViaConnector> emptyList());
+		}
+		
+		public Connecting(Lane lane, List<ViaConnector> vias) {
+			this.lane = lane;
+			this.vias = vias;
+		}
+		
+		public Connecting next(RoadGui.ViaConnector via) {
+			if (vias.isEmpty()) {
+				return new Connecting(lane, Collections.unmodifiableList(Arrays.asList(via)));
+			}
+			
+			final List<RoadGui.ViaConnector> tmp = new ArrayList<RoadGui.ViaConnector>(vias.size() + 1);
+			final boolean even = (vias.size() & 1) == 0;
+			final RoadGui.ViaConnector last = vias.get(vias.size() - 1);
+			
+			if (last.equals(via) || !even && last.getRoadEnd().getJunction().equals(via.getRoadEnd().getJunction())) {
+				return pop().next(via);
+			}
+			
+			if (vias.size() >= 2) {
+				if (lane.getOutgoingJunction().equals(via.getRoadEnd().getJunction())) {
+					return new Connecting(lane);
+				} else if (via.equals(getBacktrackViaConnector())) {
+					return new Connecting(lane, vias.subList(0, vias.size() - 1));
+				}
+			}
+			
+			for (RoadGui.ViaConnector v : vias) {
+				tmp.add(v);
+				
+				if (!(even && v.equals(last)) && v.getRoadEnd().getJunction().equals(via.getRoadEnd().getJunction())) {
+					return new Connecting(lane, Collections.unmodifiableList(tmp));
+				}
+			}
+			
+			tmp.add(via);
+			return new Connecting(lane, Collections.unmodifiableList(tmp));
+		}
+		
+		public Junction getJunction() {
+			return vias.isEmpty() ? lane.getOutgoingJunction() : vias.get(vias.size() - 1).getRoadEnd().getJunction();
+		}
+		
+		public RoadGui.ViaConnector getBacktrackViaConnector() {
+			return vias.size() < 2 ? null : vias.get(vias.size() - 2);
+		}
+		
+		public List<RoadGui.ViaConnector> getViaConnectors() {
+			return vias;
+		}
+		
+		public Lane getLane() {
+			return lane;
+		}
+		
+		public Connecting pop() {
+			return new Connecting(lane, vias.subList(0, vias.size() - 1));
+		}
+	}
+	
 	public class Invalid implements State {
 		private final State wrapped;
@@ -9,8 +96,4 @@
 		public Invalid(State wrapped) {
 			this.wrapped = wrapped;
-		}
-		
-		public JunctionGui getJunction() {
-			return wrapped.getJunction();
 		}
 		
@@ -27,8 +110,4 @@
 		}
 		
-		public JunctionGui getJunction() {
-			return wrapped.getJunction();
-		}
-		
 		public State unwrap() {
 			return wrapped;
@@ -37,21 +116,11 @@
 	
 	class Default implements State {
-		private final JunctionGui junction;
-		
-		public Default(JunctionGui junction) {
-			this.junction = junction;
-		}
-		
-		public JunctionGui getJunction() {
-			return junction;
-		}
+		public Default() {}
 	}
 	
 	class IncomingActive implements State {
-		private final JunctionGui junction;
 		private final Road.End roadEnd;
 		
-		public IncomingActive(JunctionGui junction, Road.End roadEnd) {
-			this.junction = junction;
+		public IncomingActive(Road.End roadEnd) {
 			this.roadEnd = roadEnd;
 		}
@@ -60,17 +129,10 @@
 			return roadEnd;
 		}
-		
-		@Override
-		public JunctionGui getJunction() {
-			return junction;
-		}
 	}
 	
 	class OutgoingActive implements State {
-		private final JunctionGui junction;
 		private final LaneGui lane;
 		
-		public OutgoingActive(JunctionGui junction, LaneGui lane) {
-			this.junction = junction;
+		public OutgoingActive(LaneGui lane) {
 			this.lane = lane;
 		}
@@ -79,11 +141,4 @@
 			return lane;
 		}
-		
-		@Override
-		public JunctionGui getJunction() {
-			return junction;
-		}
 	}
-	
-	JunctionGui getJunction();
 }
Index: /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/gui/TurnLanesDialog.java
===================================================================
--- /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/gui/TurnLanesDialog.java	(revision 25782)
+++ /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/gui/TurnLanesDialog.java	(revision 25783)
@@ -1,5 +1,4 @@
 package org.openstreetmap.josm.plugins.turnlanes.gui;
 
-import static org.openstreetmap.josm.plugins.turnlanes.gui.GuiUtil.loc;
 import static org.openstreetmap.josm.tools.I18n.tr;
 
@@ -9,20 +8,20 @@
 import java.awt.event.ActionEvent;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
 
 import javax.swing.Action;
+import javax.swing.ButtonGroup;
 import javax.swing.JLabel;
 import javax.swing.JPanel;
+import javax.swing.JToggleButton;
 
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.actions.JosmAction;
 import org.openstreetmap.josm.data.SelectionChangedListener;
-import org.openstreetmap.josm.data.coor.EastNorth;
 import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
-import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
-import org.openstreetmap.josm.gui.SideButton;
+import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.gui.dialogs.ToggleDialog;
-import org.openstreetmap.josm.plugins.turnlanes.model.Junction;
 import org.openstreetmap.josm.plugins.turnlanes.model.ModelContainer;
 
@@ -71,41 +70,30 @@
 			@Override
 			public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
-				if (newSelection.size() != 1) {
+				final Collection<OsmPrimitive> s = Collections.unmodifiableCollection(newSelection);
+				final List<Node> nodes = OsmPrimitive.getFilteredList(s, Node.class);
+				final List<Way> ways = OsmPrimitive.getFilteredList(s, Way.class);
+				
+				if (nodes.isEmpty()) {
+					setJunction(null);
 					return;
 				}
 				
-				final OsmPrimitive p = newSelection.iterator().next();
-				
-				if (p.getType() == OsmPrimitiveType.NODE) {
-					final Node n = (Node) p;
-					
-					final ModelContainer mc;
-					final Junction j;
-					
-					try {
-						mc = ModelContainer.create(n);
-						j = mc.getJunction(n);
-					} catch (RuntimeException e) {
-						displayError(e);
-						
-						return;
-					}
-					
-					final EastNorth en = Main.proj.latlon2eastNorth(n.getCoor());
-					final EastNorth rel = new EastNorth(en.getX() + 1, en.getY());
-					
-					// meters per source unit
-					final double mpsu = Main.proj.eastNorth2latlon(rel).greatCircleDistance(n.getCoor());
-					final GuiContainer model = new GuiContainer(loc(n), mpsu);
-					
-					setJunction(model.getGui(j));
+				try {
+					setJunction(ModelContainer.create(nodes, ways));
+				} catch (RuntimeException e) {
+					displayError(e);
+					return;
 				}
 			}
-			
 		});
 		
 		final JPanel buttonPanel = new JPanel(new GridLayout(1, 2, 4, 4));
-		buttonPanel.add(new SideButton(editAction));
-		buttonPanel.add(new SideButton(validateAction));
+		final ButtonGroup group = new ButtonGroup();
+		final JToggleButton editButton = new JToggleButton(editAction);
+		final JToggleButton validateButton = new JToggleButton(validateAction);
+		group.add(editButton);
+		group.add(validateButton);
+		buttonPanel.add(editButton);
+		buttonPanel.add(validateButton);
 		
 		body.setLayout(new CardLayout(4, 4));
@@ -117,10 +105,11 @@
 		body.add(new ValidationPanel(), CARD_VALIDATE);
 		body.add(error, CARD_ERROR);
-		editAction.actionPerformed(null);
+		
+		editButton.doClick();
 	}
 	
 	void displayError(RuntimeException e) {
 		if (editing) {
-			// e.printStackTrace();
+			e.printStackTrace();
 			
 			error.setText("<html>An error occured while constructing the model."
@@ -133,7 +122,7 @@
 	}
 	
-	void setJunction(JunctionGui j) {
-		if (j != null && editing) {
-			junctionPane.setJunction(j);
+	void setJunction(ModelContainer mc) {
+		if (mc != null && editing) {
+			junctionPane.setJunction(new GuiContainer(mc));
 			final CardLayout cl = (CardLayout) body.getLayout();
 			cl.show(body, CARD_EDIT);
Index: /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/model/Constants.java
===================================================================
--- /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/model/Constants.java	(revision 25782)
+++ /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/model/Constants.java	(revision 25783)
@@ -5,5 +5,5 @@
 public interface Constants {
 	String SEPARATOR = ";";
-	String SPLIT_REGEX = "[,:;]";
+	String SPLIT_REGEX = "\\p{Zs}*[,:;]\\p{Zs}*";
 	Pattern SPLIT_PATTERN = Pattern.compile(SPLIT_REGEX);
 	
Index: /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/model/Junction.java
===================================================================
--- /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/model/Junction.java	(revision 25782)
+++ /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/model/Junction.java	(revision 25783)
@@ -2,49 +2,16 @@
 
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
 import org.openstreetmap.josm.data.osm.Node;
-import org.openstreetmap.josm.data.osm.OsmPrimitive;
-import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
-import org.openstreetmap.josm.data.osm.Relation;
-import org.openstreetmap.josm.data.osm.RelationMember;
 import org.openstreetmap.josm.data.osm.Way;
 
 public class Junction {
-	private static final List<Way> filterHighways(List<OsmPrimitive> of) {
-		final List<Way> result = new ArrayList<Way>();
-		
-		for (OsmPrimitive p : of) {
-			if (p.getType() == OsmPrimitiveType.WAY && Utils.isRoad((Way) p)) {
-				result.add((Way) p);
-			}
-		}
-		
-		return result;
-	}
-	
-	private static List<Way> filterBeginsOrEndsAt(List<Way> ways, Node node) {
-		final List<Way> result = new ArrayList<Way>();
-		
-		for (Way w : ways) {
-			if (w.isFirstLastNode(node)) {
-				result.add(w);
-			}
-		}
-		
-		return result;
-	}
-	
-	private static List<Road> loadRoads(ModelContainer container, Junction j) {
-		final List<Way> ways = filterBeginsOrEndsAt(filterHighways(j.getNode().getReferrers()), j.getNode());
-		
-		return Road.map(container, ways, j);
-	}
-	
 	private final ModelContainer container;
 	
 	private final Node node;
-	private final List<Road> roads = new ArrayList<Road>();
+	private final Set<Way> roads = new HashSet<Way>();
 	
 	Junction(ModelContainer container, Node n) {
@@ -55,12 +22,11 @@
 		
 		if (isPrimary()) {
-			loadRoads(container, this);
 			// if turn data is invalid, this will force an exception now, not later during painting
-			getTurns();
+			// getTurns(); TODO force this again
 		}
 	}
 	
-	boolean isPrimary() {
-		return container.getPrimary().equals(this);
+	public boolean isPrimary() {
+		return getContainer().isPrimary(this);
 	}
 	
@@ -70,84 +36,53 @@
 	
 	public List<Road> getRoads() {
-		return roads;
+		final List<Road> result = new ArrayList<Road>(roads.size());
+		
+		for (Way w : roads) {
+			result.add(container.getRoad(w));
+		}
+		
+		return result;
 	}
 	
-	void addRoad(Road r) {
-		roads.add(r);
+	public List<Road.End> getRoadEnds() {
+		final List<Road.End> result = new ArrayList<Road.End>(roads.size());
+		
+		for (Way w : roads) {
+			result.add(getRoadEnd(w));
+		}
+		
+		return result;
 	}
 	
-	public void addTurn(Lane from, Road.End to) {
-		assert equals(from.getOutgoingJunction());
-		assert equals(to.getJunction());
+	void addRoad(Way w) {
+		roads.add(w);
+	}
+	
+	Road.End getRoadEnd(Way w) {
+		final Road r = getContainer().getRoad(w);
 		
-		final Way fromWay = from.isReverse() ? from.getRoad().getRoute().getFirstSegment().getWay() : from.getRoad()
-		    .getRoute().getLastSegment().getWay();
-		final Way toWay = to.isFromEnd() ? to.getRoad().getRoute().getFirstSegment().getWay() : to.getRoad().getRoute()
-		    .getLastSegment().getWay();
-		
-		Relation existing = null;
-		for (Turn t : getTurns()) {
-			if ((from.isReverse() ? from.getRoad().getRoute().getFirstSegment() : from.getRoad().getRoute().getLastSegment())
-			    .getWay().equals(t.getFromWay()) && t.getTo().equals(to)) {
-				if (t.getFrom().isExtra() == from.isExtra() && t.getFrom().getIndex() == from.getIndex()) {
-					// was already added
-					return;
-				}
-				
-				existing = t.getRelation();
+		if (r.getRoute().getSegments().size() == 1) {
+			final boolean starts = r.getRoute().getStart().equals(node);
+			final boolean ends = r.getRoute().getEnd().equals(node);
+			
+			if (starts && ends) {
+				throw new IllegalArgumentException("Ambiguous: The way starts and ends at the junction node.");
+			} else if (starts) {
+				return r.getFromEnd();
+			} else if (ends) {
+				return r.getToEnd();
 			}
+		} else if (r.getRoute().getFirstSegment().getWay().equals(w)) {
+			return r.getFromEnd();
+		} else if (r.getRoute().getLastSegment().getWay().equals(w)) {
+			return r.getToEnd();
 		}
 		
-		final Relation r = existing == null ? new Relation() : existing;
-		
-		final String key = from.isExtra() ? Constants.TURN_KEY_EXTRA_LANES : Constants.TURN_KEY_LANES;
-		final List<Integer> lanes = Turn.split(r.get(key));
-		lanes.add(from.getIndex());
-		r.put(key, Turn.join(lanes));
-		
-		if (existing == null) {
-			r.put("type", Constants.TYPE_TURNS);
-			
-			r.addMember(new RelationMember(Constants.TURN_ROLE_VIA, node));
-			r.addMember(new RelationMember(Constants.TURN_ROLE_FROM, fromWay));
-			r.addMember(new RelationMember(Constants.TURN_ROLE_TO, toWay));
-			
-			node.getDataSet().addPrimitive(r);
-		}
+		throw new IllegalArgumentException("While there exists a road for the given way, the way neither "
+		    + "starts nor ends at the junction node.");
 	}
 	
-	public Set<Turn> getTurns() {
-		return Turn.load(this);
-	}
-	
-	Road.End getRoadEnd(Way way) {
-		final List<Road.End> candidates = new ArrayList<Road.End>();
-		
-		for (Road r : getRoads()) {
-			if (r.getFromEnd().getJunction().equals(this)) {
-				if (r.getRoute().getSegments().get(0).getWay().equals(way)) {
-					candidates.add(r.getFromEnd());
-				}
-			}
-			
-			if (r.getToEnd().getJunction().equals(this)) {
-				if (r.getRoute().getSegments().get(r.getRoute().getSegments().size() - 1).getWay().equals(way)) {
-					candidates.add(r.getToEnd());
-				}
-			}
-		}
-		
-		if (candidates.isEmpty()) {
-			throw new IllegalArgumentException("No such road end.");
-		} else if (candidates.size() > 1) {
-			throw new IllegalArgumentException("There are " + candidates.size()
-			    + " road ends at this junction for the given way.");
-		}
-		
-		return candidates.get(0);
-	}
-	
-	public void removeTurn(Turn turn) {
-		turn.remove();
+	public ModelContainer getContainer() {
+		return container;
 	}
 }
Index: /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/model/Lane.java
===================================================================
--- /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/model/Lane.java	(revision 25782)
+++ /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/model/Lane.java	(revision 25783)
@@ -2,9 +2,10 @@
 
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
+import java.util.Set;
 
 import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.data.osm.RelationMember;
 import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.plugins.turnlanes.CollectionUtils;
@@ -15,48 +16,27 @@
 		EXTRA_RIGHT,
 		REGULAR;
-	}
-	
-	/**
-	 * 
-	 * @param road
-	 * @param r
-	 *          lengths relation
-	 * @param w
-	 * @return
-	 */
-	public static List<Lane> load(Road road, Relation left, Relation right, Way w) {
-		double extraLengthLeft = left == null ? 0 : Route.load(left).getLengthFrom(w);
-		double extraLengthRight = right == null ? 0 : Route.load(right).getLengthFrom(w);
-		
-		final List<Double> leftLengths = loadLengths(left, Constants.LENGTHS_KEY_LENGTHS_LEFT, extraLengthLeft);
-		final List<Double> rightLengths = loadLengths(right, Constants.LENGTHS_KEY_LENGTHS_RIGHT, extraLengthRight);
-		
-		final List<Lane> result = new ArrayList<Lane>(load(road));
-		final int mid = getReverseCount(result);
-		result.addAll(mid, map(leftLengths, road, true, extraLengthLeft));
-		result.addAll(map(rightLengths, road, false, extraLengthRight));
-		
-		return result;
-	}
-	
-	private static List<Lane> map(List<Double> lengths, Road road, boolean left, double extraLength) {
-		final List<Lane> result = new ArrayList<Lane>(lengths.size());
-		
-		int index = left ? -lengths.size() : 1;
-		for (Double l : (left ? CollectionUtils.reverse(lengths) : lengths)) {
-			// TODO road may connect twice with junction => reverse might not always be false
-			result.add(new Lane(road, index++, false, left, extraLength, l - extraLength));
-		}
-		
-		return result;
-	}
-	
-	private static int getReverseCount(List<Lane> ls) {
-		int result = 0;
-		
-		for (Lane l : ls) {
-			if (l.isReverse()) {
-				++result;
-			}
+		
+		public boolean isExtra() {
+			return this == EXTRA_LEFT || this == EXTRA_RIGHT;
+		}
+	}
+	
+	static List<Lane> load(Road.End roadEnd) {
+		final List<Lane> result = new ArrayList<Lane>();
+		int i;
+		
+		i = 0;
+		for (double l : CollectionUtils.reverse(roadEnd.getLengths(Kind.EXTRA_LEFT))) {
+			result.add(new Lane(roadEnd, --i, Kind.EXTRA_LEFT, l));
+		}
+		
+		final int regulars = getRegularCount(roadEnd.getWay(), roadEnd.getJunction().getNode());
+		for (i = 1; i <= regulars; ++i) {
+			result.add(new Lane(roadEnd, i));
+		}
+		
+		i = 0;
+		for (double l : roadEnd.getLengths(Kind.EXTRA_RIGHT)) {
+			result.add(new Lane(roadEnd, ++i, Kind.EXTRA_RIGHT, l));
 		}
 		
@@ -112,50 +92,22 @@
 	}
 	
-	public static List<Lane> load(Road r) {
-		final Route.Segment first = r.getRoute().getFirstSegment();
-		final Route.Segment last = r.getRoute().getLastSegment();
-		
-		final int back = getRegularCount(first.getWay(), first.getStart());
-		final int forth = getRegularCount(last.getWay(), last.getEnd());
-		
-		final List<Lane> result = new ArrayList<Lane>(back + forth);
-		
-		for (int i = back; i >= 1; --i) {
-			result.add(new Lane(r, i, true));
-		}
-		for (int i = 1; i <= forth; ++i) {
-			result.add(new Lane(r, i, false));
-		}
-		
-		return Collections.unmodifiableList(result);
-	}
-	
-	private final Road road;
+	private final Road.End roadEnd;
 	private final int index;
 	private final Kind kind;
-	private final boolean reverse;
-	
-	/**
-	 * If this extra lane extends past the given road, this value equals the length of the way(s)
-	 * which come after the end of the road.
-	 */
-	private final double extraLength;
 	
 	private double length = -1;
 	
-	public Lane(Road road, int index, boolean reverse) {
-		this.road = road;
+	public Lane(Road.End roadEnd, int index) {
+		this.roadEnd = roadEnd;
 		this.index = index;
 		this.kind = Kind.REGULAR;
-		this.reverse = reverse;
-		this.extraLength = -1;
-	}
-	
-	public Lane(Road road, int index, boolean reverse, boolean left, double extraLength, double length) {
-		this.road = road;
+	}
+	
+	public Lane(Road.End roadEnd, int index, Kind kind, double length) {
+		assert kind == Kind.EXTRA_LEFT || kind == Kind.EXTRA_RIGHT;
+		
+		this.roadEnd = roadEnd;
 		this.index = index;
-		this.kind = left ? Kind.EXTRA_LEFT : Kind.EXTRA_RIGHT;
-		this.reverse = reverse;
-		this.extraLength = extraLength;
+		this.kind = kind;
 		this.length = length;
 		
@@ -163,16 +115,8 @@
 			throw new IllegalArgumentException("Length must be positive");
 		}
-		
-		if (extraLength < 0) {
-			throw new IllegalArgumentException("Extra length must not be negative");
-		}
 	}
 	
 	public Road getRoad() {
-		return road;
-	}
-	
-	public double getExtraLength() {
-		return extraLength;
+		return roadEnd.getRoad();
 	}
 	
@@ -182,13 +126,5 @@
 	
 	public double getLength() {
-		return isExtra() ? length : road.getLength();
-	}
-	
-	double getTotalLength() {
-		if (!isExtra()) {
-			throw new UnsupportedOperationException();
-		}
-		
-		return length + extraLength;
+		return isExtra() ? length : getRoad().getLength();
 	}
 	
@@ -201,5 +137,5 @@
 		
 		// TODO if needed, increase length of other lanes
-		road.updateLengths();
+		getOutgoingRoadEnd().updateLengths();
 		
 		this.length = length;
@@ -214,22 +150,67 @@
 	}
 	
-	public boolean isReverse() {
-		return reverse;
-	}
-	
 	public Junction getOutgoingJunction() {
-		return isReverse() ? getRoad().getFromEnd().getJunction() : getRoad().getToEnd().getJunction();
+		return getOutgoingRoadEnd().getJunction();
 	}
 	
 	public Junction getIncomingJunction() {
-		return isReverse() ? getRoad().getToEnd().getJunction() : getRoad().getFromEnd().getJunction();
+		return getIncomingRoadEnd().getJunction();
 	}
 	
 	public Road.End getOutgoingRoadEnd() {
-		return isReverse() ? getRoad().getFromEnd() : getRoad().getToEnd();
+		return roadEnd;
 	}
 	
 	public Road.End getIncomingRoadEnd() {
-		return isReverse() ? getRoad().getToEnd() : getRoad().getFromEnd();
+		return roadEnd.getOppositeEnd();
+	}
+	
+	public ModelContainer getContainer() {
+		return getRoad().getContainer();
+	}
+	
+	public void addTurn(List<Road> via, Road.End to) {
+		assert equals(to.getJunction());
+		
+		Relation existing = null;
+		for (Turn t : to.getTurns()) {
+			if (t.getFrom().getOutgoingRoadEnd().equals(getOutgoingRoadEnd()) && t.getVia().equals(via)) {
+				if (t.getFrom().equals(this)) {
+					// was already added
+					return;
+				}
+				
+				existing = t.getRelation();
+			}
+		}
+		
+		final Relation r;
+		if (existing == null) {
+			r = new Relation();
+			r.put("type", Constants.TYPE_TURNS);
+			
+			r.addMember(new RelationMember(Constants.TURN_ROLE_FROM, getOutgoingRoadEnd().getWay()));
+			if (via.isEmpty()) {
+				r.addMember(new RelationMember(Constants.TURN_ROLE_VIA, getOutgoingJunction().getNode()));
+			} else {
+				for (Way w : Utils.flattenVia(getOutgoingJunction().getNode(), via, to.getJunction().getNode())) {
+					r.addMember(new RelationMember(Constants.TURN_ROLE_VIA, w));
+				}
+			}
+			r.addMember(new RelationMember(Constants.TURN_ROLE_TO, to.getWay()));
+			
+			getOutgoingJunction().getNode().getDataSet().addPrimitive(r);
+		} else {
+			r = existing;
+		}
+		
+		final String key = isExtra() ? Constants.TURN_KEY_EXTRA_LANES : Constants.TURN_KEY_LANES;
+		final List<Integer> lanes = Turn.indices(r, key);
+		lanes.add(getIndex());
+		r.put(key, Turn.join(lanes));
+	}
+	
+	public Set<Turn> getTurns() {
+		return Turn.load(getContainer(), Constants.TURN_ROLE_FROM, getOutgoingRoadEnd().getWay());
 	}
 }
Index: /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/model/ModelContainer.java
===================================================================
--- /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/model/ModelContainer.java	(revision 25782)
+++ /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/model/ModelContainer.java	(revision 25783)
@@ -1,15 +1,91 @@
 package org.openstreetmap.josm.plugins.turnlanes.model;
 
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.data.osm.RelationMember;
 import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.plugins.turnlanes.CollectionUtils;
+import org.openstreetmap.josm.tools.Pair;
 
 public class ModelContainer {
-	public static ModelContainer create(Node n) {
-		final ModelContainer container = new ModelContainer(n);
-		container.getOrCreateJunction(n);
-		return container;
+	public static ModelContainer create(Iterable<Node> primaryNodes, Iterable<Way> primaryWays) {
+		final Set<Node> closedNodes = new HashSet<Node>(CollectionUtils.toList(primaryNodes));
+		final Set<Way> closedWays = new HashSet<Way>(CollectionUtils.toList(primaryWays));
+		
+		close(closedNodes, closedWays);
+		
+		return new ModelContainer(closedNodes, closedWays);
+	}
+	
+	private static void close(Set<Node> closedNodes, Set<Way> closedWays) {
+		boolean closed = false;
+		
+		while (!closed) {
+			closed = true;
+			
+			for (Node n : closedNodes) {
+				for (Way w : Utils.filterRoads(n.getReferrers())) {
+					if (w.isFirstLastNode(n)) {
+						closed &= close(closedNodes, closedWays, w, Constants.TURN_ROLE_FROM);
+						closed &= close(closedNodes, closedWays, w, Constants.TURN_ROLE_TO);
+					}
+				}
+				
+				for (Way w : closedWays) {
+					closed &= close(closedNodes, closedWays, w, Constants.TURN_ROLE_VIA);
+				}
+			}
+		}
+	}
+	
+	private static boolean close(Set<Node> closedNodes, Set<Way> closedWays, Way w, String role) {
+		boolean closed = true;
+		
+		for (Relation r : OsmPrimitive.getFilteredList(w.getReferrers(), Relation.class)) {
+			if (!r.get("type").equals(Constants.TYPE_TURNS)) {
+				continue;
+			}
+			
+			for (RelationMember m : r.getMembers()) {
+				if (m.getRole().equals(role) && m.getMember().equals(w)) {
+					closed &= close(closedNodes, closedWays, r);
+				}
+			}
+		}
+		
+		return closed;
+	}
+	
+	private static boolean close(Set<Node> closedNodes, Set<Way> closedWays, Relation r) {
+		boolean closed = true;
+		
+		final List<Way> via = new ArrayList<Way>();
+		for (RelationMember m : Utils.getMembers(r, Constants.TURN_ROLE_VIA)) {
+			if (m.isWay()) {
+				closed &= !closedWays.add(m.getWay());
+				via.add(m.getWay());
+			} else if (m.isNode()) {
+				closed &= !closedNodes.add(m.getNode());
+			}
+		}
+		
+		if (!via.isEmpty()) {
+			final Way from = Utils.getMemberWay(r, Constants.TURN_ROLE_FROM);
+			final Way to = Utils.getMemberWay(r, Constants.TURN_ROLE_TO);
+			
+			closed &= !closedNodes.add(Utils.lineUp(from, via.get(0)));
+			closed &= !closedNodes.add(Utils.lineUp(via.get(via.size() - 1), to));
+		}
+		
+		return closed;
 	}
 	
@@ -17,8 +93,32 @@
 	private final Map<Way, Road> roads = new HashMap<Way, Road>();
 	
-	private final Node primary;
-	
-	private ModelContainer(Node primary) {
-		this.primary = primary;
+	private final Set<Node> primaryNodes;
+	private final Set<Way> primaryWays;
+	
+	private ModelContainer(Set<Node> primaryNodes, Set<Way> primaryWays) {
+		this.primaryNodes = Collections.unmodifiableSet(new HashSet<Node>(primaryNodes));
+		this.primaryWays = Collections.unmodifiableSet(new HashSet<Way>(primaryWays));
+		
+		final Set<Pair<Way, Junction>> ws = new HashSet<Pair<Way, Junction>>();
+		for (Node n : primaryNodes) {
+			final Junction j = getOrCreateJunction(n);
+			
+			for (Way w : Utils.filterRoads(n.getReferrers())) {
+				if (w.isFirstLastNode(n)) {
+					ws.add(new Pair<Way, Junction>(w, j));
+				}
+			}
+		}
+		
+		final List<Route> rs = Utils.orderWays(primaryWays, primaryNodes);
+		for (Route r : rs) {
+			addRoad(new Road(this, r));
+		}
+		
+		for (Pair<Way, Junction> w : ws) {
+			if (!primaryWays.contains(w.a)) {
+				addRoad(new Road(this, w.a, w.b));
+			}
+		}
 	}
 	
@@ -43,12 +143,16 @@
 	}
 	
-	Road getRoad(Way w, Junction j) {
-		final Road existing = roads.get(w);
-		
-		if (existing != null && j.equals(existing.getToEnd().getJunction())) {
-			return existing;
-		}
-		
-		final Road newRoad = new Road(this, w, j);
+	Road getRoad(Way w) {
+		final Road r = roads.get(w);
+		
+		if (r == null) {
+			throw new IllegalArgumentException("There is no road containing the given way.");
+		}
+		
+		return r;
+	}
+	
+	private void addRoad(Road newRoad, Road mergedA, Road mergedB) {
+		assert (mergedA == null) == (mergedB == null);
 		
 		for (Route.Segment s : newRoad.getRoute().getSegments()) {
@@ -56,13 +160,40 @@
 			
 			if (oldRoad != null) {
-				return mergeRoads(oldRoad, newRoad);
-			}
-		}
-		
-		return newRoad;
+				if (mergedA == null) {
+					final Road mergedRoad = mergeRoads(oldRoad, newRoad);
+					addRoad(mergedRoad, oldRoad, newRoad);
+				} else if (!oldRoad.equals(mergedA) && !oldRoad.equals(mergedB)) {
+					throw new RuntimeException("A road can't be connected to more than two junctions.");
+				}
+			}
+		}
+	}
+	
+	private void addRoad(Road newRoad) {
+		addRoad(newRoad, null, null);
 	}
 	
 	private Road mergeRoads(Road a, Road b) {
-		throw null; // TODO implement
+		final String ERR_ILLEGAL_ARGS = "The given roads can not be merged into one.";
+		
+		final List<Way> ws = new ArrayList<Way>(CollectionUtils.toList(CollectionUtils.reverse(a.getRoute().getWays())));
+		final List<Way> bws = b.getRoute().getWays();
+		
+		int i = -1;
+		for (Way w : ws) {
+			if (w.equals(bws.get(i + 1))) {
+				++i;
+			} else if (i >= 0) {
+				throw new IllegalArgumentException(ERR_ILLEGAL_ARGS);
+			}
+		}
+		
+		if (i < 0) {
+			throw new IllegalArgumentException(ERR_ILLEGAL_ARGS);
+		}
+		ws.addAll(bws.subList(i + 1, bws.size()));
+		
+		final Route mergedRoute = Route.create(ws, a.getRoute().getLastSegment().getEnd());
+		return new Road(this, mergedRoute);
 	}
 	
@@ -73,6 +204,34 @@
 	}
 	
-	Junction getPrimary() {
-		return junctions.get(primary);
+	public Set<Junction> getPrimaryJunctions() {
+		final Set<Junction> pjs = new HashSet<Junction>();
+		
+		for (Node n : primaryNodes) {
+			pjs.add(getOrCreateJunction(n));
+		}
+		
+		return pjs;
+	}
+	
+	public Set<Road> getPrimaryRoads() {
+		final Set<Road> prs = new HashSet<Road>();
+		
+		for (Way w : primaryWays) {
+			prs.add(roads.get(w));
+		}
+		
+		return prs;
+	}
+	
+	public ModelContainer recalculate() {
+		return new ModelContainer(primaryNodes, primaryWays);
+	}
+	
+	public boolean isPrimary(Junction j) {
+		return primaryNodes.contains(j.getNode());
+	}
+	
+	public boolean isPrimary(Road r) {
+		return primaryWays.contains(r.getRoute().getFirstSegment().getWay());
 	}
 }
Index: /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/model/Road.java
===================================================================
--- /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/model/Road.java	(revision 25782)
+++ /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/model/Road.java	(revision 25783)
@@ -7,4 +7,5 @@
 import java.util.Arrays;
 import java.util.List;
+import java.util.Set;
 
 import org.openstreetmap.josm.data.osm.Node;
@@ -14,4 +15,5 @@
 import org.openstreetmap.josm.data.osm.RelationMember;
 import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.plugins.turnlanes.CollectionUtils;
 import org.openstreetmap.josm.plugins.turnlanes.model.Route.Segment;
 import org.openstreetmap.josm.tools.Pair;
@@ -19,8 +21,37 @@
 public class Road {
 	public class End {
+		private final boolean from;
 		private final Junction junction;
 		
-		private End(Junction junction) {
+		private final Relation lengthsLeft;
+		private final Relation lengthsRight;
+		
+		private final double extraLengthLeft;
+		private final double extraLengthRight;
+		
+		private final List<Lane> lanes;
+		
+		private End(boolean from, Junction junction, Relation lengthsLeft, Relation lengthsRight) {
+			this.from = from;
 			this.junction = junction;
+			this.lengthsLeft = lengthsLeft;
+			this.lengthsRight = lengthsRight;
+			this.extraLengthLeft = lengthsLeft == null ? 0 : Route.load(lengthsLeft).getLengthFrom(getWay());
+			this.extraLengthRight = lengthsRight == null ? 0 : Route.load(lengthsRight).getLengthFrom(getWay());
+			this.lanes = Lane.load(this);
+			
+			junction.addRoad(getWay());
+		}
+		
+		private End(boolean from, Junction junction) {
+			this.from = from;
+			this.junction = junction;
+			this.lengthsLeft = null;
+			this.lengthsRight = null;
+			this.extraLengthLeft = 0;
+			this.extraLengthRight = 0;
+			this.lanes = Lane.load(this);
+			
+			junction.addRoad(getWay());
 		}
 		
@@ -29,37 +60,40 @@
 		}
 		
+		public Way getWay() {
+			return isFromEnd() ? getRoute().getFirstSegment().getWay() : getRoute().getLastSegment().getWay();
+		}
+		
 		public Junction getJunction() {
 			return junction;
 		}
 		
-		private boolean isFrom() {
-			if (this == fromEnd) {
-				return true;
-			} else if (this == toEnd) {
-				return false;
-			}
-			
-			throw new IllegalStateException();
-		}
-		
 		public boolean isFromEnd() {
-			return isFrom();
+			return from;
 		}
 		
 		public boolean isToEnd() {
-			return !isFrom();
-		}
-		
-		public void addExtraLane(boolean left) {
-			if (!isToEnd()) {
-				throw new UnsupportedOperationException();
+			return !isFromEnd();
+		}
+		
+		public End getOppositeEnd() {
+			return isFromEnd() ? toEnd : fromEnd;
+		}
+		
+		/**
+		 * @return the turns <em>onto</em> this road at this end
+		 */
+		public Set<Turn> getTurns() {
+			return Turn.load(getContainer(), Constants.TURN_ROLE_TO, getWay());
+		}
+		
+		public void addLane(Lane.Kind kind) {
+			if (kind == Lane.Kind.REGULAR) {
+				throw new IllegalArgumentException("Only extra lanes can be added.");
 			}
 			
 			double length = Double.POSITIVE_INFINITY;
-			for (Lane l : getLanes()) {
-				if (l.getKind() == (left ? Lane.Kind.EXTRA_LEFT : Lane.Kind.EXTRA_RIGHT)) {
-					{
-						length = Math.min(length, l.getLength());
-					}
+			for (Lane l : lanes) {
+				if (l.getKind() == kind) {
+					length = Math.max(0, Math.min(length, l.getLength() - 1));
 				}
 			}
@@ -69,16 +103,121 @@
 			}
 			
-			addLength(left, length);
-		}
-	}
-	
-	public static List<Road> map(ModelContainer container, List<Way> ws, Junction j) {
-		final List<Road> result = new ArrayList<Road>();
-		
-		for (Way w : ws) {
-			result.add(container.getRoad(w, j));
-		}
-		
-		return result;
+			addLane(kind, length);
+		}
+		
+		private void addLane(Lane.Kind kind, double length) {
+			assert kind == Lane.Kind.EXTRA_LEFT || kind == Lane.Kind.EXTRA_RIGHT;
+			
+			final boolean left = kind == Lane.Kind.EXTRA_LEFT;
+			final Relation rel = left ? lengthsLeft : lengthsRight;
+			final Relation other = left ? lengthsRight : lengthsLeft;
+			final Node n = getJunction().getNode();
+			
+			final String lengthStr = toLengthString(length);
+			final Relation target;
+			if (rel == null) {
+				if (other == null || !Utils.getMemberNode(other, "end").equals(n)) {
+					target = createLengthsRelation();
+				} else {
+					target = other;
+				}
+			} else {
+				target = rel;
+			}
+			
+			final String key = left ? Constants.LENGTHS_KEY_LENGTHS_LEFT : Constants.LENGTHS_KEY_LENGTHS_RIGHT;
+			final String old = target.get(key);
+			if (old == null) {
+				target.put(key, lengthStr);
+			} else {
+				target.put(key, old + Constants.SEPARATOR + lengthStr);
+			}
+		}
+		
+		private Relation createLengthsRelation() {
+			final Node n = getJunction().getNode();
+			
+			final Relation r = new Relation();
+			r.put("type", Constants.TYPE_LENGTHS);
+			
+			r.addMember(new RelationMember(Constants.LENGTHS_ROLE_END, n));
+			for (Route.Segment s : isFromEnd() ? route.getSegments() : CollectionUtils.reverse(route.getSegments())) {
+				r.addMember(new RelationMember(Constants.LENGTHS_ROLE_WAYS, s.getWay()));
+			}
+			
+			n.getDataSet().addPrimitive(r);
+			
+			return r;
+		}
+		
+		void updateLengths() {
+			for (final boolean left : Arrays.asList(true, false)) {
+				final Lane.Kind kind = left ? Lane.Kind.EXTRA_LEFT : Lane.Kind.EXTRA_RIGHT;
+				final Relation r = left ? lengthsLeft : lengthsRight;
+				final double extra = left ? extraLengthLeft : extraLengthRight;
+				
+				if (r == null) {
+					continue;
+				}
+				
+				final StringBuilder lengths = new StringBuilder(32);
+				for (Lane l : left ? CollectionUtils.reverse(lanes) : lanes) {
+					if (l.getKind() == kind) {
+						lengths.append(toLengthString(extra + l.getLength())).append(Constants.SEPARATOR);
+					}
+				}
+				
+				lengths.setLength(lengths.length() - Constants.SEPARATOR.length());
+				r.put(left ? Constants.LENGTHS_KEY_LENGTHS_LEFT : Constants.LENGTHS_KEY_LENGTHS_RIGHT, lengths.toString());
+			}
+		}
+		
+		public List<Lane> getLanes() {
+			return lanes;
+		}
+		
+		public Lane getLane(Lane.Kind kind, int index) {
+			for (Lane l : lanes) {
+				if (l.getKind() == kind && l.getIndex() == index) {
+					return l;
+				}
+			}
+			
+			throw new IllegalArgumentException("No such lane.");
+		}
+		
+		public Lane getExtraLane(int index) {
+			return index < 0 ? getLane(Lane.Kind.EXTRA_LEFT, index) : getLane(Lane.Kind.EXTRA_RIGHT, index);
+		}
+		
+		public boolean isExtendable() {
+			final End o = getOppositeEnd();
+			return (lengthsLeft == null && lengthsRight == null) && (o.lengthsLeft != null || o.lengthsRight != null);
+		}
+		
+		public void extend(Way way) {
+			if (!isExtendable()) {
+				throw new IllegalStateException();
+			}
+			
+			final End o = getOppositeEnd();
+			if (o.lengthsLeft != null) {
+				o.lengthsLeft.addMember(new RelationMember(Constants.LENGTHS_ROLE_WAYS, way));
+			}
+			if (o.lengthsRight != null) {
+				o.lengthsRight.addMember(new RelationMember(Constants.LENGTHS_ROLE_WAYS, way));
+			}
+		}
+		
+		public List<Double> getLengths(Lane.Kind kind) {
+			switch (kind) {
+				case EXTRA_LEFT:
+					return Lane.loadLengths(lengthsLeft, Constants.LENGTHS_KEY_LENGTHS_LEFT, extraLengthLeft);
+				case EXTRA_RIGHT:
+					return Lane.loadLengths(lengthsRight, Constants.LENGTHS_KEY_LENGTHS_RIGHT, extraLengthRight);
+				default:
+					throw new IllegalArgumentException(String.valueOf(kind));
+			}
+		}
 	}
 	
@@ -142,37 +281,27 @@
 	
 	private final ModelContainer container;
-	
-	private final Relation lengthsLeft;
-	private final Relation lengthsRight;
 	private final Route route;
-	private final List<Lane> lanes;
-	
 	private final End fromEnd;
 	private final End toEnd;
 	
 	Road(ModelContainer container, Way w, Junction j) {
-		this.container = container;
-		this.toEnd = new End(j);
-		
 		final Node n = j.getNode();
-		
 		if (!w.isFirstLastNode(n)) {
 			throw new IllegalArgumentException("Way must start or end in given node.");
 		}
-		
-		final Pair<Relation, Relation> lr = getLengthRelations(w, n);
-		
-		this.lengthsLeft = lr.a;
-		this.lengthsRight = lr.b;
-		this.route = lengthsLeft == null && lengthsRight == null ? Route.create(Arrays.asList(w), n) : Route.load(
-		    lengthsLeft, lengthsRight, w);
-		this.lanes = lengthsLeft == null && lengthsRight == null ? Lane.load(this) : Lane.load(this, lengthsLeft,
-		    lengthsRight, w);
-		
-		final List<Node> nodes = route.getNodes();
-		this.fromEnd = new End(container.getOrCreateJunction(nodes.get(0)));
-		
-		fromEnd.getJunction().addRoad(this);
-		toEnd.getJunction().addRoad(this);
+		final Pair<Relation, Relation> lengthsRelations = getLengthRelations(w, n);
+		
+		this.container = container;
+		this.route = lengthsRelations.a == null && lengthsRelations.b == null ? Route.create(Arrays.asList(w), n) : Route
+		    .load(lengthsRelations.a, lengthsRelations.b, w);
+		this.fromEnd = new End(true, container.getOrCreateJunction(route.getFirstSegment().getStart()));
+		this.toEnd = new End(false, j, lengthsRelations.a, lengthsRelations.b);
+	}
+	
+	Road(ModelContainer container, Route route) {
+		this.container = container;
+		this.route = route;
+		this.fromEnd = new End(true, container.getJunction(route.getStart()));
+		this.toEnd = new End(false, container.getJunction(route.getEnd()));
 	}
 	
@@ -189,34 +318,6 @@
 	}
 	
-	public List<Lane> getLanes() {
-		return lanes;
-	}
-	
 	public double getLength() {
 		return route.getLength();
-	}
-	
-	void updateLengths() {
-		if (lengthsLeft != null) { // TODO only forward lanes at this point
-			String lengths = "";
-			for (int i = lanes.size() - 1; i >= 0; --i) {
-				final Lane l = lanes.get(i);
-				if (l.getKind() == Lane.Kind.EXTRA_LEFT) {
-					lengths += toLengthString(l.getTotalLength()) + Constants.SEPARATOR;
-				}
-			}
-			lengthsLeft.put(Constants.LENGTHS_KEY_LENGTHS_LEFT,
-			    lengths.substring(0, lengths.length() - Constants.SEPARATOR.length()));
-		}
-		if (lengthsRight != null) {
-			String lengths = "";
-			for (Lane l : lanes) {
-				if (l.getKind() == Lane.Kind.EXTRA_RIGHT) {
-					lengths += toLengthString(l.getTotalLength()) + Constants.SEPARATOR;
-				}
-			}
-			lengthsRight.put(Constants.LENGTHS_KEY_LENGTHS_RIGHT,
-			    lengths.substring(0, lengths.length() - Constants.SEPARATOR.length()));
-		}
 	}
 	
@@ -229,94 +330,10 @@
 	}
 	
-	// TODO create a special Lanes class which deals with this, misplaced here
-	public Lane getLane(boolean reverse, int index) {
-		for (Lane l : lanes) {
-			if (!l.isExtra() && l.isReverse() == reverse && l.getIndex() == index) {
-				return l;
-			}
-		}
-		
-		throw new IllegalArgumentException("No such lane.");
-	}
-	
-	public Lane getExtraLane(boolean reverse, int index) {
-		for (Lane l : lanes) {
-			if (l.isExtra() && l.isReverse() == reverse && l.getIndex() == index) {
-				return l;
-			}
-		}
-		
-		throw new IllegalArgumentException("No such lane.");
-	}
-	
 	public ModelContainer getContainer() {
 		return container;
 	}
 	
-	private void addLength(boolean left, double length) {
-		final Relation l = lengthsLeft;
-		final Relation r = lengthsRight;
-		final Node n = toEnd.getJunction().getNode();
-		
-		final String lengthStr = toLengthString(length);
-		final Relation target;
-		if (left) {
-			if (l == null) {
-				if (r == null || !Utils.getMemberNode(r, "end").equals(n)) {
-					target = createLengthsRelation();
-				} else {
-					target = r;
-				}
-			} else {
-				target = l;
-			}
-		} else {
-			if (r == null) {
-				if (l == null || !Utils.getMemberNode(l, "end").equals(n)) {
-					target = createLengthsRelation();
-				} else {
-					target = l;
-				}
-			} else {
-				target = r;
-			}
-		}
-		
-		final String key = left ? Constants.LENGTHS_KEY_LENGTHS_LEFT : Constants.LENGTHS_KEY_LENGTHS_RIGHT;
-		final String old = target.get(key);
-		if (old == null) {
-			target.put(key, lengthStr);
-		} else {
-			target.put(key, old + Constants.SEPARATOR + lengthStr);
-		}
-	}
-	
-	private Relation createLengthsRelation() {
-		final Node n = toEnd.getJunction().getNode();
-		
-		final Relation r = new Relation();
-		r.put("type", Constants.TYPE_LENGTHS);
-		
-		r.addMember(new RelationMember(Constants.LENGTHS_ROLE_END, n));
-		for (Route.Segment s : route.getSegments()) {
-			r.addMember(1, new RelationMember(Constants.LENGTHS_ROLE_WAYS, s.getWay()));
-		}
-		
-		n.getDataSet().addPrimitive(r);
-		
-		return r;
-	}
-	
-	public boolean isExtendable() {
-		return lengthsLeft != null || lengthsRight != null;
-	}
-	
-	public void extend(Way way) {
-		if (lengthsLeft != null) {
-			lengthsLeft.addMember(new RelationMember(Constants.LENGTHS_ROLE_WAYS, way));
-		}
-		if (lengthsRight != null) {
-			lengthsRight.addMember(new RelationMember(Constants.LENGTHS_ROLE_WAYS, way));
-		}
+	public boolean isPrimary() {
+		return getContainer().isPrimary(this);
 	}
 }
Index: /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/model/Route.java
===================================================================
--- /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/model/Route.java	(revision 25782)
+++ /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/model/Route.java	(revision 25783)
@@ -199,4 +199,12 @@
 	}
 	
+	public Node getStart() {
+		return getFirstSegment().getStart();
+	}
+	
+	public Node getEnd() {
+		return getLastSegment().getEnd();
+	}
+	
 	public Segment getFirstSegment() {
 		return getSegments().get(0);
@@ -210,3 +218,13 @@
 		return new Route(segments.subList(fromIndex, toIndex));
 	}
+	
+	public List<Way> getWays() {
+		final List<Way> ws = new ArrayList<Way>();
+		
+		for (Segment s : segments) {
+			ws.add(s.getWay());
+		}
+		
+		return Collections.unmodifiableList(ws);
+	}
 }
Index: /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/model/Turn.java
===================================================================
--- /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/model/Turn.java	(revision 25782)
+++ /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/model/Turn.java	(revision 25783)
@@ -2,5 +2,8 @@
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Set;
@@ -8,182 +11,173 @@
 import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
-import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
 import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.RelationMember;
 import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.plugins.turnlanes.CollectionUtils;
 
-public class Turn {
-	public static Set<Turn> load(Junction junction) {
+public final class Turn {
+	static Set<Turn> load(ModelContainer c, String role, OsmPrimitive primitive) {
 		final Set<Turn> result = new HashSet<Turn>();
-
-		for (OsmPrimitive p : junction.getNode().getReferrers()) {
-			if (p.getType() != OsmPrimitiveType.RELATION) {
-				continue;
-			}
-
-			Relation r = (Relation) p;
-
+		
+		for (Relation r : OsmPrimitive.getFilteredList(primitive.getReferrers(), Relation.class)) {
 			if (!r.get("type").equals(Constants.TYPE_TURNS)) {
 				continue;
 			}
-
-			result.addAll(Turn.load(junction, r));
+			
+			for (RelationMember m : r.getMembers()) {
+				if (m.getRole().equals(role) && m.getMember().equals(primitive)) {
+					result.addAll(load(c, r));
+				}
+			}
 		}
-
+		
 		return result;
 	}
-
-	private static Set<Turn> load(Junction junction, Relation turns) {
-		if (!Constants.TYPE_TURNS.equals(turns.get("type"))) {
-			throw new IllegalArgumentException("Relation must be a of type \""
-					+ Constants.TYPE_TURNS + "\".");
-		}
-
-		Way fromWay = null;
-		Way toWay = null;
-		Node via = null;
-		final List<Integer> lanes = split(turns.get(Constants.TURN_KEY_LANES));
-		final List<Integer> extraLanes = split(turns
-				.get(Constants.TURN_KEY_EXTRA_LANES));
-
-		for (RelationMember m : turns.getMembers()) {
-			final String r = m.getRole();
-			if (Constants.TURN_ROLE_VIA.equals(r)) {
-				if (via != null) {
-					throw new IllegalArgumentException("More than one \""
-							+ Constants.TURN_ROLE_VIA + "\" members.");
+	
+	static Set<Turn> load(ModelContainer c, Relation r) {
+		for (RelationMember m : r.getMembers()) {
+			if (m.getRole().equals(Constants.TURN_ROLE_VIA)) {
+				if (m.isNode()) {
+					return loadWithViaNode(c, r);
+				} else if (m.isWay()) {
+					return loadWithViaWays(c, r);
 				}
-
-				via = m.getNode();
-			} else if (Constants.TURN_ROLE_FROM.equals(r)) {
-				if (fromWay != null) {
-					throw new IllegalArgumentException("More than one \""
-							+ Constants.TURN_ROLE_FROM + "\" members.");
-				}
-
-				fromWay = m.getWay();
-			} else if (Constants.TURN_ROLE_TO.equals(r)) {
-				if (toWay != null) {
-					throw new IllegalArgumentException("More than one \""
-							+ Constants.TURN_ROLE_TO + "\" members.");
-				}
-
-				toWay = m.getWay();
 			}
 		}
-
-		if (!via.equals(junction.getNode())) {
-			throw new IllegalArgumentException(
-					"Turn is for a different junction.");
-		}
-
-		final Road.End to = junction.getRoadEnd(toWay);
-
-		final Set<Turn> result = new HashSet<Turn>();
-		for (Road r : junction.getRoads()) {
-			final boolean reverse;
-			if (r.getRoute().getFirstSegment().getWay().equals(fromWay)
-					&& r.getRoute().getFirstSegment().getStart().equals(via)) {
-				reverse = true;
-			} else if (r.getRoute().getLastSegment().getWay().equals(fromWay)
-					&& r.getRoute().getLastSegment().getEnd().equals(via)) {
-				reverse = false;
-			} else {
-				continue;
+		
+		throw new IllegalArgumentException("No via node or way(s).");
+	}
+	
+	private static Set<Turn> loadWithViaWays(ModelContainer c, Relation r) {
+		final Way from = Utils.getMemberWay(r, Constants.TURN_ROLE_FROM);
+		final Way to = Utils.getMemberWay(r, Constants.TURN_ROLE_TO);
+		
+		final List<Way> tmp = Utils.getMemberWays(r, Constants.TURN_ROLE_VIA);
+		final LinkedList<Road> via = new LinkedList<Road>();
+		
+		final Road.End fromRoadEnd = c.getJunction(Utils.lineUp(from, tmp.get(0))).getRoadEnd(from);
+		
+		Node n = fromRoadEnd.getJunction().getNode();
+		final Iterator<Way> it = tmp.iterator();
+		while (it.hasNext()) {
+			final Way w = it.next();
+			final Road v = c.getRoad(w);
+			via.add(v);
+			n = Utils.getOppositeEnd(w, n);
+			
+			if (!v.isPrimary()) {
+				throw new IllegalStateException("The road is not part of the junction.");
 			}
-
-			for (int l : lanes) {
-				result.add(new Turn(turns, r.getLane(reverse, l), junction, to,
-						fromWay, toWay));
-			}
-			for (int l : extraLanes) {
-				result.add(new Turn(turns, r.getExtraLane(reverse, l),
-						junction, to, fromWay, toWay));
+			
+			final Iterator<Route.Segment> it2 = (v.getRoute().getFirstSegment().getWay().equals(w) ? v.getRoute()
+			    .getSegments() : CollectionUtils.reverse(v.getRoute().getSegments())).iterator();
+			it2.next(); // first is done
+			
+			while (it2.hasNext()) {
+				final Way w2 = it2.next().getWay();
+				n = Utils.getOppositeEnd(w2, n);
+				
+				if (!it.hasNext() || !w2.equals(it.next())) {
+					throw new IllegalStateException("The via ways of the relation do not form a road.");
+				}
 			}
 		}
-
+		final Road.End toRoadEnd = c.getJunction(n).getRoadEnd(to);
+		n = Utils.getOppositeEnd(to, n);
+		
+		final Set<Turn> result = new HashSet<Turn>();
+		for (int i : indices(r, Constants.TURN_KEY_LANES)) {
+			result.add(new Turn(r, fromRoadEnd.getLane(Lane.Kind.REGULAR, i), via, toRoadEnd));
+		}
+		for (int i : indices(r, Constants.TURN_KEY_EXTRA_LANES)) {
+			result.add(new Turn(r, fromRoadEnd.getExtraLane(i), via, toRoadEnd));
+		}
 		return result;
 	}
-
-	static List<Integer> split(String lanes) {
-		if (lanes == null) {
+	
+	static List<Integer> indices(Relation r, String key) {
+		final String joined = r.get(key);
+		
+		if (joined == null) {
 			return new ArrayList<Integer>(1);
 		}
-
+		
 		final List<Integer> result = new ArrayList<Integer>();
-		for (String lane : Constants.SPLIT_PATTERN.split(lanes)) {
+		for (String lane : Constants.SPLIT_PATTERN.split(joined)) {
 			result.add(Integer.parseInt(lane));
 		}
-
+		
 		return result;
 	}
-
+	
+	private static Set<Turn> loadWithViaNode(ModelContainer c, Relation r) {
+		final Way from = Utils.getMemberWay(r, Constants.TURN_ROLE_FROM);
+		final Node via = Utils.getMemberNode(r, Constants.TURN_ROLE_VIA);
+		final Way to = Utils.getMemberWay(r, Constants.TURN_ROLE_TO);
+		
+		final Junction j = c.getJunction(via);
+		
+		final Road.End fromRoadEnd = j.getRoadEnd(from);
+		final Road.End toRoadEnd = j.getRoadEnd(to);
+		
+		final Set<Turn> result = new HashSet<Turn>();
+		for (int i : indices(r, Constants.TURN_KEY_LANES)) {
+			result.add(new Turn(r, fromRoadEnd.getLane(Lane.Kind.REGULAR, i), Collections.<Road> emptyList(), toRoadEnd));
+		}
+		for (int i : indices(r, Constants.TURN_KEY_EXTRA_LANES)) {
+			result.add(new Turn(r, fromRoadEnd.getExtraLane(i), Collections.<Road> emptyList(), toRoadEnd));
+		}
+		return result;
+	}
+	
 	static String join(List<Integer> list) {
 		if (list.isEmpty()) {
 			return null;
 		}
-
-		final StringBuilder builder = new StringBuilder(list.size()
-				* (2 + Constants.SEPARATOR.length()));
-
+		
+		final StringBuilder builder = new StringBuilder(list.size() * (2 + Constants.SEPARATOR.length()));
+		
 		for (int e : list) {
 			builder.append(e).append(Constants.SEPARATOR);
 		}
-
+		
 		builder.setLength(builder.length() - Constants.SEPARATOR.length());
 		return builder.toString();
 	}
-
+	
 	private final Relation relation;
-
+	
 	private final Lane from;
-	private final Junction via;
+	private final List<Road> via;
 	private final Road.End to;
-
-	private final Way fromWay;
-	private final Way toWay;
-
-	public Turn(Relation relation, Lane from, Junction via, Road.End to,
-			Way fromWay, Way toWay) {
+	
+	public Turn(Relation relation, Lane from, List<Road> via, Road.End to) {
 		this.relation = relation;
 		this.from = from;
 		this.via = via;
 		this.to = to;
-		this.fromWay = fromWay;
-		this.toWay = toWay;
 	}
-
+	
 	public Lane getFrom() {
 		return from;
 	}
-
-	public Junction getVia() {
+	
+	public List<Road> getVia() {
 		return via;
 	}
-
+	
 	public Road.End getTo() {
 		return to;
 	}
-
+	
 	Relation getRelation() {
 		return relation;
 	}
-
-	Way getFromWay() {
-		return fromWay;
-	}
-
-	Way getToWay() {
-		return toWay;
-	}
-
-	void remove() {
-		final List<Integer> lanes = split(relation
-				.get(Constants.TURN_KEY_LANES));
-		final List<Integer> extraLanes = split(relation
-				.get(Constants.TURN_KEY_EXTRA_LANES));
-
-		if (lanes.size() + extraLanes.size() == 1
-				&& (from.isExtra() ^ !lanes.isEmpty())) {
+	
+	public void remove() {
+		final List<Integer> lanes = indices(relation, Constants.TURN_KEY_LANES);
+		final List<Integer> extraLanes = indices(relation, Constants.TURN_KEY_EXTRA_LANES);
+		
+		if (lanes.size() + extraLanes.size() == 1 && (from.isExtra() ^ !lanes.isEmpty())) {
 			relation.getDataSet().removePrimitive(relation.getPrimitiveId());
 		} else if (from.isExtra()) {
@@ -192,7 +186,7 @@
 			lanes.remove(Integer.valueOf(from.getIndex()));
 		}
-
-		relation.put(Constants.TURN_KEY_LANES, join(lanes));
-		relation.put(Constants.TURN_KEY_EXTRA_LANES, join(extraLanes));
+		
+		relation.put(Constants.TURN_KEY_LANES, lanes.isEmpty() ? null : join(lanes));
+		relation.put(Constants.TURN_KEY_EXTRA_LANES, extraLanes.isEmpty() ? null : join(extraLanes));
 	}
 }
Index: /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/model/Utils.java
===================================================================
--- /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/model/Utils.java	(revision 25782)
+++ /applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/model/Utils.java	(revision 25783)
@@ -3,14 +3,19 @@
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Set;
 
 import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
 import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.RelationMember;
 import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.plugins.turnlanes.CollectionUtils;
 
 public class Utils {
@@ -24,50 +29,91 @@
 	}
 	
+	public static final List<Way> filterRoads(List<OsmPrimitive> of) {
+		final List<Way> result = new ArrayList<Way>();
+		
+		for (OsmPrimitive p : of) {
+			if (p.getType() == OsmPrimitiveType.WAY && Utils.isRoad((Way) p)) {
+				result.add((Way) p);
+			}
+		}
+		
+		return result;
+	}
+	
 	public static Node getMemberNode(Relation r, String role) {
 		return getMember(r, role, OsmPrimitiveType.NODE).getNode();
 	}
 	
+	public static Way getMemberWay(Relation r, String role) {
+		return getMember(r, role, OsmPrimitiveType.WAY).getWay();
+	}
+	
 	public static RelationMember getMember(Relation r, String role, OsmPrimitiveType type) {
 		final List<RelationMember> candidates = getMembers(r, role, type);
-		
-		if (candidates.size() == 0) {
+		if (candidates.isEmpty()) {
 			throw new IllegalStateException("No member with given role and type.");
 		} else if (candidates.size() > 1) {
 			throw new IllegalStateException(candidates.size() + " members with given role and type.");
 		}
-		
 		return candidates.get(0);
 	}
 	
 	public static List<RelationMember> getMembers(Relation r, String role, OsmPrimitiveType type) {
+		final List<RelationMember> result = getMembers(r, role);
+		for (RelationMember m : getMembers(r, role)) {
+			if (m.getType() != type) {
+				throw new IllegalArgumentException("Member has the specified role, but wrong type.");
+			}
+		}
+		return result;
+	}
+	
+	public static List<RelationMember> getMembers(Relation r, String role) {
 		final List<RelationMember> result = new ArrayList<RelationMember>();
-		
 		for (RelationMember m : r.getMembers()) {
-			if (m.getRole().equals(role) && m.getType() == type) {
+			if (m.getRole().equals(role)) {
 				result.add(m);
 			}
 		}
-		
-		return result;
+		return result;
+	}
+	
+	public static List<Node> getMemberNodes(Relation r, String role) {
+		return mapMembers(getMembers(r, role, OsmPrimitiveType.NODE), Node.class);
 	}
 	
 	public static List<Way> getMemberWays(Relation r, String role) {
-		final List<Way> result = new ArrayList<Way>();
-		
-		for (RelationMember m : getMembers(r, role, OsmPrimitiveType.WAY)) {
-			result.add(m.getWay());
-		}
-		
-		return result;
-	}
-	
-	public static List<Node> getMemberNodes(Relation r, String role) {
-		final List<Node> result = new ArrayList<Node>();
-		
-		for (RelationMember m : getMembers(r, role, OsmPrimitiveType.NODE)) {
-			result.add(m.getNode());
-		}
-		
-		return result;
+		return mapMembers(getMembers(r, role, OsmPrimitiveType.WAY), Way.class);
+	}
+	
+	private static <T> List<T> mapMembers(List<RelationMember> ms, Class<T> t) {
+		final List<T> result = new ArrayList<T>(ms.size());
+		for (RelationMember m : ms) {
+			result.add(t.cast(m.getMember()));
+		}
+		return result;
+	}
+	
+	/**
+	 * 
+	 * @param a
+	 * @param b
+	 * @return the node at which {@code a} and {@code b} are connected
+	 */
+	public static Node lineUp(Way a, Way b) {
+		final Set<Node> s = new HashSet<Node>(Arrays.asList(a.firstNode(), a.lastNode(), b.firstNode(), b.lastNode()));
+		if (a.firstNode() == a.lastNode() || b.firstNode().equals(b.lastNode()) || s.size() == 2) {
+			throw new IllegalArgumentException("Cycles are not allowed.");
+		} else if (s.size() == 4) {
+			throw new IllegalArgumentException("Ways are not connected (at their first and last nodes).");
+		}
+		
+		if (a.firstNode() == b.firstNode() || a.lastNode() == b.firstNode()) {
+			return b.firstNode();
+		} else if (a.firstNode() == b.lastNode() || a.lastNode() == b.lastNode()) {
+			return b.lastNode();
+		} else {
+			throw new AssertionError();
+		}
 	}
 	
@@ -86,3 +132,94 @@
 		}
 	}
+	
+	/**
+	 * Orders the {@code ways} such that the combined ways out of each returned list form a path (in
+	 * order) from one node out of {@code nodes} to another out of {@code nodes}.
+	 * 
+	 * <ul>
+	 * <li>Each way is used exactly once.</li>
+	 * <li>Paths contain no {@code nodes} excepting the first and last nodes.</li>
+	 * <li>Paths contain no loops w.r.t. the ways' first and last nodes</li>
+	 * </ul>
+	 * 
+	 * @param ways
+	 *          ways to be ordered
+	 * @param nodes
+	 *          start/end nodes
+	 * @return
+	 * @throws IllegalArgumentException
+	 *           if the ways can't be ordered
+	 */
+	public static List<Route> orderWays(Iterable<Way> ways, Iterable<Node> nodes) {
+		final List<Way> ws = new LinkedList<Way>(CollectionUtils.toList(ways));
+		final Set<Node> ns = new HashSet<Node>(CollectionUtils.toList(nodes));
+		
+		final List<Route> result = new ArrayList<Route>();
+		
+		while (!ws.isEmpty()) {
+			result.add(findPath(ws, ns));
+		}
+		
+		return result;
+	}
+	
+	private static Route findPath(List<Way> ws, Set<Node> ns) {
+		final Way w = findPathSegment(ws, ns);
+		final boolean first = ns.contains(w.firstNode());
+		final boolean last = ns.contains(w.lastNode());
+		
+		if (first && last) {
+			return Route.create(Arrays.asList(w), w.firstNode());
+		} else if (!first && !last) {
+			throw new AssertionError();
+		}
+		
+		final List<Way> result = new ArrayList<Way>();
+		result.add(w);
+		Node n = first ? w.lastNode() : w.firstNode();
+		while (true) {
+			final Way next = findPathSegment(ws, Arrays.asList(n));
+			result.add(next);
+			n = getOppositeEnd(next, n);
+			
+			if (ns.contains(n)) {
+				return Route.create(result, first ? w.firstNode() : w.lastNode());
+			}
+		}
+	}
+	
+	private static Way findPathSegment(List<Way> ws, Collection<Node> ns) {
+		final Iterator<Way> it = ws.iterator();
+		
+		while (it.hasNext()) {
+			final Way w = it.next();
+			
+			if (ns.contains(w.firstNode()) || ns.contains(w.lastNode())) {
+				it.remove();
+				return w;
+			}
+		}
+		
+		throw new IllegalArgumentException("Ways can't be ordered.");
+	}
+	
+	public static Iterable<Way> flattenVia(Node start, List<Road> via, Node end) {
+		final List<Way> result = new ArrayList<Way>();
+		
+		Node n = start;
+		for (Road r : via) {
+			final Iterable<Route.Segment> segments = r.getRoute().getFirstSegment().getWay().isFirstLastNode(n) ? r
+			    .getRoute().getSegments() : CollectionUtils.reverse(r.getRoute().getSegments());
+			
+			for (Route.Segment s : segments) {
+				result.add(s.getWay());
+				n = Utils.getOppositeEnd(s.getWay(), n);
+			}
+		}
+		if (!end.equals(n)) {
+			throw new IllegalArgumentException("The given via ways don't end at the given node.");
+		}
+		
+		return result;
+	}
 }
