Index: src/org/openstreetmap/josm/actions/AverageWaysAction.java
===================================================================
--- src/org/openstreetmap/josm/actions/AverageWaysAction.java	(nonexistent)
+++ src/org/openstreetmap/josm/actions/AverageWaysAction.java	(working copy)
@@ -0,0 +1,183 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.actions;
+
+import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import javax.swing.JOptionPane;
+import javax.swing.SwingUtilities;
+
+import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.command.DeleteCommand;
+import org.openstreetmap.josm.command.MoveCommand;
+import org.openstreetmap.josm.command.SequenceCommand;
+import org.openstreetmap.josm.data.UndoRedoHandler;
+import org.openstreetmap.josm.data.coor.LatLon;
+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.Way;
+import org.openstreetmap.josm.gui.Notification;
+import org.openstreetmap.josm.tools.Logging;
+import org.openstreetmap.josm.tools.Shortcut;
+
+/**
+ * Finds the median between two or more ways
+ * @since xxx
+ */
+public class AverageWaysAction extends JosmAction {
+
+    /**
+     * Constructs a new {@link AverageWaysAction}
+     */
+    public AverageWaysAction() {
+        super(tr("Average Ways"), "average", tr("Find the median between two ways"), Shortcut.registerShortcut(
+                "tools:average", tr("Tool: {0}", tr("Average Ways")), KeyEvent.VK_C, Shortcut.SHIFT), true);
+        setHelpId(ht("/Action/AverageWays")); //TODO
+    }
+
+    @Override
+    public void actionPerformed(ActionEvent arg0) {
+        DataSet ds = getLayerManager().getEditDataSet();
+        ds.update(() -> {
+            List<Way> ways = ds.getSelectedWays().stream().filter(p -> !p.isIncomplete()).collect(Collectors.toList());
+            if (ways.size() < 2) {
+                alertSelectAtLeastTwoWays();
+                return;
+            } else if (!confirmWayWithNodesOutsideBoundingBox(ways)) {
+                return;
+            }
+
+            averageWays(ways);
+
+        });
+    }
+
+    private void averageWays(List<Way> ways) {
+        Way refWay = ways.stream().max(Comparator.comparing(Way::getNodesCount)).get();
+        AverageWay avgWay = new AverageWay(refWay);
+        ways.remove(refWay);
+        double tolerance = 2e-4; //?
+        int checkBefore = 10;
+        int checkAfter = 20;
+        int total = 0;
+        boolean checkAll = false;
+        for (Way way : ways) {
+            int offset = 0;
+            for (int r = 0; r < avgWay.size(); r++) {
+                AverageNode avgWayNode = avgWay.get(r);
+                double minDist = tolerance;
+                double lastDist = 0;
+                double distanceCounter = 0;
+                Node closestNode = null;
+                List<Node> wayNodes = way.getNodes();
+                for (int n = offset; n != offset - 1; n++) {
+                    total++;
+                    if (n > wayNodes.size() - 1) {
+                        if (offset <= 1) {
+                            break;
+                        } else {
+                            n = 0;
+                        }
+                    }
+                    Node thisWayNode = wayNodes.get(n);
+
+                    double dist = thisWayNode.getCoor().distance(avgWayNode.getRefNode().getCoor());
+                    if (dist < minDist) {
+                        minDist = dist;
+                        closestNode = thisWayNode;
+                        offset = Math.max(0, n - checkBefore);
+                    } else if (!checkAll //flag to force checking all points
+                            && r != 0 //make sure the whole way is checked at least once
+                            && closestNode != null //we found a node closer than tolerance
+                            && lastDist < dist) {
+                        //we had a close match earlier, but now we're moving away
+                        distanceCounter++;
+                        if (distanceCounter > checkAfter) {
+                            break;
+                        }
+                    }
+                    lastDist = dist;
+                }
+                if (closestNode != null)
+                    avgWayNode.addNode(closestNode);
+            }
+        }
+        List<Command> commands = new ArrayList<>(avgWay.getMoveCommands());
+        commands.add(new DeleteCommand(ways));
+        commands.addAll(ways.stream().map(Way::getNodes).map(DeleteCommand::new).collect(Collectors.toList()));
+        UndoRedoHandler.getInstance().add(new SequenceCommand(tr("Average Ways"), commands));
+        Logging.debug(total + " iterations");
+    }
+
+    class AverageWay extends ArrayList<AverageNode> {
+
+        public AverageWay(Way refWay) {
+            addAll(refWay.getNodes().stream().map(AverageNode::new).collect(Collectors.toList()));
+        }
+
+        public List<MoveCommand> getMoveCommands() {
+            return stream().map(n -> new MoveCommand(n.getRefNode(), n.getCoor())).collect(Collectors.toList());
+        }
+
+    }
+
+    class AverageNode {
+        private final Node refNode;
+        private List<Node> nodes = new ArrayList<>();
+
+        public AverageNode(Node refNode) {
+            this.refNode = refNode;
+        }
+
+        public void addNode(Node node) {
+            nodes.add(node);
+        }
+
+        public Node getRefNode() {
+            return refNode;
+        }
+
+        public LatLon getCoor() {
+            double lat = refNode.getCoor().lat();
+            double lon = refNode.getCoor().lon();
+            for (Node node : nodes) {
+                lat += node.getCoor().lat();
+                lon += node.getCoor().lon();
+            }
+            lat /= (nodes.size() + 1);
+            lon /= (nodes.size() + 1);
+            return new LatLon(lat, lon);
+        }
+
+    }
+
+    protected boolean confirmWayWithNodesOutsideBoundingBox(List<? extends OsmPrimitive> primitives) {
+        return DeleteAction.checkAndConfirmOutlyingDelete(primitives, null);
+    }
+
+    protected void alertSelectAtLeastTwoWays() {
+        SwingUtilities.invokeLater(() -> new Notification(tr("Please select at least two ways to average."))
+                .setIcon(JOptionPane.WARNING_MESSAGE).setDuration(Notification.TIME_SHORT)
+                .setHelpTopic(ht("/Action/AverageWays")).show());
+    }
+
+    @Override
+    protected void updateEnabledState() {
+        updateEnabledStateOnCurrentSelection();
+    }
+
+    @Override
+    protected void updateEnabledState(Collection<? extends OsmPrimitive> selection) {
+        updateEnabledStateOnModifiableSelection(selection);
+    }
+
+}
Index: src/org/openstreetmap/josm/gui/MainMenu.java
===================================================================
--- src/org/openstreetmap/josm/gui/MainMenu.java	(revision 16895)
+++ src/org/openstreetmap/josm/gui/MainMenu.java	(working copy)
@@ -34,6 +34,7 @@
 import org.openstreetmap.josm.actions.AlignInLineAction;
 import org.openstreetmap.josm.actions.AutoScaleAction;
 import org.openstreetmap.josm.actions.AutoScaleAction.AutoScaleMode;
+import org.openstreetmap.josm.actions.AverageWaysAction;
 import org.openstreetmap.josm.actions.ChangesetManagerToggleAction;
 import org.openstreetmap.josm.actions.CloseChangesetAction;
 import org.openstreetmap.josm.actions.CombineWayAction;
@@ -263,6 +264,8 @@
     public final ReverseWayAction reverseWay = new ReverseWayAction();
     /** Tools / Simplify Way */
     public final SimplifyWayAction simplifyWay = new SimplifyWayAction();
+    /** Tools / Average Way */
+    public final AverageWaysAction averageWays = new AverageWaysAction();
     /** Tools / Align Nodes in Circle */
     public final AlignInCircleAction alignInCircle = new AlignInCircleAction();
     /** Tools / Align Nodes in Line */
@@ -857,6 +860,7 @@
         toolsMenu.addSeparator();
         add(toolsMenu, reverseWay);
         add(toolsMenu, simplifyWay);
+        add(toolsMenu, averageWays);
         toolsMenu.addSeparator();
         add(toolsMenu, alignInCircle);
         add(toolsMenu, alignInLine);
