Index: src/org/openstreetmap/josm/gui/mappaint/mapcss/ExpressionFactory.java
===================================================================
--- src/org/openstreetmap/josm/gui/mappaint/mapcss/ExpressionFactory.java	(revision 15193)
+++ src/org/openstreetmap/josm/gui/mappaint/mapcss/ExpressionFactory.java	(working copy)
@@ -28,6 +28,8 @@
 import org.openstreetmap.josm.data.osm.IPrimitive;
 import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
+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.data.osm.search.SearchCompiler;
 import org.openstreetmap.josm.data.osm.search.SearchCompiler.Match;
@@ -570,6 +572,25 @@
         }
 
         /**
+         * Returns true if role is in relation. Returns false if not a relation or it does not have the role.
+         * @param env the environment
+         * @param roles The roles to count in the relation
+         * @return The number of relation members with the specified role
+         * @since xxx
+         */
+        public static int count_roles(final Environment env, String... roles) { // NO_UCD (unused code)
+            int rValue = 0;
+            if (env.osm instanceof Relation) {
+                List<String> roleList = Arrays.asList(roles);
+                Relation rel = (Relation) env.osm;
+                for (RelationMember member : rel.getMembers()) {
+                    if (roleList.contains(member.getRole())) rValue++;
+                }
+            }
+            return rValue;
+        }
+
+        /**
          * Returns the area of a closed way or multipolygon in square meters or {@code null}.
          * @param env the environment
          * @return the area of a closed way or multipolygon in square meters or {@code null}
Index: test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParserTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParserTest.java	(revision 15193)
+++ test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParserTest.java	(working copy)
@@ -13,10 +13,13 @@
 
 import org.junit.Rule;
 import org.junit.Test;
+import org.openstreetmap.josm.TestUtils;
 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.OsmUtils;
+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.mappaint.Environment;
 import org.openstreetmap.josm.gui.mappaint.MultiCascade;
@@ -404,6 +407,47 @@
     }
 
     @Test
+    public void testCountRoles() throws Exception {
+        DataSet ds = new DataSet();
+        Way way1 = TestUtils.newWay("highway=residential name=1",
+                new Node(new LatLon(0, 0)), new Node((new LatLon(0.001, 0.001))));
+        for (Node node : way1.getNodes()) {
+            ds.addPrimitive(node);
+        }
+        ds.addPrimitive(way1);
+
+        Relation rel1 = TestUtils.newRelation("type=destination_sign", new RelationMember("", way1));
+        ds.addPrimitive(rel1);
+
+        /* Check with empty role and one object */
+        Environment e = new Environment(rel1, new MultiCascade(), Environment.DEFAULT_LAYER, null);
+        assertEquals(1, ExpressionFactory.Functions.count_roles(e, ""));
+
+        /* Check with non-empty role and one object */
+        e = new Environment(rel1, new MultiCascade(), Environment.DEFAULT_LAYER, null);
+        assertEquals(0, ExpressionFactory.Functions.count_roles(e, "from"));
+
+        /* Check with empty role and two objects */
+        Way way2 = TestUtils.newWay("highway=residential name=2", way1.firstNode(), way1.lastNode());
+        ds.addPrimitive(way2);
+        rel1.addMember(new RelationMember("", way2));
+        e = new Environment(rel1, new MultiCascade(), Environment.DEFAULT_LAYER, null);
+        assertEquals(2, ExpressionFactory.Functions.count_roles(e, ""));
+
+        /* Check with non-empty role and two objects */
+        rel1.setMember(0, new RelationMember("from", way1));
+        e = new Environment(rel1, new MultiCascade(), Environment.DEFAULT_LAYER, null);
+        assertEquals(1, ExpressionFactory.Functions.count_roles(e, "from"));
+
+        /* Check with multiple roles */
+        assertEquals(1, ExpressionFactory.Functions.count_roles(e, "from", "to"));
+
+        /* Check with non-relation */
+        e = new Environment(way1, new MultiCascade(), Environment.DEFAULT_LAYER, null);
+        assertEquals(0, ExpressionFactory.Functions.count_roles(e, "from", "to"));
+    }
+
+    @Test
     public void testSiblingSelectorInterpolation() throws Exception {
         ChildOrParentSelector s1 = (Selector.ChildOrParentSelector) getParser(
                 "*[tag(\"addr:housenumber\") > child_tag(\"addr:housenumber\")][regexp_test(\"even|odd\", parent_tag(\"addr:interpolation\"))]" +
