Index: /applications/editors/josm/plugins/duplicateway/src/org/openstreetmap/josm/plugins/duplicateway/DuplicateWayAction.java
===================================================================
--- /applications/editors/josm/plugins/duplicateway/src/org/openstreetmap/josm/plugins/duplicateway/DuplicateWayAction.java	(revision 4650)
+++ /applications/editors/josm/plugins/duplicateway/src/org/openstreetmap/josm/plugins/duplicateway/DuplicateWayAction.java	(revision 4651)
@@ -1,2 +1,3 @@
+// License: GPL. Copyright 2007 by Brent Easton and others
 package org.openstreetmap.josm.plugins.duplicateway;
 
@@ -17,5 +18,5 @@
 
 import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.actions.mapmode.MapMode;
 import org.openstreetmap.josm.command.AddCommand;
 import org.openstreetmap.josm.command.Command;
@@ -23,5 +24,4 @@
 import org.openstreetmap.josm.data.SelectionChangedListener;
 import org.openstreetmap.josm.data.coor.EastNorth;
-import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.data.osm.Node;
@@ -30,282 +30,242 @@
 import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.tools.JVector;
 
 /**
- * A plugin to add ways manipulation things
- * 
- * @author Thomas.Walraet
+ * Duplicate an existing set of ordered ways, offset by a specified distance.
+ * 
+ * This basic version just creates a completely seperate way and makes no
+ * attempt to attach it to any other ways.
+ * 
+ * Planned Improvements:
+ * 
+ * 1. After creation of the duplicate way and while it is still selected allow
+ * the Mouse wheel, or the up and down arrow (probably in association with the
+ * shift key) to increase/decrease the offset distance. Clicking anywhere, or
+ * moving the mouse out of the view window should finish this mode.
+ * 
+ * 2. Locate points close to the end points and pop up a dialog asking of these
+ * should be joined.
+ * 
+ * 3. Handle intersecting ways. Pop up a dialog for each asking if the
+ * intersecting way should be carried accross to intersect the newly created
+ * way. Handle multiple intersecting ways at a point.
+ * 
+ * 
+ * @author Brent Easton
+ * 
  */
-class DuplicateWayAction extends JosmAction implements SelectionChangedListener, MouseListener {
-
-  private static final long serialVersionUID = 1L;
-  protected String name;
-  protected Cursor oldCursor;
-  protected List<Way> selectedWays;
-
-  public DuplicateWayAction(String name) {
-    super(name, "duplicateway", tr("Duplicate selected ways."), KeyEvent.VK_W, KeyEvent.CTRL_MASK
-        | KeyEvent.SHIFT_MASK, true);
-    this.name = name;
-    setEnabled(false);
-    DataSet.listeners.add(this);
-  }
-
-  public void actionPerformed(ActionEvent e) {
-
-//    DataSet d = Main.ds;
-    selectedWays = new ArrayList<Way>();
-    for (OsmPrimitive osm : Main.ds.getSelected()) {
-      if (osm instanceof Way) {
-        Way way = (Way) osm;
-        EastNorth last = null;
-        for (Segment seg : way.segments) {
-          if (last != null) {
-            if (! seg.from.eastNorth.equals(last)) {
-              JOptionPane.showMessageDialog(Main.parent, tr("Can't duplicate unnordered way."));
-              return;
-            }
-          }
-          last = seg.to.eastNorth;
-        }
-        selectedWays.add(way);
-      }
-    }
-
-    if (Main.map == null) {
-      JOptionPane.showMessageDialog(Main.parent, tr("No data loaded."));
-      return;
-    }
-    
-    if (selectedWays.isEmpty()) {
-      JOptionPane.showMessageDialog(Main.parent, tr("You must select at least one way."));
-      return;
-    }
-
-    oldCursor = Main.map.mapView.getCursor();
-    Main.map.mapView.setCursor(ImageProvider.getCursor("crosshair", "duplicate"));
-    Main.map.mapView.addMouseListener(this);
-  }
-  
-  public static Node createNode(double east, double north) {
-    return new Node(Main.proj.eastNorth2latlon(new EastNorth(east, north)));
-  }
-  /**
-   * Duplicate the selected ways. The distance to be offset is 
-   * determined by finding the distance of the 'offset' point from 
-   * the nearest segment. 
-   * 
-   * @param offset The point in screen co-ordinates used to calculate the offset distance
-   */
-  protected void duplicate(Point clickPoint) {
-
-    EastNorth clickEN = Main.map.mapView.getEastNorth(clickPoint.x, clickPoint.y);
-
-    /*
-     * First, find the nearest Segment belonging to a selected way
-     */
-    Segment cs = null;
-    for (Way way : selectedWays) {
-      double minDistance = Double.MAX_VALUE;
-      // segments
-      for (Segment ls : way.segments) {
-        if (ls.deleted || ls.incomplete)
-          continue;
-        double perDist = JosmVector.perpDistance(ls, clickEN);
-        if (perDist < minDistance) {
-          minDistance = perDist;
-          cs = ls;
-        }
-      }
-    }
-    
-    if (cs == null) {
-      return;
-    }
-    
-    /*
-     * Find the distance we need to offset the new way
-     * +ve offset is to the right of the initial way, -ve to the left
-     */
-    JosmVector closestSegment = new JosmVector(cs);
-    double offset = closestSegment.calculateOffset(clickEN);
-    
-    Collection<Command> commands = new LinkedList<Command>();
-    Collection<Way> ways = new LinkedList<Way>();
-    
-    /*
-     * First new node is offset 90 degrees from the first point
-     */
-    for (Way way : selectedWays) {
-      Way newWay = new Way();
-      
-      Node lastNode = null;
-      JosmVector lastLine = null;
-    
-      for (Segment seg : way.segments) {
-        JosmVector currentLine = new JosmVector(seg);
-        Node newNode = null;
-        
-        if (lastNode == null) {
-          JosmVector perpVector = new JosmVector(currentLine);
-          perpVector.rotate90(offset);
-          newNode = createNode(perpVector.getP2().getX(), perpVector.getP2().getY());
-          commands.add(new AddCommand(newNode));
-        }
-        else {
-//          if (lastLine != null &&
-//              ((lastLine.getTheta() < 0 && currentLine.getTheta() > 0) ||
-//              (lastLine.getTheta() > 0 && currentLine.getTheta() < 0))) {
-//            offset = -offset;
-//          }
-//          if ()
-//              ) {
-//            offset = -offset;
-//          }
-          JosmVector bisector = lastLine.bisector(currentLine, offset);
-          newNode = createNode(bisector.getP2().getX(), bisector.getP2().getY());
-          commands.add(new AddCommand(newNode));
-          Segment s = new Segment (newNode, lastNode);
-          commands.add(new AddCommand(s));
-          newWay.segments.add(0, s);
-//          if ((lastLine.direction().equals("ne") && currentLine.direction().equals("se")) ||
-//              (lastLine.direction().equals("se") && currentLine.direction().equals("ne")) ||
-//              (lastLine.direction().equals("nw") && currentLine.direction().equals("sw")) ||
-//              (lastLine.direction().equals("sw") && currentLine.direction().equals("nw"))) {
-//            offset = -offset;
-//          }
-        }
-
-        lastLine = currentLine;
-        lastNode = newNode;
-        
-      }
-      lastLine.reverse();
-      lastLine.rotate90(-offset);
-      Node newNode = createNode(lastLine.getP2().getX(), lastLine.getP2().getY());
-      commands.add(new AddCommand(newNode));
-      Segment s = new Segment (newNode, lastNode);
-      commands.add(new AddCommand(s));
-      newWay.segments.add(0, s);
-      
-      for (String key : way.keySet()) {
-        newWay.put(key, way.get(key));
-      }
-      commands.add(new AddCommand(newWay));
-      ways.add(newWay);
-    }
-  
-    Main.main.undoRedo.add(new SequenceCommand(tr("Create duplicate way"), commands));
-    Main.ds.setSelected(ways);
-  }
-
-  protected Node offsetNode(Node oldNode, double offsetE, double offsetN) {
-    EastNorth en = Main.proj.latlon2eastNorth(oldNode.coor);
-    EastNorth newEn = new EastNorth(en.east()+offsetE, en.north()+offsetN);
-    LatLon ll = Main.proj.eastNorth2latlon(newEn);   
-    return new Node(ll);
-  }
-  
-  /**
-   * Enable the "Duplicate way" menu option if at least one way is selected
-   */
-  public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
-    for (OsmPrimitive osm : newSelection) {
-      if (osm instanceof Way) {
-          setEnabled(true);
-          return;
-      }
-    }
-    setEnabled(false);
-  }
-
-  public void mouseClicked(MouseEvent e) {
-    Main.map.mapView.removeMouseListener(this);
-    Main.map.mapView.setCursor(oldCursor);
-    duplicate(e.getPoint());
-  }
-
-  public void mouseEntered(MouseEvent e) {
-    // TODO Auto-generated method stub
-    
-  }
-
-  public void mouseExited(MouseEvent e) {
-    // TODO Auto-generated method stub
-    
-  }
-
-  public void mousePressed(MouseEvent e) {
-    // TODO Auto-generated method stub
-    
-  }
-
-  public void mouseReleased(MouseEvent e) {
-    // TODO Auto-generated method stub
-    
-  }
-  
-//  class DuplicateDialog extends JDialog {
-//    private static final long serialVersionUID = 1L;
-//    protected Box mainPanel;
-//    protected IntConfigurer offset;
-//    protected boolean cancelled;
-//    protected String right;
-//    protected String left;
-//    protected JComboBox moveCombo;
-//
-//    public DuplicateDialog(String title) {
-//      super();
-//      this.setTitle(title);
-//      this.setModal(true);
-//      initComponents();
-//    }
-//
-//    protected void initComponents() {
-//      mainPanel = Box.createVerticalBox();
-//      offset = new IntConfigurer("", tr("Offset (metres):  "), new Integer(15));
-//      mainPanel.add(offset.getControls());
-//      getContentPane().add(mainPanel);
-//
-//      right = tr("right/down");
-//      left = tr("left/up");
-//      Box movePanel = Box.createHorizontalBox();
-//      movePanel.add(new JLabel(tr("Create new segments to the ")));
-//      moveCombo = new JComboBox(new String[] {right, left});
-//      movePanel.add(moveCombo);
-//      movePanel.add(new JLabel(tr(" of existing segments.")));
-//      mainPanel.add(movePanel);
-//
-//      Box buttonPanel = Box.createHorizontalBox();
-//      JButton okButton = new JButton(tr("Ok"));
-//      okButton.addActionListener(new ActionListener() {
-//        public void actionPerformed(ActionEvent e) {
-//          cancelled = false;
-//          setVisible(false);
-//
-//        }
-//      });
-//      JButton canButton = new JButton(tr("Cancel"));
-//      canButton.addActionListener(new ActionListener() {
-//        public void actionPerformed(ActionEvent e) {
-//          cancelled = true;
-//          setVisible(false);
-//        }
-//      });
-//      buttonPanel.add(okButton);
-//      buttonPanel.add(canButton);
-//      mainPanel.add(buttonPanel);
-//
-//      pack();
-//    }
-//
-//    protected int getOffset() {
-//      int off = offset.getIntValue(15);
-//      return right.equals(moveCombo.getSelectedItem()) ? off : -off;
-//    }
-//
-//    protected boolean isCancelled() {
-//      return cancelled;
-//    }
-//
-//  }
+public class DuplicateWayAction extends MapMode implements
+        SelectionChangedListener, MouseListener {
+
+	private static final long serialVersionUID = 1L;
+
+	protected Cursor oldCursor;
+
+	protected List<Way> selectedWays;
+	
+	protected MapMode previousMode;
+
+	/**
+	 * Create new DuplicateWay Action
+	 * 
+	 * @param name
+	 */
+	public DuplicateWayAction() {
+		super(tr("Duplicate Way"), "duplicateway",
+		        tr("Duplicate selected ways."), KeyEvent.VK_W, null,
+		        ImageProvider.getCursor("crosshair", "duplicate"));
+		setEnabled(false);
+		DataSet.listeners.add(this);
+	}
+
+	@Override public void enterMode() {
+		super.enterMode();
+		Main.map.mapView.addMouseListener(this);
+	}
+
+	@Override public void exitMode() {
+		super.exitMode();
+		Main.map.mapView.removeMouseListener(this);
+	}
+
+	/**
+	 * The Duplicate Way button has been clicked
+	 * 
+	 * @param e
+	 *            Action Event
+	 */
+	@Override public void actionPerformed(ActionEvent e) {
+
+		selectedWays = new ArrayList<Way>();
+		for (OsmPrimitive osm : Main.ds.getSelected()) {
+			if (osm instanceof Way) {
+				Way way = (Way)osm;
+				EastNorth last = null;
+				for (Segment seg : way.segments) {
+					if (last != null) {
+						if (!seg.from.eastNorth.equals(last)) {
+							JOptionPane.showMessageDialog(Main.parent,
+							        tr("Can't duplicate unnordered way."));
+							return;
+						}
+					}
+					last = seg.to.eastNorth;
+				}
+				selectedWays.add(way);
+			}
+		}
+
+		if (Main.map == null) {
+			JOptionPane.showMessageDialog(Main.parent, tr("No data loaded."));
+			return;
+		}
+
+		if (selectedWays.isEmpty()) {
+			JOptionPane.showMessageDialog(Main.parent,
+			        tr("You must select at least one way."));
+			return;
+		}
+		previousMode = Main.map.mapMode;
+		super.actionPerformed(e);
+	}
+
+	/**
+	 * Create a new Node object at a specified Easting/Northing location
+	 * 
+	 * @param east
+	 *            Easting of new Node
+	 * @param north
+	 *            Northing of new node
+	 * @return new Node
+	 */
+	public static Node createNode(double east, double north) {
+		return new Node(Main.proj.eastNorth2latlon(new EastNorth(east, north)));
+	}
+
+	/**
+	 * Duplicate the selected ways. The distance to be offset is determined by
+	 * finding the distance of the 'offset' point from the nearest segment.
+	 * 
+	 * @param clickPoint
+	 *            The point in screen co-ordinates used to calculate the offset
+	 *            distance
+	 */
+	protected void duplicate(Point clickPoint) {
+
+		EastNorth clickEN = Main.map.mapView.getEastNorth(clickPoint.x,
+		        clickPoint.y);
+
+		/*
+		 * First, find the nearest Segment belonging to a selected way
+		 */
+		Segment cs = null;
+		for (Way way : selectedWays) {
+			double minDistance = Double.MAX_VALUE;
+			// segments
+			for (Segment ls : way.segments) {
+				if (ls.deleted || ls.incomplete)
+					continue;
+				double perDist = JVector.perpDistance(ls, clickEN);
+				if (perDist < minDistance) {
+					minDistance = perDist;
+					cs = ls;
+				}
+			}
+		}
+
+		if (cs == null) {
+			return;
+		}
+
+		/*
+		 * Find the distance we need to offset the new way +ve offset is to the
+		 * right of the initial way, -ve to the left
+		 */
+		JVector closestSegment = new JVector(cs);
+		double offset = closestSegment.calculateOffset(clickEN);
+
+		Collection<Command> commands = new LinkedList<Command>();
+		Collection<Way> ways = new LinkedList<Way>();
+
+		/*
+		 * First new node is offset 90 degrees from the first point
+		 */
+		for (Way way : selectedWays) {
+			Way newWay = new Way();
+
+			Node lastNode = null;
+			JVector lastLine = null;
+
+			for (Segment seg : way.segments) {
+				JVector currentLine = new JVector(seg);
+				Node newNode = null;
+
+				if (lastNode == null) {
+					JVector perpVector = new JVector(currentLine);
+					perpVector.rotate90(offset);
+					newNode = createNode(perpVector.getP2().getX(), perpVector
+					        .getP2().getY());
+					commands.add(new AddCommand(newNode));
+				} else {
+					JVector bisector = lastLine.bisector(currentLine, offset);
+					newNode = createNode(bisector.getP2().getX(), bisector
+					        .getP2().getY());
+					commands.add(new AddCommand(newNode));
+					Segment s = new Segment(newNode, lastNode);
+					commands.add(new AddCommand(s));
+					newWay.segments.add(0, s);
+				}
+
+				lastLine = currentLine;
+				lastNode = newNode;
+
+			}
+			lastLine.reverse();
+			lastLine.rotate90(-offset);
+			Node newNode = createNode(lastLine.getP2().getX(), lastLine.getP2()
+			        .getY());
+			commands.add(new AddCommand(newNode));
+			Segment s = new Segment(newNode, lastNode);
+			commands.add(new AddCommand(s));
+			newWay.segments.add(0, s);
+
+			for (String key : way.keySet()) {
+				newWay.put(key, way.get(key));
+			}
+			commands.add(new AddCommand(newWay));
+			ways.add(newWay);
+		}
+
+		Main.main.undoRedo.add(new SequenceCommand(tr("Create duplicate way"),
+		        commands));
+		Main.ds.setSelected(ways);
+	}
+
+	/**
+	 * Enable the "Duplicate way" menu option if at least one way is selected
+	 * 
+	 * @param newSelection
+	 */
+	public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
+		for (OsmPrimitive osm : newSelection) {
+			if (osm instanceof Way) {
+				setEnabled(true);
+				return;
+			}
+		}
+		setEnabled(false);
+	}
+
+	/**
+	 * User has clicked on map to indicate the offset. Create the
+	 * duplicate way and exit duplicate mode
+	 * 
+	 * @param e
+	 */
+	public void mouseClicked(MouseEvent e) {
+		duplicate(e.getPoint());
+		exitMode();
+		Main.map.selectMapMode(previousMode);
+	}
 }
Index: /applications/editors/josm/plugins/duplicateway/src/org/openstreetmap/josm/plugins/duplicateway/DuplicateWayPlugin.java
===================================================================
--- /applications/editors/josm/plugins/duplicateway/src/org/openstreetmap/josm/plugins/duplicateway/DuplicateWayPlugin.java	(revision 4650)
+++ /applications/editors/josm/plugins/duplicateway/src/org/openstreetmap/josm/plugins/duplicateway/DuplicateWayPlugin.java	(revision 4651)
@@ -31,10 +31,10 @@
     if (toolsMenu == null) {
       toolsMenu = new JMenu(name);
-      toolsMenu.add(new JMenuItem(new DuplicateWayAction(name)));
+      toolsMenu.add(new JMenuItem(new DuplicateWayAction()));
       Main.main.menu.add(toolsMenu, 2);
     }
     else {
       toolsMenu.addSeparator();
-      toolsMenu.add(new JMenuItem(new DuplicateWayAction(name)));
+      toolsMenu.add(new JMenuItem(new DuplicateWayAction()));
     }
     
Index: /applications/editors/josm/plugins/duplicateway/src/org/openstreetmap/josm/plugins/duplicateway/JVector.java
===================================================================
--- /applications/editors/josm/plugins/duplicateway/src/org/openstreetmap/josm/plugins/duplicateway/JVector.java	(revision 4651)
+++ /applications/editors/josm/plugins/duplicateway/src/org/openstreetmap/josm/plugins/duplicateway/JVector.java	(revision 4651)
@@ -0,0 +1,343 @@
+
+// License: GPL. Copyright 2007 by Brent Easton
+
+package org.openstreetmap.josm.plugins.duplicateway;
+
+import java.awt.geom.Line2D;
+import java.awt.geom.Point2D;
+
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.Segment;
+
+/**
+ *
+ * A class encapsulating a line Segment treating it as a Vector (
+ * direction and length). Includes Utility routines to perform various
+ * transformations and Trigonometric operations 
+ *
+ * @author Brent Easton
+ *
+ */
+public class JVector extends Line2D.Double {
+  
+  public static final double EARTH_CIRCUMFERENCE = 40041455;
+  protected static final double PI_ON_2 = Math.PI / 2.0;
+  protected Point2D.Double slopeIntercept = null;
+  protected Point2D.Double rtheta = null;
+  
+  /**
+   * Create new JVector joining two points
+   * 
+   * @param x1
+   * @param y1
+   * @param x2
+   * @param y2
+   */
+  public JVector(double x1, double y1, double x2, double y2) {
+    super(x1, y1, x2, y2);
+  }
+  
+  /**
+   * Create new JVector from another JVector
+   * 
+   * @param v JVector to copy
+   */
+  public JVector(JVector v) {
+    super();
+    x1 = v.x1;
+    x2 = v.x2;
+    y1 = v.y1;
+    y2 = v.y2;
+  }
+  
+  /**
+   * Create a new JVector based on a JOSM Segment object
+   * 
+   * @param s
+   */
+  public JVector (Segment s) {
+    super(s.from.eastNorth.east(), s.from.eastNorth.north(), s.to.eastNorth.east(), s.to.eastNorth.north());
+  }
+  
+  /**
+   * Calculate slope/intersect from cartesian co-ords
+   *
+   */
+  protected void calculateSlopeIntercept() {
+    double slope = (y2 - y1) /(x2 - x1);
+    double intersect = y1 - (slope * x1);
+    slopeIntercept = new Point2D.Double(slope, intersect);
+  }
+  
+  /**
+   * Return the slope of the JVector
+   * @return slope
+   */
+  public double getSlope() {
+    if (slopeIntercept == null) {
+      calculateSlopeIntercept();
+    }
+    return slopeIntercept.x;
+  }
+  
+  /**
+   * Return the Y intercept of the JVector
+   * @return intercept
+   */
+  public double getIntercept() {
+    if (slopeIntercept == null) {
+      calculateSlopeIntercept();
+    }
+    return slopeIntercept.y;
+  }
+  
+  /**
+   * Calculate the polar coordinates for this line as a ray with
+   * the from point as origin
+   */
+  protected void calculatePolar() {
+    double x = x2 - x1;
+    double y = y2 - y1;
+    double r = Math.sqrt(x * x + y * y);
+    double theta = Math.atan2(y, x);
+    rtheta = new Point2D.Double(r, theta);
+  }
+  
+  /**
+   * Return the length of the line segment
+   * @return length
+   */
+  public double getLength() {
+    if (rtheta == null) {
+      calculatePolar();
+    }
+    return rtheta.x;
+  }
+  
+  /**
+   * Return the angle of the line segment
+   * @return theta
+   */
+  public double getTheta() {
+    if (rtheta == null) {
+      calculatePolar();
+    }
+    return rtheta.y;
+  }
+  
+  /**
+   * Convert Polar co-ords to cartesian
+   */
+  protected void polarToCartesian() {
+    double newx2 = x1 + getLength() * Math.cos(getTheta());
+    double newy2 = y1 + getLength() * Math.sin(getTheta());
+    x2 = newx2;
+    y2 = newy2;
+    slopeIntercept = null;
+  }
+  
+  /**
+   * Set the line segment using Polar co-ordinates
+   * 
+   * @param r line length
+   * @param theta angle
+   */
+  protected void setPolar(double r, double theta) {
+    rtheta = new Point2D.Double(r, theta);
+    polarToCartesian();
+  }
+  
+  /**
+   * Set the length of the line segment
+   * @param l length
+   */
+  protected void setLength(double l) {
+    rtheta.x = l;
+    polarToCartesian();
+  }
+  
+  /**
+   * Reverse the direction of the segment
+   */
+  public void reverse() {
+    double t = x2;
+    x2 = x1;
+    x1 = t;
+    t = y2;
+    y2 = y1;
+    y1 = t;
+    slopeIntercept = null;
+    rtheta = null;
+  }
+  
+  /**
+   * Rotate the line segment about the origin
+   * @param rot angle to rotate
+   */
+  protected void rotate(double rot) {
+    if (rtheta == null) {
+       calculatePolar();
+    }
+    rtheta.y = normalize(rtheta.y + rot);
+    polarToCartesian();
+  }
+  
+  /**
+   * Rotate 90 degrees clockwise
+   *
+   */
+  protected void rotate90CW() {
+    rotate(-PI_ON_2);
+  }
+  
+  /**
+   * Rotate 90 degrees counterclockwise
+   *
+   */
+  protected void rotate90CCW() {
+    rotate(PI_ON_2);
+  }
+  
+  /**
+   * Normalize theta to be in the range -PI < theta < PI
+   * @param theta
+   * @return normalized angle
+   */
+  protected double normalize(double theta) {
+    while (theta < -Math.PI || theta > Math.PI) {
+      if (theta > Math.PI) {
+        return (theta - 2 * Math.PI);
+      }
+      if (theta < -Math.PI) {
+        return (theta + 2 * Math.PI);
+      }
+    }
+    return theta;
+  }
+ 
+  /**
+   * Rotate the line segment 90 degrees and set the length.  
+   * @param offset length
+   */
+  protected void rotate90(double offset) {
+    rotate(PI_ON_2 * (offset < 0 ? 1 : -1));
+    setLength(Math.abs(offset));
+  }
+
+  /* 
+   * Return the distance of the given point from this line. Offset is 
+   * -ve if the point is to the left of the line, or +ve if to the right
+   */
+  
+  
+  /**
+   * Return the distance of the given point from this line. Offset is 
+   * -ve if the point is to the left of the line, or +ve if to the right
+   * @param target 
+   */
+  protected double calculateOffset(EastNorth target) {
+    
+    // Calculate the perpendicular interceptor to this point
+    EastNorth intersectPoint = perpIntersect(target);
+    JVector intersectRay = new JVector(intersectPoint.east(), intersectPoint.north(), target.east(), target.north());
+    
+    // Offset is equal to the length of the interceptor
+    double offset = intersectRay.getLength();
+    
+    // Check the angle between this line and the interceptor to calculate left/right
+    double theta = normalize(getTheta() - intersectRay.getTheta());
+    if (theta < 0) {
+      offset = -offset;
+    }
+    
+    return offset;
+  }
+  
+
+  /**
+   * Return the Perpendicular distance between a point
+   * and this line. Units is degrees.
+   * @param n Node
+   */
+  public double perpDistance(Node n) {
+    return perpDistance(n.eastNorth);
+  }
+
+  /**
+   * Calculate the perpendicular distance from the supplied
+   * point to this line
+   * @param en point
+   * @return distance
+   */
+  public double perpDistance(EastNorth en) {
+   return ptLineDist(en.east(), en.north());
+  }
+
+  /**
+   * Calculate the perpendicular distance from the given point
+   * to a JOSM segment.
+   * @param s
+   * @param en
+   * @return
+   */
+  public static double perpDistance(Segment s, EastNorth en) {  
+   return Line2D.ptSegDist(s.from.eastNorth.east(), s.from.eastNorth.north(), s.to.eastNorth.east(), s.to.eastNorth.north(), en.east(), en.north());
+  }
+  
+  /**
+   * Calculate the bisector between this and another Vector. A positive offset means
+   * the bisector must point to the right of the Vectors.
+   * @param ls Other vector to create angle to bisect
+   * @param offset length of bisecing vector
+   * @return the bisecting vector
+   */
+  public JVector bisector(JVector ls, double offset) {
+    JVector newSeg = new JVector(ls);
+    double newTheta = Math.PI + ls.getTheta() - getTheta();
+    newSeg.setPolar(Math.abs(offset), newSeg.getTheta() - newTheta/2.0);
+    
+    double angle = normalize(getTheta() - newSeg.getTheta());
+    if ((angle < 0 && offset > 0) || (angle > 0 && offset < 0)) {
+      newSeg.rotate(Math.PI);
+    }
+    return newSeg;
+  }
+  
+  /**
+   * Return the Perpendicular Intersector from a point to this line
+   * @param n a JOSM node
+   * @return the point on our line closest to n
+   */
+  public EastNorth perpIntersect(Node n) {
+    return perpIntersect(n.eastNorth);
+  }
+  
+  /**
+   * Calculate the point on our line closest to the supplied point
+   * @param en point
+   * @return return closest point
+   */
+  public EastNorth perpIntersect(EastNorth en) {
+    
+    /*
+     * Calculate the coefficients for the two lines
+     *  1. The segment: y = ax + b
+     *  2. The perpendicular line through the new point: y = cx + d
+     */
+    double perpSlope = -1 / getSlope();    
+    double perpIntercept = en.north() - (en.east() * perpSlope);
+    
+    /*
+     * Solve the simultaneous equation to calculate the intersection
+     *  ax+b = cx+d
+     *  ax - cx = d - b
+     *  x (a-c) = d - b
+     *  x = (d - b) / (a - c) 
+     */
+    double intersectE = (perpIntercept - getIntercept()) / (getSlope() - perpSlope);
+    double intersectN = intersectE * getSlope() + getIntercept();
+    
+    return new EastNorth(intersectE, intersectN);
+  }
+}
Index: plications/editors/josm/plugins/duplicateway/src/org/openstreetmap/josm/plugins/duplicateway/JosmVector.java
===================================================================
--- /applications/editors/josm/plugins/duplicateway/src/org/openstreetmap/josm/plugins/duplicateway/JosmVector.java	(revision 4650)
+++ 	(revision )
@@ -1,304 +1,0 @@
-package org.openstreetmap.josm.plugins.duplicateway;
-
-import java.awt.geom.Line2D;
-import java.awt.geom.Point2D;
-
-import org.openstreetmap.josm.data.coor.EastNorth;
-import org.openstreetmap.josm.data.osm.Node;
-import org.openstreetmap.josm.data.osm.Segment;
-
-public class JosmVector extends Line2D.Double {
-  
-  public static final double EARTH_CIRCUMFERENCE = 40041455;
-  protected static final double PI_ON_2 = Math.PI / 2.0;
-  protected Point2D.Double slopeIntercept = null;
-  protected Point2D.Double rtheta = null;
-  
-  public JosmVector(double x1, double y1, double x2, double y2) {
-    super(x1, y1, x2, y2);
-  }
-  
-  public JosmVector(JosmVector ls) {
-    super();
-    x1 = ls.x1;
-    x2 = ls.x2;
-    y1 = ls.y1;
-    y2 = ls.y2;
-  }
-  
-  public JosmVector (Segment s) {
-    super(s.from.eastNorth.east(), s.from.eastNorth.north(), s.to.eastNorth.east(), s.to.eastNorth.north());
-  }
-  
-  /*
-   * Calculate slope/intersect from cartesian co-ords
-   */
-  protected void calculateSlopeIntercept() {
-    double slope = (y2 - y1) /(x2 - x1);
-    double intersect = y1 - (slope * x1);
-    slopeIntercept = new Point2D.Double(slope, intersect);
-  }
-  
-  public double getSlope() {
-    if (slopeIntercept == null) {
-      calculateSlopeIntercept();
-    }
-    return slopeIntercept.x;
-  }
-  
-  public double getIntercept() {
-    if (slopeIntercept == null) {
-      calculateSlopeIntercept();
-    }
-    return slopeIntercept.y;
-  }
-  
-  /*
-   * Calculate the polar coordinates for this line as a ray with
-   * the from point as origin
-   */
-  protected void calculatePolar() {
-    double x = x2 - x1;
-    double y = y2 - y1;
-    double r = Math.sqrt(x * x + y * y);
-    double theta = Math.atan2(y, x);
-    rtheta = new Point2D.Double(r, theta);
-  }
-  
-  public double getLength() {
-    if (rtheta == null) {
-      calculatePolar();
-    }
-    return rtheta.x;
-  }
-  
-  public double getTheta() {
-    if (rtheta == null) {
-      calculatePolar();
-    }
-    return rtheta.y;
-  }
-  
-  /*
-   * Set the Cartesian co-ords of the to point from the Polar co-ords
-   */
-  protected void polarToCartesian() {
-    double newx2 = x1 + getLength() * Math.cos(getTheta());
-    double newy2 = y1 + getLength() * Math.sin(getTheta());
-    x2 = newx2;
-    y2 = newy2;
-    slopeIntercept = null;
-  }
-  
-  protected void setPolar(double r, double theta) {
-    rtheta = new Point2D.Double(r, theta);
-    polarToCartesian();
-  }
-  
-  protected void setLength(double l) {
-    rtheta.x = l;
-    polarToCartesian();
-  }
-  /*
-   * Reverse the direction of the segment
-   */
-  public void reverse() {
-    double t = x2;
-    x2 = x1;
-    x1 = t;
-    t = y2;
-    y2 = y1;
-    y1 = t;
-    slopeIntercept = null;
-    rtheta = null;
-  }
-  
-  /*
-   * Rotate the line 
-   */
-  protected void rotate(double rot) {
-    if (rtheta == null) {
-       calculatePolar();
-    }
-    rtheta.y = normalize(rtheta.y + rot);
-    polarToCartesian();
-  }
-  
-  protected void rotate90CW() {
-    rotate(-PI_ON_2);
-  }
-  
-  protected void rotate90CCW() {
-    rotate(PI_ON_2);
-  }
-  
-  /*
-   * Normalize theta to be in the range -PI < theta < PI
-   */
-  protected double normalize(double theta) {
-    while (theta < -Math.PI || theta > Math.PI) {
-      if (theta > Math.PI) {
-        return (theta - 2 * Math.PI);
-      }
-      if (theta < -Math.PI) {
-        return (theta + 2 * Math.PI);
-      }
-    }
-    return theta;
-  }
-  
-//  /*
-//   * Rotate vector and set lenngth. If offset is positive,
-//   * rotate so the vector points more towards the right, 
-//   * otherwise towards the left
-//   */
-//  protected void rotate(double theta, double offset) {
-//    if (getTheta() > 0) {
-//      if (offset > 0) {
-//        rotate(-theta);
-//      }
-//      else {
-//        rotate(theta);
-//      }
-//    }
-//    else {
-//      if (offset > 0) {
-//        rotate(theta);
-//      }
-//      else {
-//        rotate(-theta);
-//      }
-//    }
-//    setLength(Math.abs(offset));
-//  }
- 
-  protected void rotate90(double offset) {
-    rotate(PI_ON_2 * (offset < 0 ? 1 : -1));
-    setLength(Math.abs(offset));
-  }
-
-  /* 
-   * Return the distance of the given point from this line. Offset is 
-   * -ve if the point is to the left of the line, or +ve if to the right
-   */
-  protected double calculateOffset(EastNorth target) {
-    
-    // Calculate the perpendicular interceptor to this point
-    EastNorth intersectPoint = perpIntersect(target);
-    JosmVector intersectRay = new JosmVector(intersectPoint.east(), intersectPoint.north(), target.east(), target.north());
-    
-    // Offset is equal to the length of the interceptor
-    double offset = intersectRay.getLength();
-    
-    // Check the angle between this line and the interceptor to calculate left/right
-    double theta = normalize(getTheta() - intersectRay.getTheta());
-    if (theta < 0) {
-      offset = -offset;
-    }
-    
-    return offset;
-  }
-  
-
-  /*
-   * Return the Perpendicular distance between a point
-   * and this line. Units is degrees.
-   */
-  public double perpDistance(Node n) {
-    return perpDistance(n.eastNorth);
-  }
-
-  public double perpDistance(EastNorth en) {
-   return ptLineDist(en.east(), en.north());
-  }
-
-  public static double perpDistance(Segment s, EastNorth en) {  
-   return Line2D.ptSegDist(s.from.eastNorth.east(), s.from.eastNorth.north(), s.to.eastNorth.east(), s.to.eastNorth.north(), en.east(), en.north());
-  }
-  /*
-   * Calculate the bisector between this and another Vector. A positive offset means
-   * the bisector must point to the right of the Vectors.
-   */
-  public JosmVector bisector(JosmVector ls, double offset) {
-    JosmVector newSeg = new JosmVector(ls);
-    double newTheta = Math.PI + ls.getTheta() - getTheta();
-    newSeg.setPolar(Math.abs(offset), newSeg.getTheta() - newTheta/2.0);
-    
-    double angle = normalize(getTheta() - newSeg.getTheta());
-    if ((angle < 0 && offset > 0) || (angle > 0 && offset < 0)) {
-      newSeg.rotate(Math.PI);
-    }
-    
-//    if (newSeg.getTheta() < -PI_ON_2) {
-//      if (offset > 0) {
-//        newSeg.rotate(Math.PI);
-//      }
-//    }
-//    else if (newSeg.getTheta() > PI_ON_2) {
-//      if (offset > 0) {
-//        newSeg.rotate(-Math.PI);
-//      }
-//    }
-//    else {   
-//     if (offset < 0) {
-//       newSeg.rotate(Math.PI);
-//      }
-//    }
-    return newSeg;
-  }
-  
-  /*
-   * Return the Perpendicular Intersector from a point to this line
-   */
-  public EastNorth perpIntersect(Node n) {
-    return perpIntersect(n.eastNorth);
-  }
-  
-  public EastNorth perpIntersect(EastNorth en) {
-    
-    /*
-     * Calculate the coefficients for the two lines
-     *  1. The segment: y = ax + b
-     *  2. The perpendicular line through the new point: y = cx + d
-     */
-    double perpSlope = -1 / getSlope();    
-    double perpIntercept = en.north() - (en.east() * perpSlope);
-    
-    /*
-     * Solve the simultaneous equation to calculate the intersection
-     *  ax+b = cx+d
-     *  ax - cx = d - b
-     *  x (a-c) = d - b
-     *  x = (d - b) / (a - c) 
-     */
-    double intersectE = (perpIntercept - getIntercept()) / (getSlope() - perpSlope);
-    double intersectN = intersectE * getSlope() + getIntercept();
-    
-    return new EastNorth(intersectE, intersectN);
-  }
-//  
-//  /*
-//   * Return a compass heading  
-//   */
-//  public String direction() {
-//    double theta = getTheta();
-//    String direction = "";
-//    if (theta >= 0) {
-//      if (theta < PI_ON_2) {
-//        direction = "ne";
-//      }
-//      else {
-//        direction = "nw";
-//      }
-//    }
-//    else {
-//      if (theta < -PI_ON_2) {
-//        direction = "sw";
-//      }
-//      else {
-//        direction = "se";
-//      }
-//    }
-//    return direction;
-//  }
-}
