Index: /applications/editors/josm/plugins/public_transport_layer/src/ptl/DistanceBetweenStops.java
===================================================================
--- /applications/editors/josm/plugins/public_transport_layer/src/ptl/DistanceBetweenStops.java	(revision 32142)
+++ /applications/editors/josm/plugins/public_transport_layer/src/ptl/DistanceBetweenStops.java	(revision 32142)
@@ -0,0 +1,162 @@
+package ptl;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.data.SystemOfMeasurement;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.osm.Node;
+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.gui.DefaultNameFormatter;
+import org.openstreetmap.josm.gui.ExtendedDialog;
+import org.openstreetmap.josm.gui.dialogs.relation.sort.WayConnectionType;
+import org.openstreetmap.josm.gui.dialogs.relation.sort.WayConnectionTypeCalculator;
+import org.openstreetmap.josm.gui.util.GuiHelper;
+import org.openstreetmap.josm.gui.widgets.JosmTextArea;
+import org.openstreetmap.josm.tools.CheckParameterUtil;
+import org.openstreetmap.josm.tools.Utils;
+
+public class DistanceBetweenStops extends JosmAction {
+
+    public DistanceBetweenStops() {
+        super(tr("Distance between stops"), null, null, null, false);
+    }
+
+    static String calculateDistanceBetweenStops(final Relation route) {
+        CheckParameterUtil.ensureThat(isRouteSupported(route), "A valid public_transport:version=2 route is required");
+
+        final List<Node> stopNodes = new ArrayList<>();
+        final List<RelationMember> routeSegments = new ArrayList<>();
+        final List<Node> routeNodes = new ArrayList<>();
+        for (final RelationMember member : route.getMembers()) {
+            if (member.hasRole("stop", "stop_exit_only", "stop_entry_only") && OsmPrimitiveType.NODE.equals(member.getType())) {
+                stopNodes.add(member.getNode());
+            } else if (member.hasRole("") && OsmPrimitiveType.WAY.equals(member.getType())) {
+                routeSegments.add(member);
+            }
+        }
+
+        final WayConnectionTypeCalculator connectionTypeCalculator = new WayConnectionTypeCalculator();
+        final List<WayConnectionType> links = connectionTypeCalculator.updateLinks(routeSegments);
+        for (int i = 0; i < links.size(); i++) {
+            final WayConnectionType link = links.get(i);
+            final WayConnectionType.Direction direction = link.direction;
+            final List<Node> nodes = routeSegments.get(i).getWay().getNodes();
+            switch (link.direction) {
+                case BACKWARD:
+                    Collections.reverse(nodes);
+                    // fall through
+                case FORWARD:
+                    routeNodes.addAll(link.linkPrev ? nodes.subList(1, nodes.size()) : nodes);
+                    break;
+            }
+        }
+
+        final StringBuilder sb = new StringBuilder();
+        Node lastN = null;
+        List<Node> remainingRouteNodes = routeNodes;
+        double totalLength = 0.0;
+        int lengthN = 0;
+        final boolean onlyLowerUnit = Main.pref.getBoolean("system_of_measurement.use_only_lower_unit", false);
+        Main.pref.put("system_of_measurement.use_only_lower_unit", true);
+        try {
+            for (Node n : stopNodes) {
+                final double length;
+                if (lastN != null) {
+                    if (remainingRouteNodes.indexOf(lastN) > 0) {
+                        remainingRouteNodes = remainingRouteNodes.subList(remainingRouteNodes.indexOf(lastN), remainingRouteNodes.size());
+                    }
+                    if (remainingRouteNodes.indexOf(n) > 0) {
+                        final List<Node> segmentBetweenStops = remainingRouteNodes.subList(0, remainingRouteNodes.indexOf(n) + 1);
+                        length = getLength(segmentBetweenStops);
+                        totalLength += length;
+                        lengthN++;
+                    } else {
+                        length = Double.NaN;
+                    }
+                } else {
+                    length = 0.0;
+                }
+                sb.append(SystemOfMeasurement.getSystemOfMeasurement().getDistText(length, new DecimalFormat("0"), -1));
+                sb.append("\t");
+                sb.append(n.getDisplayName(DefaultNameFormatter.getInstance()));
+                sb.append("\n");
+                lastN = n;
+            }
+            sb.insert(0, "\n");
+            sb.insert(0, route.getDisplayName(DefaultNameFormatter.getInstance()));
+            sb.insert(0, "\t");
+            sb.insert(0, SystemOfMeasurement.getSystemOfMeasurement().getDistText(totalLength / lengthN, new DecimalFormat("0"), -1));
+        } finally {
+            Main.pref.put("system_of_measurement.use_only_lower_unit", onlyLowerUnit);
+        }
+
+        return sb.toString();
+    }
+
+    private static boolean isRouteSupported(Relation route) {
+        return !route.hasIncompleteMembers()
+                && route.hasTag("type", "route")
+                && route.hasKey("route")
+                && route.hasTag("public_transport:version", "2");
+    }
+
+    private static double getLength(Iterable<Node> nodes) {
+        double length = 0;
+        Node lastN = null;
+        for (Node n : nodes) {
+            if (lastN != null) {
+                LatLon lastNcoor = lastN.getCoor();
+                LatLon coor = n.getCoor();
+                if (lastNcoor != null && coor != null) {
+                    length += coor.greatCircleDistance(lastNcoor);
+                }
+            }
+            lastN = n;
+        }
+        return length;
+    }
+
+    @Override
+    public void actionPerformed(ActionEvent e) {
+        if (getCurrentDataSet() == null) {
+            return;
+        }
+        final StringBuilder sb = new StringBuilder();
+        for (Relation relation : getCurrentDataSet().getSelectedRelations()) {
+            if (!isRouteSupported(relation)) {
+                JOptionPane.showMessageDialog(Main.parent, "<html>" + tr("A valid public_transport:version=2 route is required")
+                        + Utils.joinAsHtmlUnorderedList(Collections.singleton(relation.getDisplayName(DefaultNameFormatter.getInstance()))),
+                        tr("Invalid selection"), JOptionPane.WARNING_MESSAGE);
+                continue;
+            }
+            sb.append("\n");
+            sb.append(calculateDistanceBetweenStops(relation)).append("\n");
+        }
+
+        new ExtendedDialog(Main.parent, getValue(NAME).toString(), new String[]{tr("Close")}) {
+            {
+                setButtonIcons(new String[]{"ok.png"});
+                final JosmTextArea jte = new JosmTextArea();
+                jte.setFont(GuiHelper.getMonospacedFont(jte));
+                jte.setEditable(false);
+                jte.append(sb.toString());
+                jte.setSelectionStart(0);
+                jte.setSelectionEnd(0);
+                setContent(jte);
+            }
+
+        }.showDialog();
+    }
+}
Index: /applications/editors/josm/plugins/public_transport_layer/src/ptl/Plugin.java
===================================================================
--- /applications/editors/josm/plugins/public_transport_layer/src/ptl/Plugin.java	(revision 32141)
+++ /applications/editors/josm/plugins/public_transport_layer/src/ptl/Plugin.java	(revision 32142)
@@ -10,4 +10,5 @@
         super(info);
         MainMenu.add(Main.main.menu.dataMenu, new PublicTransportLayer.AddLayerAction());
+        MainMenu.add(Main.main.menu.dataMenu, new DistanceBetweenStops());
     }
 }
