Index: trunk/src/org/openstreetmap/josm/tools/RotationAngle.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/RotationAngle.java	(revision 18660)
+++ trunk/src/org/openstreetmap/josm/tools/RotationAngle.java	(revision 18661)
@@ -33,9 +33,25 @@
             }
             final Way w = ways.iterator().next();
+            Double angle = getRotationAngleForNodeOnWay(n, w);
+            return angle != null ? angle : 0;
+        }
+
+        /**
+         * Calculates the rotation angle of a node in a way based on the preceding way segment.
+         * If the node is the first node in the given way, the angle of the following way segment is used instead.
+         * @param n the node to get the rotation angle for
+         * @param w the way containing the node
+         * @return the rotation angle in radians or null if the way does not contain the node
+         * @since 18661
+         */
+        public static Double getRotationAngleForNodeOnWay(Node n, Way w) {
             final int idx = w.getNodes().indexOf(n);
+            if (idx == -1) {
+                return null;
+            }
             if (idx == 0) {
-                return -Geometry.getSegmentAngle(n.getEastNorth(), w.getNode(idx + 1).getEastNorth());
+                return -(Geometry.getSegmentAngle(n.getEastNorth(), w.getNode(idx + 1).getEastNorth()) - Math.PI/2);
             } else {
-                return -Geometry.getSegmentAngle(w.getNode(idx - 1).getEastNorth(), n.getEastNorth());
+                return -(Geometry.getSegmentAngle(w.getNode(idx - 1).getEastNorth(), n.getEastNorth()) - Math.PI/2);
             }
         }
@@ -107,5 +123,5 @@
      * Calculates the rotation angle depending on the primitive to be displayed.
      * @param p primitive
-     * @return rotation angle
+     * @return rotation angle in radians, clockwise starting from up/north
      * @since 13623 (signature)
      */
Index: trunk/test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/IconRotationTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/IconRotationTest.java	(revision 18661)
+++ trunk/test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/IconRotationTest.java	(revision 18661)
@@ -0,0 +1,139 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.mappaint.mapcss;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.IPrimitive;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmUtils;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.gui.mappaint.Environment;
+import org.openstreetmap.josm.gui.mappaint.MultiCascade;
+import org.openstreetmap.josm.gui.mappaint.styleelement.NodeElement;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
+import org.openstreetmap.josm.testutils.annotations.Projection;
+import org.openstreetmap.josm.tools.Utils;
+
+/**
+ * Unit tests of icon-rotation mapcss style property.
+ */
+@BasicPreferences
+@Projection
+class IconRotationTest {
+
+    private DataSet ds;
+
+    private final String CSS_N_WAY_ROTATION = "node { symbol-shape: triangle; icon-rotation: way; }";
+
+    /**
+     * Setup test
+     */
+    @BeforeEach
+    public void setUp() {
+        ds = new DataSet();
+    }
+
+    @Test
+    void testRotationNone() {
+        String css = "node { symbol-shape: triangle; }";
+        Node n = (Node) OsmUtils.createPrimitive("n");
+        ds.addPrimitive(n);
+
+        NodeElement ne = NodeElement.create(createStyleEnv(n, css));
+        assertEquals(0f, ne.mapImageAngle.getRotationAngle(n));
+    }
+
+    @Test
+    void testRotationRad() {
+        String css = "node { symbol-shape: triangle; icon-rotation: 3.14; }";
+        Node n = (Node) OsmUtils.createPrimitive("n");
+        ds.addPrimitive(n);
+
+        NodeElement ne = NodeElement.create(createStyleEnv(n, css));
+        assertEquals(3.14f, ne.mapImageAngle.getRotationAngle(n));
+    }
+
+    @Test
+    void testRotationDeg() {
+        String css = "node { symbol-shape: triangle; icon-rotation: 22.5°; }";
+        Node n = (Node) OsmUtils.createPrimitive("n");
+        ds.addPrimitive(n);
+
+        NodeElement ne = NodeElement.create(createStyleEnv(n, css));
+        assertEquals(Math.PI/8, ne.mapImageAngle.getRotationAngle(n), 0.01);
+    }
+
+    @Test
+    void testRotationWayNoParent() {
+        Node n = (Node) OsmUtils.createPrimitive("n");
+        ds.addPrimitive(n);
+
+        NodeElement ne = NodeElement.create(createStyleEnv(n, CSS_N_WAY_ROTATION));
+        assertEquals(0f, ne.mapImageAngle.getRotationAngle(n));
+    }
+
+    @Test
+    void testRotationWay() {
+        Node n0 = new Node(new LatLon(0.0, 0.2));
+        Node n1 = new Node(new LatLon(-0.1, 0.1));
+        Node n2 = new Node(LatLon.ZERO);
+        Node n3 = new Node(new LatLon(0.0, -0.1));
+        Way w = new Way();
+        w.addNode(n0);
+        w.addNode(n1);
+        w.addNode(n2);
+        w.addNode(n3);
+        ds.addPrimitiveRecursive(w);
+
+        assertEquals(Utils.toRadians(225),
+                     NodeElement.create(createStyleEnv(n0, CSS_N_WAY_ROTATION)).mapImageAngle.getRotationAngle(n0), 
+                     0.01);
+        assertEquals(Utils.toRadians(225),
+                     NodeElement.create(createStyleEnv(n1, CSS_N_WAY_ROTATION)).mapImageAngle.getRotationAngle(n1), 
+                     0.01);
+        assertEquals(Utils.toRadians(-45),
+                     NodeElement.create(createStyleEnv(n2, CSS_N_WAY_ROTATION)).mapImageAngle.getRotationAngle(n2), 
+                     0.01);
+        assertEquals(Utils.toRadians(-90),
+                     NodeElement.create(createStyleEnv(n3, CSS_N_WAY_ROTATION)).mapImageAngle.getRotationAngle(n3), 
+                     0.01);
+    }
+
+    /**
+     * icon-rotation: way; picks first way when a node has multiple parent ways
+     */
+    @Test
+    void testRotationWayMultiple() {
+        Node n = new Node(LatLon.ZERO);
+        Node n1 = new Node(new LatLon(0.1, 0.1));
+        Node n2 = new Node(new LatLon(-0.1, 0.1));
+        Way w1 = new Way();
+        Way w2 = new Way();
+        w1.addNode(n);
+        w1.addNode(n1);
+        w2.addNode(n);
+        w2.addNode(n2);
+        ds.addPrimitiveRecursive(w1);
+        ds.addPrimitiveRecursive(w2);
+
+        assertEquals(Utils.toRadians(45),
+                     NodeElement.create(createStyleEnv(n, CSS_N_WAY_ROTATION)).mapImageAngle.getRotationAngle(n), 
+                     0.01);
+    }
+
+    private Environment createStyleEnv(IPrimitive osm, String css) {
+        MapCSSStyleSource source = new MapCSSStyleSource(css);
+        source.loadStyleSource();
+        Environment env = new Environment(osm, new MultiCascade(), Environment.DEFAULT_LAYER, source);
+
+        for (MapCSSRule r : source.rules) {
+            r.execute(env);
+        }
+
+        return env;
+    }
+}
