Index: doc/styles/rules.txt
===================================================================
--- doc/styles/rules.txt	(revision 4258)
+++ doc/styles/rules.txt	(working copy)
@@ -487,6 +487,13 @@
 ways multiple times, unless all member ways have been defined as parallel
 one way streets.
 
+=== apply_first ===
+The apply_first action is like +apply+, but it will apply the action only to the 
+first relation member as appearing in the input file. In combination with the 
+--add-pois-to-lines option this might be used with route relations to mark the 
+beginning of a route, presuming that the relation is complete and ordered so
+that the first member is the start of the route. 
+                                                
 === echo ===
 The echo action prints the element id plus a text to standard error. This can be
 used for quality checks and debugging purposes.
Index: src/uk/me/parabola/mkgmap/osmstyle/actions/ActionReader.java
===================================================================
--- src/uk/me/parabola/mkgmap/osmstyle/actions/ActionReader.java	(revision 4258)
+++ src/uk/me/parabola/mkgmap/osmstyle/actions/ActionReader.java	(working copy)
@@ -71,9 +71,11 @@
 			} else if ("addaccess".equals(cmd)) { 
 				actions.add(readAccessValue(false, changeableTags));
 			} else if ("apply".equals(cmd)) {
-				actions.add(readAllCmd(false));
+				actions.add(readAllCmd(null));
 			} else if ("apply_once".equals(cmd)) {
-				actions.add(readAllCmd(true));
+				actions.add(readAllCmd("once"));
+			} else if ("apply_first".equals(cmd)) {
+				actions.add(readAllCmd("first"));
 			} else if ("name".equals(cmd)) {
 				actions.add(readValueBuilder(new NameAction()));
 				changeableTags.add("mkgmap:label:1");
@@ -115,7 +117,7 @@
 		return new ActionList(actions, changeableTags);
 	}
 
-	private Action readAllCmd(boolean once) {
+	private Action readAllCmd(String selector) {
 		String role = null;
 		if (scanner.checkToken("role")) {
 			scanner.nextToken();
@@ -124,7 +126,7 @@
 				throw new SyntaxException(scanner, "Expecting '=' after role keyword");
 			role = scanner.nextWord();
 		}
-		SubAction subAction = new SubAction(role, once);
+		SubAction subAction = new SubAction(role, selector);
 
 		List<Action> actionList = readActions().getList();
 		for (Action a : actionList)
Index: src/uk/me/parabola/mkgmap/osmstyle/actions/SubAction.java
===================================================================
--- src/uk/me/parabola/mkgmap/osmstyle/actions/SubAction.java	(revision 4258)
+++ src/uk/me/parabola/mkgmap/osmstyle/actions/SubAction.java	(working copy)
@@ -36,11 +36,11 @@
 public class SubAction implements Action {
 	private final List<Action> actionList = new ArrayList<Action>();
 	private final String role;
-	private final boolean once;
+	private final String selector;
 
-	public SubAction(String role, boolean once) {
+	public SubAction(String role, String selector) {
 		this.role = role;
-		this.once = once;
+		this.selector = selector;
 	}
 
 	public boolean perform(Element el) {
@@ -58,6 +58,8 @@
 			else if (a instanceof AddAccessAction)
 				((AddAccessAction) a).setValueTags(rel);
 
+		boolean once = "once".equals(selector);
+		boolean first_only = "first".equals(selector);
 		HashSet<Element> elems = once ? new HashSet<Element>() : null;
 
 		for (Map.Entry<String,Element> r_el : elements) {
@@ -67,6 +69,8 @@
 				for (Action a : actionList)
 					a.perform(r_el.getValue());
 			}
+			if (first_only)
+				break;
 		}
 	}
 
@@ -76,7 +80,10 @@
 
 	public String toString() {
 		Formatter fmt = new Formatter();
-		fmt.format(once ? "apply_once" : "apply");
+		fmt.format("apply");
+		if (selector != null) {
+			fmt.format("_%s",selector);
+		}
 		if (role != null)
 			fmt.format(" role=%s ", role);
 		
