Index: trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/ExpressionFactory.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/ExpressionFactory.java	(revision 18663)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/ExpressionFactory.java	(revision 18664)
@@ -217,4 +217,5 @@
         FACTORY_MAP.put("parent_tag", Factory.ofEnv(String.class, Functions::parent_tag));
         FACTORY_MAP.put("parent_tags", Factory.ofEnv(String.class, Functions::parent_tags));
+        FACTORY_MAP.put("parent_way_angle", Factory.ofEnv(Functions::parent_way_angle));
         FACTORY_MAP.put("plus", Factory.ofNumberVarArgs(0.0, DoubleUnaryOperator.identity(), Functions::plus));
         FACTORY_MAP.put("print", Factory.of(Object.class, Functions::print));
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Functions.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Functions.java	(revision 18663)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Functions.java	(revision 18664)
@@ -42,4 +42,5 @@
 import org.openstreetmap.josm.tools.Territories;
 import org.openstreetmap.josm.tools.Utils;
+import org.openstreetmap.josm.tools.RotationAngle.WayDirectionRotationAngle;
 
 /**
@@ -479,4 +480,22 @@
 
     /**
+     * Get the rotation angle of the preceding parent way segment at the node location.
+     * If there is no preceding parent way segment, the following way segment is used instead.
+     * Requires a parent way object matched via
+     * <a href="https://josm.openstreetmap.de/wiki/Help/Styles/MapCSSImplementation#LinkSelector">child selector</a>.
+     * 
+     * @param env the environment
+     * @return the rotation angle of the parent way segment at the node in radians,
+     * otherwise null if there is no matching parent way or the object is not a node
+     * @since 18664
+     */
+    public static Double parent_way_angle(final Environment env) {
+        if (env.osm instanceof Node && env.parent instanceof Way) {
+            return WayDirectionRotationAngle.getRotationAngleForNodeOnWay((Node) env.osm, (Way) env.parent);
+        }
+        return null;
+    }
+
+    /**
      * Gets the value of the key {@code key} from the object's child.
      * @param env the environment
Index: trunk/test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/FunctionsTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/FunctionsTest.java	(revision 18663)
+++ trunk/test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/FunctionsTest.java	(revision 18664)
@@ -3,4 +3,5 @@
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -8,31 +9,27 @@
 
 import java.util.Collections;
+import java.util.Objects;
 
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.junit.jupiter.api.Test;
 import org.openstreetmap.josm.TestUtils;
+import org.openstreetmap.josm.data.coor.LatLon;
+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.User;
+import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.data.preferences.NamedColorProperty;
 import org.openstreetmap.josm.gui.mappaint.Environment;
 import org.openstreetmap.josm.gui.util.GuiHelper;
 import org.openstreetmap.josm.spi.preferences.Config;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
+import org.openstreetmap.josm.testutils.annotations.Projection;
 
 /**
  * Unit tests of {@link Functions}.
  */
+@BasicPreferences
+@Projection
 class FunctionsTest {
-
-    /**
-     * Setup rule
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules();
-
     private static class EnvBuilder {
         private final OsmPrimitive osm;
@@ -108,4 +105,25 @@
 
     /**
+     * Test for {@link Functions#parent_way_angle(Environment)}
+     */
+    @Test
+    void testParentWayAngle() {
+        assertNull(Functions.parent_way_angle(new EnvBuilder(NODE).build()));
+        final Environment environment = new EnvBuilder(NODE).build();
+        ((Node) environment.osm).setCoor(LatLon.ZERO);
+        final Way parent = TestUtils.newWay("", new Node(new LatLon(-.1, 0)), (Node) environment.osm, new Node(new LatLon(.1, 0)));
+        environment.parent = parent;
+        Double actual = Functions.parent_way_angle(environment);
+        assertNotNull(actual);
+        assertEquals(Math.toRadians(0), actual, 1e-9);
+        // Reverse node order
+        Objects.requireNonNull(parent.firstNode()).setCoor(LatLon.NORTH_POLE);
+        Objects.requireNonNull(parent.lastNode()).setCoor(LatLon.SOUTH_POLE);
+        actual = Functions.parent_way_angle(environment);
+        assertNotNull(actual);
+        assertEquals(Math.toRadians(180), actual, 1e-9);
+    }
+
+    /**
      * Unit test of {@code Functions#to_xxx}
      */
@@ -163,4 +181,3 @@
         Config.getPref().put(colorKey, null);
     }
-
 }
