Index: src/org/openstreetmap/josm/actions/relation/ExportRelationToGpxAction.java
===================================================================
--- src/org/openstreetmap/josm/actions/relation/ExportRelationToGpxAction.java	(Revision 18721)
+++ src/org/openstreetmap/josm/actions/relation/ExportRelationToGpxAction.java	(Arbeitskopie)
@@ -1,13 +1,15 @@
 // License: GPL. For details, see LICENSE file.
 package org.openstreetmap.josm.actions.relation;
 
-import static org.openstreetmap.josm.actions.relation.ExportRelationToGpxAction.Mode.FROM_FIRST_MEMBER;
+import static org.openstreetmap.josm.actions.relation.ExportRelationToGpxAction.Mode.LAST_MEMBER_FIRST;
 import static org.openstreetmap.josm.actions.relation.ExportRelationToGpxAction.Mode.TO_FILE;
 import static org.openstreetmap.josm.actions.relation.ExportRelationToGpxAction.Mode.TO_LAYER;
 import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
 import static org.openstreetmap.josm.tools.I18n.tr;
 
+import java.awt.GridBagLayout;
 import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -14,6 +16,7 @@
 import java.util.Collections;
 import java.util.EnumSet;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
@@ -21,7 +24,15 @@
 import java.util.Set;
 import java.util.Stack;
 import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
 
+import javax.swing.BorderFactory;
+import javax.swing.ButtonGroup;
+import javax.swing.JCheckBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+
 import org.openstreetmap.josm.actions.GpxExportAction;
 import org.openstreetmap.josm.actions.IPrimitiveAction;
 import org.openstreetmap.josm.data.gpx.GpxData;
@@ -31,6 +42,8 @@
 import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.RelationMember;
+import org.openstreetmap.josm.data.validation.tests.RelationChecker;
+import org.openstreetmap.josm.gui.ExtendedDialog;
 import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.dialogs.relation.sort.WayConnectionType;
 import org.openstreetmap.josm.gui.dialogs.relation.sort.WayConnectionTypeCalculator;
@@ -37,6 +50,8 @@
 import org.openstreetmap.josm.gui.layer.GpxLayer;
 import org.openstreetmap.josm.gui.layer.Layer;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.spi.preferences.Config;
+import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.SubclassFilteredCollection;
 import org.openstreetmap.josm.tools.Utils;
 
@@ -49,12 +64,12 @@
 public class ExportRelationToGpxAction extends GpxExportAction
     implements IPrimitiveAction {
 
+    private static final String SETTING_KEY = "gpx.export-from-relation";
+
     /** Enumeration of export variants */
     public enum Mode {
-        /** concatenate members from first to last element */
-        FROM_FIRST_MEMBER,
-        /** concatenate members from last to first element */
-        FROM_LAST_MEMBER,
+        /** concatenate members from last to first element, instead of first to last */
+        LAST_MEMBER_FIRST,
         /** export to GPX layer and add to LayerManager */
         TO_LAYER,
         /** export to GPX file and open FileChooser */
@@ -69,9 +84,21 @@
 
     /** Construct a new ExportRelationToGpxAction with default mode */
     public ExportRelationToGpxAction() {
-        this(EnumSet.of(FROM_FIRST_MEMBER, TO_FILE));
+        this(EnumSet.of(TO_FILE));
     }
 
+    /** A flat representation of the input data */
+    private List<RelationMember> flat;
+
+    /** The relations sourced to build {@code ExportRelationToGpxAction.flat} */
+    private List<Relation> relsFound;
+
+    /** Discard relation members with unknown roles if the set is not empty. */
+    private Set<String> okRoles;
+
+    /** Ignore relation members' roles, treat any role as the empty role during export. */
+    private List<Boolean> ignRoles;
+
     /**
      * Constructs a new {@code ExportRelationToGpxAction}
      *
@@ -86,13 +113,13 @@
 
     private static String name(Set<Mode> mode) {
         if (mode.contains(TO_FILE)) {
-            if (mode.contains(FROM_FIRST_MEMBER)) {
+            if (!mode.contains(LAST_MEMBER_FIRST)) {
                 return tr("Export GPX file starting from first member");
             } else {
                 return tr("Export GPX file starting from last member");
             }
         } else {
-            if (mode.contains(FROM_FIRST_MEMBER)) {
+            if (!mode.contains(LAST_MEMBER_FIRST)) {
                 return tr("Convert to GPX layer starting from first member");
             } else {
                 return tr("Convert to GPX layer starting from last member");
@@ -101,7 +128,7 @@
     }
 
     private static String tooltip(Set<Mode> mode) {
-        if (mode.contains(FROM_FIRST_MEMBER)) {
+        if (!mode.contains(LAST_MEMBER_FIRST)) {
             return tr("Flatten this relation to a single gpx track recursively, " +
                     "starting with the first member, successively continuing to the last.");
         } else {
@@ -110,9 +137,11 @@
         }
     }
 
-    @Override
-    protected Layer getLayer() {
-        List<RelationMember> flat = new ArrayList<>();
+    protected void prepareData() {
+        ignRoles = new ArrayList<>();
+        okRoles = new HashSet<>();
+        flat = new ArrayList<>();
+        relsFound = new ArrayList<>();
 
         List<RelationMember> init = new ArrayList<>();
         relations.forEach(t -> init.add(new RelationMember("", t)));
@@ -119,8 +148,6 @@
 
         Stack<Iterator<RelationMember>> stack = new Stack<>();
         stack.push(modeAwareIterator(init));
-
-        List<Relation> relsFound = new ArrayList<>();
         do {
             Iterator<RelationMember> i = stack.peek();
             if (!i.hasNext())
@@ -128,7 +155,7 @@
             while (i.hasNext()) {
                 RelationMember m = i.next();
                 if (m.isRelation() && !m.getRelation().isIncomplete()) {
-                    final List<RelationMember> members = m.getRelation().getMembers();
+                    List<RelationMember> members = m.getRelation().getMembers();
                     stack.push(modeAwareIterator(members));
                     relsFound.add(m.getRelation());
                     break;
@@ -138,11 +165,24 @@
                 }
             }
         } while (!stack.isEmpty());
+    }
 
+    @Override
+    protected Layer getLayer() {
         GpxData gpxData = new GpxData();
         final String layerName;
         long time = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) - 24*3600;
 
+        if (!okRoles.isEmpty()) {
+            flat.removeIf(rm -> !okRoles.contains(rm.getRole()));
+        } else if (!ignRoles.isEmpty()) {
+            for (int i = 0; i < flat.size(); i++) {
+                RelationMember rm = flat.get(i);
+                if (!rm.getRole().isEmpty())
+                    flat.set(i, new RelationMember("", rm.getMember()));
+            }
+        }
+
         if (!flat.isEmpty()) {
             Map<String, Object> trkAttr = new HashMap<>();
             Collection<Collection<WayPoint>> trk = new ArrayList<>();
@@ -178,6 +218,9 @@
                         trkseg.add(OsmDataLayer.nodeToWayPoint(n, TimeUnit.SECONDS.toMillis(time)));
                         time += 1;
                     }
+                } else if (i+1 < flat.size()) {
+                    WayConnectionType nxt = wct.get(i+1);
+                    nxt.linkPrev &= wayConnectionType.linkPrev;
                 }
             }
             gpxData.addTrack(new GpxTrack(trk, trkAttr));
@@ -194,17 +237,169 @@
     }
 
     private <T> Iterator<T> modeAwareIterator(List<T> list) {
-        return mode.contains(FROM_FIRST_MEMBER)
-                ? list.iterator()
-                : new LinkedList<>(list).descendingIterator();
+        return mode.contains(LAST_MEMBER_FIRST)
+                ? new LinkedList<>(list).descendingIterator()
+                : list.iterator();
     }
 
+    private static Set<String> getRolesInPresets(List<Relation> lr) {
+        return lr.stream().flatMap(n -> RelationChecker.getPresetDefinedRolesFor(n).stream()).collect(Collectors.toSet());
+    }
+
     /**
+     * Shows a dialog asking the user if relation members of
+     * <li>any role</li>
+     * <li>a user selection of roles</li>
+     * <li>roles known/defined in relation presets<li>
+     * should be considered when exporting.
      *
+     * The second case allows exclusion of preset-defined roles,
+     * although this may produce unexpected results. It is an
+     * expert feature.
+     *
+     * @param lrm a flat list of RelationMembers about to be exported
+     * @param lr a list of relations lrm was sourced from
+     * @param oR okRoles, possibly modified by the method
+     *
+     * @return true if the dialog was confirmed, false if cancelled
+     */
+    private static boolean askRelationMemberRolesToExport(List<RelationMember> lrm, List<Relation> lr, Set<String> oR, List<Boolean> iR) {
+        // "do not ask again" handled here:
+        switch (Config.getPref().get(SETTING_KEY, "ask")) {
+            case "all":
+                oR.clear();
+                return true;
+            case "list":
+                oR.addAll(Config.getPref().getList(SETTING_KEY + ".list"));
+                return true;
+            case "presets":
+                oR.addAll(getRolesInPresets(lr));
+                return true;
+            case "treatempty":
+                iR.add(true);
+                return true;
+        }
+
+        String lSel = Config.getPref().get(SETTING_KEY + ".last", "all");
+        List<String> userRoles = Config.getPref().getList(SETTING_KEY + ".list");
+        Set<String> rolesInData = lrm.stream().map(rm -> rm.getRole()).collect(Collectors.toSet());
+        Set<String> rolesInPresets = getRolesInPresets(lr);
+
+        // skip asking if all RelationMembers are assigned the same role
+        if (rolesInData.size() == 1) {
+            oR.clear();
+            return true;
+        }
+
+        JPanel p = new JPanel(new GridBagLayout());
+        ButtonGroup r = new ButtonGroup();
+
+        p.add(new JLabel("<html><body style=\"width:404px;\">"
+          + tr("This converts the relation to GPX format.") + "<br><br>"
+          + tr("Relation members may be filtered out based on their role. Which roles should be considered by export?")
+          + "</body></html>"), GBC.eol());
+        JRadioButton rAll = new JRadioButton(tr("Any role"), "all".equals(lSel));
+        r.add(rAll);
+        p.add(rAll, GBC.eol());
+
+        JRadioButton rList = new JRadioButton(tr("Only selected roles:"), "list".equals(lSel));
+        rList.setToolTipText("excluding forward / backward will impact WayConnectionTypeCalculator and thus may produce unexpected results");
+        r.add(rList);
+        p.add(rList, GBC.eol());
+
+        JPanel q = new JPanel();
+
+        List<JCheckBox> checkList = new ArrayList<>();
+        ActionListener ensureAtLeastOneSelected = new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                if (checkList.stream().noneMatch(cb -> cb.isSelected()))
+                    checkList.get(0).setSelected(true);
+            }
+        };
+        for (String role : rolesInData) {
+            JCheckBox cTmp = new JCheckBox(role, (userRoles.isEmpty() ? rolesInPresets : userRoles).contains(role));
+            cTmp.addActionListener(ensureAtLeastOneSelected);
+            checkList.add(cTmp);
+            q.add(cTmp);
+        }
+
+        q.setBorder(BorderFactory.createEmptyBorder(0, 20, 5, 0));
+        p.add(q, GBC.eol());
+
+        JRadioButton rPre = new JRadioButton(tr("Roles as defined in presets"), "presets".equals(lSel));
+        r.add(rPre);
+        p.add(rPre, GBC.eol());
+
+        JRadioButton rIgn = new JRadioButton(tr("Ignore roles / treat all as empty"), "treatempty".equals(lSel));
+        r.add(rIgn);
+        p.add(rIgn, GBC.eol());
+
+        rList.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                for (JCheckBox ch : checkList) {
+                    ch.setEnabled(true);
+                }
+            }
+        });
+        ActionListener disabler = new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                for (JCheckBox ch : checkList) {
+                    ch.setEnabled(false);
+                }
+            }
+        };
+        rAll.addActionListener(disabler);
+        rPre.addActionListener(disabler);
+        rIgn.addActionListener(disabler);
+
+        if (!"list".equals(lSel)) {
+            disabler.actionPerformed(null);
+        }
+
+        ExtendedDialog ed = new ExtendedDialog(MainApplication.getMainFrame(), tr("Options"),
+                tr("Convert"), tr("Convert and remember selection"), tr("Cancel"))
+                .setButtonIcons("exportgpx", "exportgpx", "cancel").setContent(p);
+        int ret = ed.showDialog().getValue();
+
+        if (ret == 1 || ret == 2) {
+            userRoles = checkList.stream().filter(cb -> cb.isSelected()).map(cb -> cb.getText()).collect(Collectors.toList());
+            String sel = rAll.isSelected() ? "all" : (rIgn.isSelected() ? "treatempty" : (rPre.isSelected() ? "presets" : "list"));
+            Config.getPref().put(SETTING_KEY + ".last", sel);
+            Config.getPref().put(SETTING_KEY, (ret == 2) ? sel : "ask");
+            switch (sel) {
+            case "all":
+                oR.clear();
+                break;
+            case "list":
+                oR.addAll(userRoles);
+                Config.getPref().putList(SETTING_KEY + ".list", userRoles);
+                break;
+            case "presets":
+                oR.addAll(rolesInPresets);
+                break;
+            case "treatempty":
+                iR.add(true);
+                break;
+            }
+        } else {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     *
      * @param e the ActionEvent
      */
     @Override
     public void actionPerformed(ActionEvent e) {
+        prepareData();
+        if (!askRelationMemberRolesToExport(flat, relsFound, okRoles, ignRoles))
+            return;
         if (mode.contains(TO_LAYER))
             MainApplication.getLayerManager().addLayer(getLayer());
         if (mode.contains(TO_FILE))
Index: src/org/openstreetmap/josm/data/osm/Relation.java
===================================================================
--- src/org/openstreetmap/josm/data/osm/Relation.java	(Revision 18721)
+++ src/org/openstreetmap/josm/data/osm/Relation.java	(Arbeitskopie)
@@ -556,6 +556,12 @@
         return Stream.of(members).map(RelationMember::getRole).filter(role -> !role.isEmpty()).collect(Collectors.toSet());
     }
 
+    public List<? extends OsmPrimitive> findRelationMembersWithUnknownRole(Set<String> knownRoles) {
+        return Stream.of(members)
+                .filter(m -> knownRoles.isEmpty() || knownRoles.stream().noneMatch(kr -> kr.equals(m.getRole())))
+                .map(RelationMember::getMember).collect(Collectors.toList());
+    }
+
     @Override
     public List<? extends OsmPrimitive> findRelationMembers(String role) {
         return IRelation.super.findRelationMembers(role).stream()
Index: src/org/openstreetmap/josm/data/validation/tests/RelationChecker.java
===================================================================
--- src/org/openstreetmap/josm/data/validation/tests/RelationChecker.java	(Revision 18721)
+++ src/org/openstreetmap/josm/data/validation/tests/RelationChecker.java	(Arbeitskopie)
@@ -15,6 +15,7 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.stream.Collectors;
 
 import org.openstreetmap.josm.command.ChangeMembersCommand;
@@ -480,4 +481,30 @@
             return Collections.unmodifiableList(test.loops.iterator().next());
     }
 
+    /**
+     * Check RelationMember roles to be known in presets, report members with unknown roles.
+     * @param n the relation to check
+     * @return An empty list if all members have known roles, or a list of members with preset-wise unknown roles.
+     */
+    public static List<? extends OsmPrimitive> checkMembersForRoleUnknownInPresets(Relation n) {
+        return n.findRelationMembersWithUnknownRole(getPresetDefinedRolesFor(n));
+    }
+
+    /**
+     * Reply a set of preset-defined "known" roles for the type of relation passed.
+     *
+     * The data of the relation instance' members is irrelevant for this call.
+     * Preset data is looked up based on the relation tags, in particular its
+     * type tag.  Thus, the returned set may very well contain roles not as-
+     * signed to any of the passed instance' members.
+     *
+     * @param n the relation whose type the preset-defined roles should be determined for
+     * @return a set of known roles for the relation's type.
+     */
+    public static Set<String> getPresetDefinedRolesFor(Relation n) {
+        initializePresets();
+        Map<Role, String> allroles = buildAllRoles(n);
+        return allroles.keySet().stream().map(r -> r.key).collect(Collectors.toSet());
+    }
+
 }
Index: src/org/openstreetmap/josm/gui/dialogs/RelationListDialog.java
===================================================================
--- src/org/openstreetmap/josm/gui/dialogs/RelationListDialog.java	(Revision 18721)
+++ src/org/openstreetmap/josm/gui/dialogs/RelationListDialog.java	(Arbeitskopie)
@@ -129,13 +129,13 @@
 
     /** export relation to GPX track action */
     private final ExportRelationToGpxAction exportRelationFromFirstAction =
-            new ExportRelationToGpxAction(EnumSet.of(Mode.FROM_FIRST_MEMBER, Mode.TO_FILE));
+            new ExportRelationToGpxAction(EnumSet.of(Mode.TO_FILE));
     private final ExportRelationToGpxAction exportRelationFromLastAction =
-            new ExportRelationToGpxAction(EnumSet.of(Mode.FROM_LAST_MEMBER, Mode.TO_FILE));
+            new ExportRelationToGpxAction(EnumSet.of(Mode.LAST_MEMBER_FIRST, Mode.TO_FILE));
     private final ExportRelationToGpxAction exportRelationFromFirstToLayerAction =
-            new ExportRelationToGpxAction(EnumSet.of(Mode.FROM_FIRST_MEMBER, Mode.TO_LAYER));
+            new ExportRelationToGpxAction(EnumSet.of(Mode.TO_LAYER));
     private final ExportRelationToGpxAction exportRelationFromLastToLayerAction =
-            new ExportRelationToGpxAction(EnumSet.of(Mode.FROM_LAST_MEMBER, Mode.TO_LAYER));
+            new ExportRelationToGpxAction(EnumSet.of(Mode.LAST_MEMBER_FIRST, Mode.TO_LAYER));
 
     private final transient HighlightHelper highlightHelper = new HighlightHelper();
     private final boolean highlightEnabled = Config.getPref().getBoolean("draw.target-highlight", true);
