Index: relcontext/RelContextDialog.java
===================================================================
--- relcontext/RelContextDialog.java	(revision 30769)
+++ relcontext/RelContextDialog.java	(working copy)
@@ -88,6 +88,7 @@
 import relcontext.actions.EditChosenRelationAction;
 import relcontext.actions.FindRelationAction;
 import relcontext.actions.ReconstructPolygonAction;
+import relcontext.actions.ReconstructRouteAction;
 import relcontext.actions.RelationHelpAction;
 import relcontext.actions.SelectInRelationPanelAction;
 import relcontext.actions.SelectMembersAction;
@@ -423,6 +424,7 @@
 
     private static Map<String, List<String>> loadRoles() {
         Map<String, List<String>> result = new HashMap<>();
+
         ClassLoader classLoader = RelContextDialog.class.getClassLoader();
         try (
             InputStream possibleRolesStream = classLoader.getResourceAsStream(POSSIBLE_ROLES_FILE);
@@ -525,6 +527,7 @@
             add(new DeleteChosenRelationAction(chosenRelation));
             add(new DownloadParentsAction(chosenRelation));
             add(new ReconstructPolygonAction(chosenRelation));
+            add(new ReconstructRouteAction(chosenRelation));
             addSeparator();
             add(new SelectInRelationPanelAction(chosenRelation));
             add(new RelationHelpAction(chosenRelation));
Index: relcontext/actions/PublicTransportHelper.java
===================================================================
--- relcontext/actions/PublicTransportHelper.java	(revision 0)
+++ relcontext/actions/PublicTransportHelper.java	(working copy)
@@ -0,0 +1,133 @@
+package relcontext.actions;
+
+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.RelationMember;
+import org.openstreetmap.josm.data.osm.Way;
+
+/**
+ * @see http://wiki.openstreetmap.org/wiki/Key:public_transport
+ */
+
+/**
+ *
+ * @author freeExec
+ */
+public final class PublicTransportHelper {
+
+    public final static String PUBLIC_TRANSPORT = "public_transport";
+    public final static String STOP_POSITION = "stop_position";
+    public final static String STOP = "stop";
+    public final static String STOP_AREA = "stop_area";
+    public final static String PLATFORM = "platform";
+    public final static String HIGHWAY = "highway";
+    public final static String RAILWAY = "railway";
+    public final static String BUS_STOP = "bus_stop";
+    public final static String RAILWAY_HALT = "halt";
+    public final static String RAILWAY_STATION = "station";    
+    
+    private PublicTransportHelper() {
+        // Hide default constructor for utils classes
+    }        
+    
+    public static String getRoleByMember(RelationMember m) {
+        if (isMemberStop(m)) return STOP;
+        else if (isMemberPlatform(m)) return PLATFORM;
+        return null;
+    }
+    
+    public static boolean isMemberStop(RelationMember m) {
+        return isNodeStop(m);   // stop is only node
+    }
+    
+    public static boolean isMemberPlatform(RelationMember m) {
+        return isNodePlatform(m) || isWayPlatform(m);
+    }
+    
+    public static boolean isNodeStop(RelationMember m) {
+        return isNodeStop(m.getMember());
+    }
+    
+    public static boolean isNodeStop(OsmPrimitive p) {
+        if (p.getType() == OsmPrimitiveType.NODE && !p.isIncomplete()) {
+            if (p.hasKey(PUBLIC_TRANSPORT)) {
+                String pt = p.get(PUBLIC_TRANSPORT);
+                if (STOP_POSITION.equals(pt)) return true;
+            }
+            else if (p.hasKey(RAILWAY)) {
+                String rw = p.get(RAILWAY);
+                if (RAILWAY_HALT.equals(rw) || RAILWAY_STATION.equals(rw)) return true;
+            }
+        }
+        return false;
+    }
+    
+    public static boolean isNodePlatform(RelationMember m) {
+        return isNodePlatform(m.getMember());
+    }
+    
+    public static boolean isNodePlatform(OsmPrimitive p) {
+        if (p.getType() == OsmPrimitiveType.NODE && !p.isIncomplete()) {
+            if (p.hasKey(PUBLIC_TRANSPORT)) {
+                String pt = p.get(PUBLIC_TRANSPORT);
+                if (PLATFORM.equals(pt)) return true;
+            } else if (p.hasKey(HIGHWAY)) {
+                String hw = p.get(HIGHWAY);
+                if (BUS_STOP.equals(hw)) return true;
+                else if (PLATFORM.equals(hw)) return true;
+            } else if (p.hasKey(RAILWAY)) {
+                String rw = p.get(RAILWAY);
+                if (PLATFORM.equals(rw)) return true;
+            }
+        }
+        return false;
+    }
+    public static boolean isWayPlatform(RelationMember m) {
+        return isWayPlatform(m.getMember());
+    }
+    
+    public static boolean isWayPlatform(OsmPrimitive p) {
+        if (p.getType() == OsmPrimitiveType.WAY && !p.isIncomplete()) {
+            if (p.hasKey(PUBLIC_TRANSPORT)) {
+                String pt = p.get(PUBLIC_TRANSPORT);
+                if (PLATFORM.equals(pt)) return true;
+            } else if (p.hasKey(HIGHWAY)) {
+                String hw = p.get(HIGHWAY);
+                if (PLATFORM.equals(hw)) return true;
+            } else if (p.hasKey(RAILWAY)) {
+                String rw = p.get(RAILWAY);
+                if (PLATFORM.equals(rw)) return true;
+            }
+        }
+        return false;
+    }
+    
+    public static boolean isMemberRouteway(RelationMember m) {
+        return isWayRouteway(m.getMember());
+    }
+    
+    public static boolean isWayRouteway(OsmPrimitive p) {
+        if (p.getType() == OsmPrimitiveType.WAY && !p.isIncomplete()) {
+            return p.hasKey(HIGHWAY) || p.hasKey(RAILWAY);
+        }
+        return false;
+    }
+    
+    public static String getNameViaStoparea(RelationMember m) {
+        return getNameViaStoparea(m.getMember());
+    }
+    
+    public static String getNameViaStoparea(OsmPrimitive prim) {
+        String result = prim.getName();
+        if (result != null) return result;
+        // try to get name by stop_area
+        for (OsmPrimitive refOp : prim.getReferrers())
+            if (refOp.getType() == OsmPrimitiveType.RELATION
+                && refOp.hasTag(PUBLIC_TRANSPORT, STOP_AREA)) {
+                result = refOp.getName();
+                if (result != null) return result;
+            }
+        return result;
+    }    
+}
Index: relcontext/actions/ReconstructRouteAction.java
===================================================================
--- relcontext/actions/ReconstructRouteAction.java	(revision 0)
+++ relcontext/actions/ReconstructRouteAction.java	(working copy)
@@ -0,0 +1,205 @@
+package relcontext.actions;
+
+import java.awt.event.ActionEvent;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import javax.swing.AbstractAction;
+import static javax.swing.Action.LONG_DESCRIPTION;
+import static javax.swing.Action.SMALL_ICON;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.command.ChangeCommand;
+import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.data.coor.EastNorth;
+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.gui.dialogs.relation.sort.RelationSorter;
+import org.openstreetmap.josm.tools.Geometry;
+import static org.openstreetmap.josm.tools.I18n.tr;
+import org.openstreetmap.josm.tools.ImageProvider;
+import relcontext.ChosenRelation;
+import relcontext.ChosenRelationListener;
+
+/**
+ * Build in order stop/platforms, stop/platforms ... route
+ * @author freeExec
+ */
+public class ReconstructRouteAction  extends AbstractAction implements ChosenRelationListener {
+    private final ChosenRelation rel;
+    
+    public ReconstructRouteAction (ChosenRelation rel) {
+        super(tr("Reconstruct route"));
+        putValue(SMALL_ICON, ImageProvider.get("dialogs", "filter"));
+        putValue(LONG_DESCRIPTION, "Reconstruct route relation to scheme of public_transport");
+        this.rel = rel;
+        rel.addChosenRelationListener(this);
+        setEnabled(isSuitableRelation(rel.get()));
+    }
+    
+    public void actionPerformed( ActionEvent e ) {
+        Relation r = rel.get();
+        Relation recRel = new Relation(r);        
+        recRel.removeMembersFor(recRel.getMemberPrimitives());
+        
+        Map<OsmPrimitive, RelationMember> stopMembers = new LinkedHashMap<>();
+        Map<String, List<RelationMember>> platformMembers = new LinkedHashMap<>();
+
+        List<RelationMember> routeMembers = new ArrayList<>();
+        List<RelationMember> wtfMembers = new ArrayList<>();
+        
+        int mCount = r.getMembersCount();
+        for (int i = 0; i < mCount; i++) {
+            RelationMember m = r.getMember(i);
+            if (PublicTransportHelper.isMemberStop(m)) {
+                RelationMember rm = new RelationMember(
+                        m.hasRole() ? m.getRole() : PublicTransportHelper.STOP, 
+                        m.getMember());
+                stopMembers.put(rm.getMember(), rm);
+            }
+            else if (PublicTransportHelper.isMemberPlatform(m)) {
+                RelationMember rm = new RelationMember(
+                        m.hasRole() ? m.getRole() : PublicTransportHelper.PLATFORM, 
+                        m.getMember());
+                String platformName = PublicTransportHelper.getNameViaStoparea(rm);
+                if (platformName == null) platformName = "";
+                if (platformMembers.containsKey(platformName)) platformMembers.get(platformName).add(rm);
+                else {
+                    List<RelationMember> nList = new ArrayList<>();
+                    nList.add(rm);
+                    platformMembers.put(platformName, nList);
+                }                
+            }
+            else if (PublicTransportHelper.isMemberRouteway(m)) routeMembers.add(new RelationMember(m));
+            else wtfMembers.add(new RelationMember(m));
+        }
+        
+        routeMembers = RelationSorter.sortMembersByConnectivity(routeMembers);
+        
+        Node lastNode = null;
+        for (int rIndex = 0; rIndex < routeMembers.size(); rIndex++) {
+            Way w = (Way)routeMembers.get(rIndex).getMember();
+            boolean dirForward = false;
+            if (lastNode == null) { // first segment
+                if (routeMembers.size() > 2) {
+                    Way nextWay = (Way)routeMembers.get(rIndex + 1).getMember();
+                    if (w.lastNode().equals(nextWay.lastNode()) || w.lastNode().equals(nextWay.firstNode())) {
+                        dirForward = true;
+                        lastNode = w.lastNode();
+                    } else lastNode = w.firstNode();
+                } // else one segment - direction unknown
+            } else {
+                if (lastNode.equals(w.firstNode())) { dirForward = true; lastNode = w.lastNode(); }
+                else lastNode = w.firstNode();
+            }
+            final int wayNodeBeginIndex = (dirForward ? 0 : w.getNodesCount() - 1);
+            final int wayNodeEndIndex = (dirForward ? w.getNodesCount() - 1 : 0);
+            final int increment = (dirForward ? 1 : -1);
+            for(int nIndex = wayNodeBeginIndex;
+                    nIndex != wayNodeEndIndex;
+                    nIndex += increment) {
+                Node refNode = w.getNode(nIndex);
+                if (PublicTransportHelper.isNodeStop(refNode)) {
+                    if (stopMembers.containsKey(refNode)) {
+                        recRel.addMember(stopMembers.get(refNode));
+                        stopMembers.remove(refNode);
+                        String stopName = PublicTransportHelper.getNameViaStoparea(refNode);
+                        if (stopName == null) stopName = "";
+                        boolean existsPlatform = platformMembers.containsKey(stopName);
+                        if (!existsPlatform) { stopName = ""; } // find of the nameless
+                        if (existsPlatform || platformMembers.containsKey(stopName)) {
+                            List<RelationMember> lMember = platformMembers.get(stopName);
+                            if (lMember.size() == 1) {
+                                recRel.addMember(lMember.get(0));
+                                lMember.remove(0);
+                            } else {
+                                // choose closest                                
+                                RelationMember candidat = getClosestPlatform(lMember, refNode);
+                                if (candidat != null) {
+                                    recRel.addMember(candidat);
+                                    lMember.remove(candidat);
+                                }
+                            }
+                            if (lMember.isEmpty()) platformMembers.remove(stopName);                            
+                        }
+                    }
+                }
+            }
+        }
+        
+        for (RelationMember stop : stopMembers.values()) {
+            recRel.addMember(stop);
+            String stopName = PublicTransportHelper.getNameViaStoparea(stop);
+            boolean existsPlatform = platformMembers.containsKey(stopName);
+            if (!existsPlatform) { stopName = ""; } // find of the nameless
+            if (existsPlatform || platformMembers.containsKey(stopName)) {            
+                List<RelationMember> lMember = platformMembers.get(stopName);
+                if (lMember.size() == 1) {
+                    recRel.addMember(lMember.get(0));
+                    lMember.remove(0);
+                } else {
+                    // choose closest                                
+                    RelationMember candidat = getClosestPlatform(lMember, stop.getNode());
+                    if (candidat != null) {
+                        recRel.addMember(candidat);
+                        lMember.remove(candidat);
+                    }
+                }
+                if (lMember.isEmpty()) platformMembers.remove(stopName);                            
+            }
+        }
+        
+        for (List<RelationMember> lPlatforms : platformMembers.values())
+            for (RelationMember platform : lPlatforms)
+                recRel.addMember(platform);
+        
+        for (RelationMember route : routeMembers)
+            recRel.addMember(route);
+        for (RelationMember wtf : wtfMembers)
+            recRel.addMember(wtf);        
+        Command command = new ChangeCommand(r, recRel);
+        Main.main.undoRedo.add(command);
+    }
+    
+    private static final double maxSqrDistBetweenStopAndPlatform = 2000; // ~ 26m    
+    private RelationMember getClosestPlatform(List<RelationMember> members, Node stop) {
+        if (stop == null || members.isEmpty()) return null;
+        double maxDist = maxSqrDistBetweenStopAndPlatform;
+        RelationMember result = null;
+        for (RelationMember member : members) {
+            if (member.getType() == OsmPrimitiveType.NODE) {
+                Node node = member.getNode();
+                double sqrDist = stop.getEastNorth().distanceSq(node.getEastNorth());
+                if (sqrDist < maxDist) {
+                    maxDist = sqrDist;
+                    result = member;
+                }
+            } else if (member.getType() == OsmPrimitiveType.WAY) {
+                Way way = member.getWay();
+                EastNorth closest = Geometry.closestPointToSegment(
+                                way.firstNode().getEastNorth(), 
+                                way.lastNode().getEastNorth(), 
+                                stop.getEastNorth()
+                );
+                double sqrDist = stop.getEastNorth().distanceSq(closest);
+                if (sqrDist < maxDist) {
+                    maxDist = sqrDist;
+                    result = member;
+                }
+            }
+        }
+        return result;
+    }
+ 
+    public void chosenRelationChanged( Relation oldRelation, Relation newRelation ) {
+	setEnabled(isSuitableRelation(newRelation));
+    }    
+    
+    private boolean isSuitableRelation (Relation newRelation) {
+        return !(newRelation == null || !"route".equals(newRelation.get("type")) || newRelation.getMembersCount() == 0);
+    }
+}
Index: relcontext/actions/SortAndFixAction.java
===================================================================
--- relcontext/actions/SortAndFixAction.java	(revision 30769)
+++ relcontext/actions/SortAndFixAction.java	(working copy)
@@ -21,6 +21,7 @@
 import relcontext.relationfix.BoundaryFixer;
 import relcontext.relationfix.MultipolygonFixer;
 import relcontext.relationfix.NothingFixer;
+import relcontext.relationfix.PublicTransportFixer;
 import relcontext.relationfix.RelationFixer;
 
 public class SortAndFixAction extends AbstractAction implements ChosenRelationListener {
@@ -43,6 +44,7 @@
         fixers.add(new BoundaryFixer()); // boundary, multipolygon, boundary=administrative
         fixers.add(new MultipolygonFixer()); // multipolygon
         fixers.add(new AssociatedStreetFixer()); //associatedStreet
+        fixers.add(new PublicTransportFixer()); //public_transport
 
         for(RelationFixer fix : fixers) {
             fix.setFixAction(this);
Index: relcontext/possible_roles.txt
===================================================================
--- relcontext/possible_roles.txt	(revision 30769)
+++ relcontext/possible_roles.txt	(working copy)
@@ -1,5 +1,6 @@
 boundary: admin_centre, label, subarea
-route: stop, platform, forward, backward, stop_exit_only, stop_entry_only, platform_exit_only, platform_entry_only
+route: forward, backward, stop, platform, stop_exit_only, stop_entry_only, platform_exit_only, platform_entry_only
+public_transport: stop, platform
 restriction: from, to, via, location_hint
 enforcement: device, from, to, force
 destination_sign: to, from, intersection, sign
Index: relcontext/relationfix/PublicTransportFixer.java
===================================================================
--- relcontext/relationfix/PublicTransportFixer.java	(revision 0)
+++ relcontext/relationfix/PublicTransportFixer.java	(working copy)
@@ -0,0 +1,79 @@
+package relcontext.relationfix;
+
+import org.openstreetmap.josm.command.ChangeCommand;
+import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
+import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.data.osm.RelationMember;
+import static org.openstreetmap.josm.tools.I18n.tr;
+import relcontext.actions.PublicTransportHelper;
+
+/**
+ * @see http://wiki.openstreetmap.org/wiki/Key:public_transport
+ */
+
+/**
+ * Helper function for determinate role in public_transport relation
+ * @author freeExec
+ */
+public class PublicTransportFixer extends RelationFixer {
+    	
+    public PublicTransportFixer() {
+        super("route", "public_transport");
+    }
+
+    /*protected PublicTransportFixer(String...types) {
+        super(types);
+    }*/
+
+    @Override
+    public boolean isRelationGood(Relation rel) {
+        for (RelationMember m : rel.getMembers()) {
+            if (m.getType().equals(OsmPrimitiveType.NODE) 
+                    && !(m.getRole().startsWith(PublicTransportHelper.STOP) || m.getRole().startsWith(PublicTransportHelper.PLATFORM))) {
+                setWarningMessage(tr("Node without ''stop'' or ''platform'' role found"));
+                return false;
+            }
+            if (m.getType().equals(OsmPrimitiveType.WAY)
+                    && PublicTransportHelper.isWayPlatform(m)
+                    && !m.getRole().startsWith(PublicTransportHelper.PLATFORM)) {
+                setWarningMessage(tr("Way platform without ''platform'' role found") + " r" + m.getUniqueId());
+                return false;
+            }
+        }
+        clearWarningMessage();
+        return true;
+    }
+
+    /*@Override
+    public boolean isFixerApplicable(Relation rel) {
+        return true;
+    }*/
+
+    @Override
+    public Command fixRelation(Relation rel) {
+        Relation r = rel;
+        Relation rr = fixStopPlatformRole(r);
+        boolean fixed = false;
+        if (rr != null) {
+            fixed = true;
+            r = rr;
+        }
+        return fixed ? new ChangeCommand(rel, r) : null;
+    }
+    
+    private Relation fixStopPlatformRole(Relation source) {
+        Relation r = new Relation(source);
+        boolean fixed = false;
+        for( int i = 0; i < r.getMembersCount(); i++ ) {
+            RelationMember m = r.getMember(i);
+            String role = PublicTransportHelper.getRoleByMember(m);
+
+            if (role != null && !m.getRole().startsWith(role)) {
+                r.setMember(i, new RelationMember(role, m.getMember()));
+                fixed = true;
+            }
+        }
+        return fixed ? r : null;            
+    }
+}
