Index: /applications/editors/josm/plugins/terracer/src/terracer/ReverseTerraceAction.java
===================================================================
--- /applications/editors/josm/plugins/terracer/src/terracer/ReverseTerraceAction.java	(revision 13990)
+++ /applications/editors/josm/plugins/terracer/src/terracer/ReverseTerraceAction.java	(revision 13990)
@@ -0,0 +1,117 @@
+package terracer;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedList;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.command.ChangePropertyCommand;
+import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.command.SequenceCommand;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.data.osm.visitor.CollectBackReferencesVisitor;
+import org.openstreetmap.josm.tools.Shortcut;
+
+/**
+ * Tool to reverse the house numbers in a terrace.
+ *
+ * Useful for when you're using the Terracer tool and the house numbers come out
+ * in the wrong direction, or when someone has added house numbers in the wrong
+ * direction anyway.
+ * 
+ * Finds all connected ways which have a building=* tag on them in order (breadth
+ * first search) and then changes the tags to be the reverse of the order in which
+ * they were found.
+ */
+public class ReverseTerraceAction extends JosmAction {
+
+	public ReverseTerraceAction() {
+		super(tr("Reverse a terrace"), 
+				"reverse_terrace", 
+				tr("Reverses house numbers on a terrace."),
+				Shortcut.registerShortcut("tools:ReverseTerrace", 
+						tr("Tool: {0}", tr("Reverse a Terrace")),
+						KeyEvent.VK_R, Shortcut.GROUP_EDIT, 
+						Shortcut.SHIFT_DEFAULT),
+						true);
+	}
+
+	/**
+	 * Breadth-first searches based on the selection while the selection is a way
+	 * with a building=* tag and then applies the addr:housenumber tag in reverse
+	 * order.
+	 */
+	public void actionPerformed(ActionEvent e) {
+		Collection<OsmPrimitive> sel = Main.ds.getSelected();
+		
+		// set to keep track of all the nodes that have been visited - that is: if
+		// we encounter them again we will not follow onto the connected ways.
+		HashSet<Node> visitedNodes = new HashSet<Node>();
+		
+		// set to keep track of the ways the algorithm has seen, but not yet visited.
+		// since when a way is visited all of its nodes are marked as visited, there
+		// is no need to keep a visitedWays set.
+		HashSet<Way> front = new HashSet<Way>();
+
+		// initialise the set with all the buildings in the selection. this means 
+		// there is undefined behaviour when there is a multiple selection, as the
+		// ordering will be based on the hash.
+		for (OsmPrimitive prim : sel) {
+			if (prim.keySet().contains("building") && prim instanceof Way) {
+				front.add((Way)prim);
+			}
+		}
+		
+		// this is like a visitedWays set, but in a linear order.
+		LinkedList<Way> orderedWays = new LinkedList<Way>();
+		
+		// and the tags to reverse on the orderedWays.
+		LinkedList<String> houseNumbers = new LinkedList<String>();
+		
+		while (front.size() > 0) {
+			// Java apparently doesn't have useful methods to get single items from sets...
+			Way w = front.iterator().next();
+			
+			// visit all the nodes in the way, adding the building's they're members of
+			// to the front.
+			for (Node n : w.nodes) {
+				if (!visitedNodes.contains(n)) {
+					CollectBackReferencesVisitor v = new CollectBackReferencesVisitor(Main.ds);
+					v.visit(n);
+					for (OsmPrimitive prim : v.data) {
+						if (prim.keySet().contains("building") && prim instanceof Way) {
+							front.add((Way)prim);
+						}
+					}
+					visitedNodes.add(n);
+				}
+			}
+
+			// we've finished visiting this way, so record the attributes we're interested
+			// in for re-writing.
+			front.remove(w);
+			orderedWays.addLast(w);
+			houseNumbers.addFirst(w.get("addr:housenumber"));
+		}
+		
+		Collection<Command> commands = new LinkedList<Command>();
+		// what, no zipWith?
+		for (int i = 0; i < orderedWays.size(); ++i) {
+			commands.add(new ChangePropertyCommand(
+					orderedWays.get(i),
+					"addr:housenumber",
+					houseNumbers.get(i)));
+		}
+
+		Main.main.undoRedo.add(new SequenceCommand(tr("Reverse Terrace"), commands));
+		Main.ds.setSelected(orderedWays);
+	}
+
+}
Index: /applications/editors/josm/plugins/terracer/src/terracer/TerracerAction.java
===================================================================
--- /applications/editors/josm/plugins/terracer/src/terracer/TerracerAction.java	(revision 13989)
+++ /applications/editors/josm/plugins/terracer/src/terracer/TerracerAction.java	(revision 13990)
@@ -9,11 +9,36 @@
 
 import static org.openstreetmap.josm.tools.I18n.tr;
-
+import static org.openstreetmap.josm.tools.I18n.trn;
+
+import java.awt.BorderLayout;
+import java.awt.Choice;
+import java.awt.Component;
+import java.awt.GridBagLayout;
 import java.awt.event.ActionEvent;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
 import java.awt.event.KeyEvent;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.LinkedList;
-
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import javax.swing.JComponent;
+import javax.swing.JFormattedTextField;
+import javax.swing.JLabel;
 import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JSpinner;
+import javax.swing.JTextField;
+import javax.swing.SpinnerNumberModel;
+import javax.swing.SpringLayout;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
 
 import org.openstreetmap.josm.Main;
@@ -25,5 +50,11 @@
 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.gui.tagging.TaggingPreset.Item;
+import org.openstreetmap.josm.tools.AutoCompleteComboBox;
+import org.openstreetmap.josm.tools.GBC;
+import org.openstreetmap.josm.tools.Pair;
 import org.openstreetmap.josm.tools.Shortcut;
 
@@ -40,4 +71,8 @@
 public final class TerracerAction extends JosmAction {
 
+	// smsms1 asked for the last value to be remembered to make it easier to do
+	// repeated terraces. this is the easiest, but not necessarily nicest, way.
+	private static String lastSelectedValue = "";
+
 	public TerracerAction() {
 		super(tr("Terrace a building"), 
@@ -65,15 +100,29 @@
 				Way way = (Way)prim;
 
-				if ((way.nodes.size() == 5) &&
+				if ((way.nodes.size() >= 5) &&
 						way.isClosed()) {
 					// first ask the user how many buildings to terrace into
-					String answer = 
-						JOptionPane.showInputDialog(
-								tr("How many buildings are in the terrace?"));
-					
-					// if the answer was null then the user clicked "Cancel"
-					if (answer != null) {
-						int nb = Integer.parseInt(answer);
-						terraceBuilding(way, nb);
+					HouseNumberDialog dialog = new HouseNumberDialog();
+					final JOptionPane optionPane = new JOptionPane(dialog, JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION);
+
+					String title = trn("Change {0} object", "Change {0} objects", sel.size(), sel.size());
+					if(sel.size() == 0)
+						title = tr("Nothing selected!");
+
+					optionPane.createDialog(Main.parent, title).setVisible(true);
+					Object answerObj = optionPane.getValue();
+					if (answerObj != null && 
+							answerObj != JOptionPane.UNINITIALIZED_VALUE &&
+							(answerObj instanceof Integer && 
+									(Integer)answerObj == JOptionPane.OK_OPTION)) {
+
+						// call out to the method which does the actual
+						// terracing.
+						terraceBuilding(way, 
+								dialog.numberFrom(), 
+								dialog.numberTo(),
+								dialog.stepSize(),
+								dialog.streetName());
+
 					}
 				} else {
@@ -89,5 +138,5 @@
 		if (badSelect) {
 			JOptionPane.showMessageDialog(Main.parent, 
-					tr("Select a single, closed way of four nodes."));
+					tr("Select a single, closed way of at least four nodes."));
 		}
 	}
@@ -102,57 +151,367 @@
 	 * 
 	 * @param w The closed, quadrilateral way to terrace.
-	 * @param nb The number of buildings to terrace into.
-	 */
-	private void terraceBuilding(Way w, int nb) {
+	 */
+	private void terraceBuilding(Way w, int from, int to, int step, String streetName) {
+		final int nb = 1 + (to - from) / step;
 
 		// now find which is the longest side connecting the first node
-		Node[] nodes = w.nodes.toArray(new Node[] {});
-		double side1 = nodes[0].coor.greatCircleDistance(nodes[1].coor);
-		double side2 = nodes[0].coor.greatCircleDistance(nodes[3].coor);
-		
+		Pair<Way,Way> interp = findFrontAndBack(w);
+
+		final double frontLength = wayLength(interp.a);
+		final double backLength = wayLength(interp.b);
+
 		// new nodes array to hold all intermediate nodes
 		Node[][] new_nodes = new Node[2][nb + 1];
-		
-		if (side1 > side2) {
-			new_nodes[0][0] = nodes[0];
-			new_nodes[0][nb] = nodes[1];
-			new_nodes[1][0] = nodes[3];
-			new_nodes[1][nb] = nodes[2];
-		} else {
-			new_nodes[0][0] = nodes[0];
-			new_nodes[0][nb] = nodes[3];
-			new_nodes[1][0] = nodes[1];
-			new_nodes[1][nb] = nodes[2];
-		}
-			
-        Collection<Command> commands = new LinkedList<Command>();
-        Collection<Way> ways = new LinkedList<Way>();
-
-        // create intermediate nodes by interpolating. 
-        for (int i = 1; i < nb; ++i) {
-			new_nodes[0][i] = interpolateNode(new_nodes[0][0], new_nodes[0][nb],
-					(double)(i)/(double)(nb));
-			new_nodes[1][i] = interpolateNode(new_nodes[1][0], new_nodes[1][nb],
-					(double)(i)/(double)(nb));
+
+		Collection<Command> commands = new LinkedList<Command>();
+		Collection<Way> ways = new LinkedList<Way>();
+
+		// create intermediate nodes by interpolating. 
+		for (int i = 0; i <= nb; ++i) {
+			new_nodes[0][i] = interpolateAlong(interp.a, frontLength * (double)(i) / (double)(nb));
+			new_nodes[1][i] = interpolateAlong(interp.b, backLength * (double)(i) / (double)(nb));
 			commands.add(new AddCommand(new_nodes[0][i]));
 			commands.add(new AddCommand(new_nodes[1][i]));
 		}
 
-        // assemble new quadrilateral, closed ways
-        for (int i = 0; i < nb; ++i) {
-        	Way terr = new Way();
-        	// Using Way.nodes.add rather than Way.addNode because the latter doesn't
-        	// exist in older versions of JOSM.
-        	terr.nodes.add(new_nodes[0][i]);
-        	terr.nodes.add(new_nodes[0][i+1]);
-        	terr.nodes.add(new_nodes[1][i+1]);
-        	terr.nodes.add(new_nodes[1][i]);
-        	terr.nodes.add(new_nodes[0][i]);
-        	ways.add(terr);
-        	commands.add(new AddCommand(terr));
-        }
-        
-        Main.main.undoRedo.add(new SequenceCommand(tr("Terrace"), commands));
-        Main.ds.setSelected(ways);
+		// create a new relation for addressing
+		Relation relatedStreet = new Relation();
+		relatedStreet.put("type", "relatedStreet");
+		if (streetName != null) {
+			relatedStreet.put("name", streetName);
+		}
+		// note that we don't actually add the street member to the relation, as
+		// the name isn't unambiguous and it could cause confusion if the editor were
+		// to automatically select one which wasn't the one the user intended.
+
+		// assemble new quadrilateral, closed ways
+		for (int i = 0; i < nb; ++i) {
+			Way terr = new Way();
+			// Using Way.nodes.add rather than Way.addNode because the latter doesn't
+			// exist in older versions of JOSM.
+			terr.nodes.add(new_nodes[0][i]);
+			terr.nodes.add(new_nodes[0][i+1]);
+			terr.nodes.add(new_nodes[1][i+1]);
+			terr.nodes.add(new_nodes[1][i]);
+			terr.nodes.add(new_nodes[0][i]);
+			terr.put("addr:housenumber", "" + (from + i * step));
+			terr.put("building", "yes");
+			if (streetName != null) {
+				terr.put("addr:street", streetName);
+			}
+			relatedStreet.members.add(new RelationMember("house", terr));
+			ways.add(terr);
+			commands.add(new AddCommand(terr));
+		}
+
+		commands.add(new AddCommand(relatedStreet));
+
+		Main.main.undoRedo.add(new SequenceCommand(tr("Terrace"), commands));
+		Main.ds.setSelected(ways);
+	}
+
+	/**
+	 * Creates a node at a certain distance along a way, as calculated by the
+	 * great circle distance.
+	 * 
+	 * Note that this really isn't an efficient way to do this and leads to 
+	 * O(N^2) running time for the main algorithm, but its simple and easy
+	 * to understand, and probably won't matter for reasonable-sized ways.
+	 * 
+	 * @param w The way to interpolate.
+	 * @param l The length at which to place the node.
+	 * @return A node at a distance l along w from the first point.
+	 */
+	private Node interpolateAlong(Way w, double l) {
+		Node n = null;
+		for (Pair<Node,Node> p : w.getNodePairs(false)) {
+			final double seg_length = p.a.coor.greatCircleDistance(p.b.coor);
+			if (l <= seg_length) {
+				n = interpolateNode(p.a, p.b, l / seg_length);
+				break;
+			} else {
+				l -= seg_length;
+			}
+		}
+		if (n == null) {
+			// sometimes there is a small overshoot due to numerical roundoff, so we just
+			// set these cases to be equal to the last node. its not pretty, but it works ;-)
+			n = w.nodes.get(w.nodes.size() - 1);
+		}
+		return n;
+	}
+
+	/**
+	 * Calculates the great circle length of a way by summing the great circle
+	 * distance of each pair of nodes.
+	 * 
+	 * @param w The way to calculate length of.
+	 * @return The length of the way.
+	 */
+	private double wayLength(Way w) {
+		double length = 0.0;
+		for (Pair<Node,Node> p : w.getNodePairs(false)) {
+			length += p.a.coor.greatCircleDistance(p.b.coor);
+		}
+		return length;
+	}
+
+	/**
+	 * Given a way, try and find a definite front and back by looking at the 
+	 * segments to find the "sides". Sides are assumed to be single segments
+	 * which cannot be contiguous.
+	 * 
+	 * @param w The way to analyse.
+	 * @return A pair of ways (front, back) pointing in the same directions.
+	 */
+	private Pair<Way, Way> findFrontAndBack(Way w) {
+		// calculate the "side-ness" score for each segment of the way
+		double[] sideness = calculateSideness(w);
+
+		// find the largest two sidenesses which are not contiguous
+		int[] indexes = sortedIndexes(sideness);
+		int side1 = indexes[0];
+		int side2 = indexes[1];
+		// if side2 is contiguous with side1 then look further down the
+		// list. we know there are at least 4 sides, as anything smaller
+		// than a quadrilateral would have been rejected at an earlier
+		// stage.
+		if (Math.abs(side1 - side2) < 2) {
+			side2 = indexes[2];
+		}
+		if (Math.abs(side1 - side2) < 2) {
+			side2 = indexes[3];
+		}
+
+		// swap side1 and side2 into sorted order.
+		if (side1 > side2) {
+			// i can't believe i have to write swap() myself - surely java standard
+			// library has this somewhere??!!?ONE!
+			int tmp = side2;
+			side2 = side1;
+			side1 = tmp;
+		}
+
+		Way front = new Way();
+		Way back = new Way();
+		for (int i = side2 + 1; i < w.nodes.size() - 1; ++i) {
+			front.nodes.add(w.nodes.get(i));
+		}
+		for (int i = 0; i <= side1; ++i) {
+			front.nodes.add(w.nodes.get(i));
+		}
+		// add the back in reverse order so that the front and back ways point
+		// in the same direction.
+		for (int i = side2; i > side1; --i) {
+			back.nodes.add(w.nodes.get(i));
+		}
+
+		return new Pair<Way, Way>(front, back);
+	}
+
+	/**
+	 * Given an array of doubles (but this could made generic very easily) sort
+	 * into order and return the array of indexes such that, for a returned array
+	 * x, a[x[i]] is sorted for ascending index i.
+	 * 
+	 * This isn't efficient at all, but should be fine for the small arrays we're
+	 * expecting. If this gets slow - replace it with some more efficient algorithm.
+	 * 
+	 * @param a The array to sort.
+	 * @return An array of indexes, the same size as the input, such that a[x[i]] 
+	 * is in sorted order.
+	 */
+	private int[] sortedIndexes(final double[] a) {
+		class SortWithIndex implements Comparable<SortWithIndex> {
+			public double x;
+			public int i;
+			public SortWithIndex(double a, int b) {
+				x = a; i = b;
+			}
+			public int compareTo(SortWithIndex o) {
+				return Double.compare(x, o.x);
+			};
+		}
+
+		final int length = a.length;
+		ArrayList<SortWithIndex> sortable = new ArrayList<SortWithIndex>(length);
+		for (int i = 0; i < length; ++i) {
+			sortable.add(new SortWithIndex(a[i], i));
+		}
+		Collections.sort(sortable);
+
+		int[] indexes = new int[length];
+		for (int i = 0; i < length; ++i) {
+			indexes[i] = sortable.get(i).i;
+		}
+
+		return indexes;
+	}
+
+	/**
+	 * Calculate "sideness" metric for each segment in a way.
+	 */
+	private double[] calculateSideness(Way w) {
+		final int length = w.nodes.size() - 1;
+		double[] sideness = new double[length];
+
+		sideness[0] = calculateSideness(
+				w.nodes.get(length - 1), w.nodes.get(0),
+				w.nodes.get(1), w.nodes.get(2));
+		for (int i = 1; i < length - 1; ++i) {
+			sideness[i] = calculateSideness(
+					w.nodes.get(i-1), w.nodes.get(i),
+					w.nodes.get(i+1), w.nodes.get(i+2));
+		}
+		sideness[length-1] = calculateSideness(
+				w.nodes.get(length - 2), w.nodes.get(length - 1), 
+				w.nodes.get(length), w.nodes.get(1));
+
+		return sideness;
+	}
+
+	/**
+	 * Calculate sideness of a single segment given the nodes which make up that
+	 * segment and its previous and next segments in order. Sideness is calculated
+	 * for the segment b-c.
+	 */
+	private double calculateSideness(Node a, Node b, Node c, Node d) {
+		final double ndx = b.coor.getX() - a.coor.getX();
+		final double pdx = d.coor.getX() - c.coor.getX();
+		final double ndy = b.coor.getY() - a.coor.getY();
+		final double pdy = d.coor.getY() - c.coor.getY();
+
+		return (ndx * pdx + ndy * pdy) / 
+		Math.sqrt((ndx * ndx + ndy * ndy) * (pdx * pdx + pdy * pdy));
+	}
+
+	/**
+	 * Dialog box to allow users to input housenumbers in a nice way.
+	 */
+	class HouseNumberDialog extends JPanel {
+		private SpinnerNumberModel lo, hi;
+		private JSpinner clo, chi;
+		private Choice step;
+		private AutoCompleteComboBox street;
+
+		public HouseNumberDialog() {
+			super(new GridBagLayout());
+			lo = new SpinnerNumberModel(1, 1, 1, 1);
+			hi = new SpinnerNumberModel(1, 1, null, 1);
+			step = new Choice();
+			step.add(tr("All"));
+			step.add(tr("Even"));
+			step.add(tr("Odd"));
+			clo = new JSpinner(lo);
+			chi = new JSpinner(hi);
+
+			lo.addChangeListener(new ChangeListener() { 
+				public void stateChanged(ChangeEvent e) {
+					hi.setMinimum((Integer)lo.getNumber());
+				}
+			});
+			hi.addChangeListener(new ChangeListener() { 
+				public void stateChanged(ChangeEvent e) {
+					lo.setMaximum((Integer)hi.getNumber());
+				}
+			});
+			step.addItemListener(new ItemListener() {
+				public void itemStateChanged(ItemEvent e) {
+					if (step.getSelectedItem() == tr("All")) {
+						hi.setStepSize(1);
+						lo.setStepSize(1);
+					} else {
+						int odd_or_even = 0;
+						int min = 0;
+
+						if (step.getSelectedItem() == tr("Even")) {
+							odd_or_even = 0;
+							min = 2;
+						} else {
+							odd_or_even = 1;
+							min = 1;
+						}
+
+						if ((lo.getNumber().intValue() & 1) != odd_or_even) {
+							int nextval = lo.getNumber().intValue() - 1;
+							lo.setValue((nextval > min) ? nextval : min);
+						}
+						if ((hi.getNumber().intValue() & 1) != odd_or_even) {
+							int nextval = hi.getNumber().intValue() - 1;
+							hi.setValue((nextval > min) ? nextval : min);
+						}
+						lo.setMinimum(min);
+						hi.setStepSize(2);
+						lo.setStepSize(2);
+					}
+				}
+			});
+
+			final TreeSet<String> names = createAutoCompletionInfo();
+
+			street = new AutoCompleteComboBox();
+			street.setPossibleItems(names);
+			street.setEditable(true);
+			street.setSelectedItem(null);
+
+			JFormattedTextField x;
+			x = ((JSpinner.DefaultEditor)clo.getEditor()).getTextField();
+			x.setColumns(5);
+			x = ((JSpinner.DefaultEditor)chi.getEditor()).getTextField();
+			x.setColumns(5);
+			addLabelled("From number: ", clo);
+			addLabelled("To number: ",   chi);
+			addLabelled("Interpolation: ", step);
+			addLabelled("Street name (Optional): ", street);
+		}
+
+		private void addLabelled(String str, Component c) {
+			JLabel label = new JLabel(str);
+			add(label, GBC.std());
+			label.setLabelFor(c);
+			add(c, GBC.eol());
+		}
+
+		public int numberFrom() {
+			return lo.getNumber().intValue();
+		}
+
+		public int numberTo() {
+			return hi.getNumber().intValue();
+		}
+
+		public int stepSize() {
+			return (step.getSelectedItem() == tr("All")) ? 1 : 2;
+		}
+
+		public String streetName() {
+			Object selected = street.getSelectedItem();
+			if (selected == null) {
+				return null;
+			} else {
+				String name = selected.toString();
+				if (name.isEmpty()) {
+					return null;
+				} else {
+					return name;
+				}
+			}
+		}
+	}
+
+	/**
+	 * Generates a list of all visible names of highways in order to do 
+	 * autocompletion on the road name.
+	 */
+	private TreeSet<String> createAutoCompletionInfo() {
+		final TreeSet<String> names = new TreeSet<String>();
+		for (OsmPrimitive osm : Main.ds.allNonDeletedPrimitives()) {
+			if (osm.keys != null &&
+					osm.keys.containsKey("highway") &&
+					osm.keys.containsKey("name")) {
+				names.add(osm.keys.get("name"));
+			}
+		}
+		return names;
 	}
 
@@ -167,12 +526,23 @@
 	 */
 	private Node interpolateNode(Node a, Node b, double f) {
-        // this isn't quite right - we should probably be interpolating
+		Node n = new Node(interpolateLatLon(a, b, f));
+		return n;
+	}
+
+	/**
+	 * Calculates the interpolated position between the argument nodes. Interpolates 
+	 * linearly in Lat/Lon coordinates.
+	 * 
+	 * @param a First node, at which f=0.
+	 * @param b Last node, at which f=1.
+	 * @param f Fractional position between first and last nodes.
+	 * @return The interpolated position.
+	 */
+	private LatLon interpolateLatLon(Node a, Node b, double f) {
+		// this isn't quite right - we should probably be interpolating
 		// screen coordinates rather than lat/lon, but it doesn't seem to
 		// make a great deal of difference at the scale of most terraces.
-		Node n = new Node(new LatLon(
-				a.coor.lat() * (1.0 - f) + b.coor.lat() * f,
-				a.coor.lon() * (1.0 - f) + b.coor.lon() * f
-				));
-		return n;
+		return new LatLon(a.coor.lat() * (1.0 - f) + b.coor.lat() * f,
+				a.coor.lon() * (1.0 - f) + b.coor.lon() * f);
 	}
 }
Index: /applications/editors/josm/plugins/terracer/src/terracer/TerracerPlugin.java
===================================================================
--- /applications/editors/josm/plugins/terracer/src/terracer/TerracerPlugin.java	(revision 13989)
+++ /applications/editors/josm/plugins/terracer/src/terracer/TerracerPlugin.java	(revision 13990)
@@ -20,4 +20,5 @@
 	public TerracerPlugin() {
 		MainMenu.add(Main.main.menu.toolsMenu, new TerracerAction());
+		MainMenu.add(Main.main.menu.toolsMenu, new ReverseTerraceAction());
 	}
 }
