diff --git a/CHANGELOG.md b/CHANGELOG.md
index fddc909d92c..3f36b733ab0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,7 @@
 # Unreleased
   - Changes from 5.27.1
     - Features
+      - ADDED: Add generic support for obstacles [#7130](https://github.com/Project-OSRM/osrm-backend/pull/7130)
       - ADDED: Route pedestrians over highway=platform [#6993](https://github.com/Project-OSRM/osrm-backend/pull/6993)
       - REMOVED: Remove all core-CH left-overs [#6920](https://github.com/Project-OSRM/osrm-backend/pull/6920)
       - ADDED: Add support for a keepalive_timeout flag. [#6674](https://github.com/Project-OSRM/osrm-backend/pull/6674)
diff --git a/docs/profiles.md b/docs/profiles.md
index c66eb81c299..b6c29056899 100644
--- a/docs/profiles.md
+++ b/docs/profiles.md
@@ -1,7 +1,7 @@
 # OSRM profiles
 OSRM supports "profiles". Profiles representing routing behavior for different transport modes like car, bike and foot. You can also create profiles for variations like a fastest/shortest car profile or fastest/safest/greenest bicycles profile.
 
- A profile describes whether or not it's possible to route along a particular type of way, whether we can pass a particular node, and  how quickly we'll be traveling when we do. This feeds into the way the routing graph is created and thus influences the output routes.
+A profile describes whether or not it's possible to route along a particular type of way, whether we can pass a particular node, and how quickly we'll be traveling when we do. This feeds into the way the routing graph is created and thus influences the output routes.
 
 ## Available profiles
 Out-of-the-box OSRM comes with profiles for car, bicycle and foot. You can easily modify these or create new ones if you like.
@@ -116,7 +116,7 @@ suffix_list                          | Set              | List of name suffixes
 relation_types                       | Sequence         | Determines which relations should be cached for processing in this profile. It contains relations types
 
 ### process_node(profile, node, result, relations)
-Process an OSM node to determine whether this node is a barrier or can be passed and whether passing it incurs a delay.
+Process an OSM node to determine whether this node is an obstacle, if it can be passed at all and whether passing it incurs a delay.
 
 Argument | Description
 ---------|-------------------------------------------------------
@@ -126,12 +126,177 @@ result   | The output that you will modify.
 relations| Storage of relations to access relations, where `node` is a member.
 
 The following attributes can be set on `result`:
+(Note: for new code use the `obstacle_map`.
 
 Attribute       | Type    | Notes
 ----------------|---------|---------------------------------------------------------
 barrier         | Boolean | Is it an impassable barrier?
 traffic_lights  | Boolean | Is it a traffic light (incurs delay in `process_turn`)?
 
+### Obstacle
+A user type that represents an obstacle on the road or a place where you can turn
+around.
+
+This may be a completely impassable obstacle like a barrier, a temporary obstacle like a
+traffic light or a stop sign, or an obstacle that just slows you down like a
+traffic_calming. The obstacle may be present in both directions or in one direction
+only.
+
+This also represents a good turning point like a mini_roundabout, turning_loop, or
+turning_circle.
+
+An object of this type is immutable once constructed.
+
+```lua
+local obs = Obstacle.new(
+  obstacle_type.traffic_signals,
+  obstacle_direction.forward,
+  2.5,
+  0
+)
+assert(obs.duration == 2.5)
+```
+
+Member    | Mode      | Type               | Notes
+----------|-----------|--------------------|----------------------------------
+type      | read-only | obstacle_type      | eg. `obstacle_type.barrier`
+direction | read-only | obstacle_direction | eg. `obstacle_direction.forward`
+duration  | read-only | float              | The expected delay in seconds
+weight    | read-only | float              | The weight
+
+#### obstacle_type
+An enum with the following keys:
+
+Keys            |
+----------------|
+none            |
+barrier         |
+traffic_signals |
+stop            |
+give_way        |
+crossing        |
+traffic_calming |
+mini_roundabout |
+turning_loop    |
+turning_circle  |
+
+#### obstacle_direction
+An enum with the following keys:
+
+Keys     |
+---------|
+none     |
+forward  |
+backward |
+both     |
+
+### obstacle_map
+A global user type. It stores obstacles.
+
+The canonical workflow is: to store obstacles in `process_node()` and retrieve them in
+`process_turn()`.
+
+Note: In the course of processing, between the `process_node()` stage and the
+`process_turn()` stage, the extractor switches from using OSM nodes to using
+internal nodes. Both types have different ids. You can only store OSM nodes and only
+retrieve internal nodes. This implies that, in `process_node()`, you cannot retrieve an
+obstacle you have just stored.
+
+#### obstacle_map:add(node, obstacle)
+Call this function inside `process_node()` to register an obstacle on a node. You can
+register as many different obstacles as you wish on any given node. It is your
+responsibility to register the same obstacle only once.
+
+In a following step -- likely in `process_turn()` -- you can retrieve all obstacles
+registered at any given node. This function works with OSM nodes.
+
+Argument | Type     | Notes
+---------|----------|--------------------------------------------
+node     | OSMNode  | The same node as passed to `process_node`.
+obstacle | Obstacle | The obstacle
+
+Usage example:
+
+```lua
+function process_node(profile, node, result, relations)
+  ...
+  obstacle_map:add(node,
+    Obstacle.new(
+      obstacle_type.traffic_signal,
+      obstacle_direction.forward,
+      2, 0))
+end
+```
+
+#### obstacle_map:any(from, to, type)
+Return true if there are any obstacles at node `to` when coming from node
+`from` and having the type `type`.
+
+You will likely call this function inside `process_turn()`.
+Note that this works only with internal nodes, not with OSM nodes.
+
+```lua
+bool obstacle_map:any(to)
+bool obstacle_map:any(from, to)
+bool obstacle_map:any(from, to, type)
+```
+
+Argument | Type          | Notes
+---------|---------------|-------------------------------------------------------------------------------------
+from     | Node          | The leading node. Optional.
+to       | Node          | The node with the obstacle.
+type     | obstacle_type | The obstacle type. Defaults to all types. May be a bitwise-or combination of types.
+returns  | bool          | True if there are any obstacles satisfiying the given criteria.
+
+Usage examples:
+
+```lua
+function process_turn(profile, turn)
+  if obstacle_map:any(turn.via) then
+    ...
+  end
+  if obstacle_map:any(turn.from, turn.via, obstacle_type.traffic_signal) then
+    turn.duration = turn.duration + 2
+  end
+end
+```
+
+#### obstacle_map:get(from, to, type)
+This function retrieves all registered obstacles at node `to` when coming from the node
+`from` and having the type `type`.
+
+You will likely call this function inside `process_turn()`.
+Note that this works only with internal nodes, not with OSM nodes.
+
+```lua
+obstacle_map:get(to)
+obstacle_map:get(from, to)
+obstacle_map:get(from, to, type)
+```
+
+Argument | Type          | Notes
+---------|---------------|-------------------------------------------------------------------------------------
+from     | Node          | The leading node. Optional.
+to       | Node          | The node with the obstacle.
+type     | obstacle_type | The obstacle type. Defaults to all types. May be a bitwise-or combination of types.
+returns  | table         | A table of `Obstacle`s.
+
+Usage examples:
+
+```lua
+function process_turn(profile, turn)
+  for _, obs in pairs(obstacle_map:get(turn.via)) do
+    if obs.type == obstacle_type.barrier then
+      turn.duration = turn.duration + obs.duration
+    end
+  end
+  for _, obs in pairs(obstacle_map:get(
+      turn.from, turn.via, obstacle_type.traffic_signal)) do
+    turn.duration = turn.duration + obs.duration
+  end
+end
+```
+
 ### process_way(profile, way, result, relations)
 Given an OpenStreetMap way, the `process_way` function will either return nothing (meaning we are not going to route over this way at all), or it will set up a result hash.
 
@@ -233,14 +398,24 @@ target_highway_turn_classification | Read          | Integer                   |
 target_access_turn_classification  | Read          | Integer                   | Classification based on access tag defined by user during setup. (default when not set: 0, allowed classification values are: 0-15))
 target_speed                       | Read          | Integer                   | Speed on this target road in km/h
 target_priority_class              | Read          | Enum                      | The type of road priority class of the target. Defined in `include/extractor/road_classification.hpp`
+from                               | Read          | NodeID                    | The leading node
+via                                | Read          | NodeID                    | The intersection node
+to                                 | Read          | NodeID                    | The trailing node
+source_road                        | Read          | ExtractionTurnLeg         | The incoming road
+target_road                        | Read          | ExtractionTurnLeg         | The outgoing road
 roads_on_the_right                 | Read          | Vector<ExtractionTurnLeg> | Vector with information about other roads on the right of the turn that are also connected at the intersection
 roads_on_the_left                  | Read          | Vector<ExtractionTurnLeg> | Vector with information about other roads on the left of the turn that are also connected at the intersection. If turn is a u turn, this is empty.
 weight                             | Read/write    | Float                     | Penalty to be applied for this turn (routing weight)
 duration                           | Read/write    | Float                     | Penalty to be applied for this turn (duration in deciseconds)
 
-#### `roads_on_the_right` and `roads_on_the_left`
 
-The information of `roads_on_the_right` and `roads_on_the_left` that can be read are as follows:
+#### `from`, `via`, and `to`
+Use these node IDs to retrieve obstacles. See: `obstacle_map:get`.
+
+#### `source_road`, `target_road`, `roads_on_the_right`, and `roads_on_the_left`
+
+The information of `source_road`, `target_road`, `roads_on_the_right`, and
+`roads_on_the_left` that can be read are as follows:
 
 Attribute                   | Read/write?   | Type      | Notes
 ---------------------       | ------------- | --------- | ------------------------------------------------------
@@ -252,6 +427,7 @@ number_of_lanes             | Read          | Integer   | How many lanes does th
 highway_turn_classification | Read          | Integer   | Classification based on highway tag defined by user during setup. (default when not set: 0, allowed classification values are: 0-15)
 access_turn_classification  | Read          | Integer   | Classification based on access tag defined by user during setup. (default when not set: 0, allowed classification values are: 0-15)
 speed                       | Read          | Integer   | Speed on this road in km/h
+distance                    | Read          | Double    | The length of the road edge
 priority_class              | Read          | Enum      | The type of road priority class of the leg. Defined in `include/extractor/road_classification.hpp`
 is_incoming                 | Read          | Boolean   | Is the road an incoming road of the intersection
 is_outgoing                 | Read          | Boolean   | Is the road an outgoing road of the intersection
diff --git a/docs/src/content.js b/docs/src/content.js
index 6e7844e1a29..c3eaf359d36 100644
--- a/docs/src/content.js
+++ b/docs/src/content.js
@@ -11,6 +11,4 @@ var fs = require('fs');
  */
 module.exports =
       '# HTTP API\n' +
-      fs.readFileSync('./content/http.md', 'utf8') + '\n'+
-      '# libosrm C++ API\n' +
-      fs.readFileSync('./content/libosrm.md', 'utf8') + '\n';
+      fs.readFileSync('./content/http.md', 'utf8') + '\n';
diff --git a/features/car/obstacle_penalties.feature b/features/car/obstacle_penalties.feature
new file mode 100644
index 00000000000..1b733933004
--- /dev/null
+++ b/features/car/obstacle_penalties.feature
@@ -0,0 +1,227 @@
+@routing @car @obstacle
+Feature: Car - Handle obstacle penalties
+
+    Background:
+        Given the profile "car"
+
+    Scenario: Car - Give-Way signs
+        Given the node map
+            """
+            a-b-c   d-e-f   g-h-i   j-k-l   m-n-o
+
+            """
+
+        And the ways
+            | nodes   | highway |
+            | abc     | primary |
+            | def     | primary |
+            | ghi     | primary |
+            | jkl     | primary |
+            | mno     | primary |
+
+        And the nodes
+            | node | highway  | direction |
+            | e    | give_way |           |
+            | h    | give_way | forward   |
+            | k    | give_way | backward  |
+            | n    | give_way | both      |
+
+        When I route I should get
+            | from | to | time | #             |
+            | a    | c  | 11s  |               |
+            | c    | a  | 11s  |               |
+            | d    | f  | 12s  | give-way sign |
+            | f    | d  | 12s  | give-way sign |
+            | g    | i  | 12s  | give-way sign |
+            | i    | g  | 11s  |               |
+            | j    | l  | 11s  |               |
+            | l    | j  | 12s  | give-way sign |
+            | m    | o  | 12s  | give-way sign |
+            | o    | m  | 12s  | give-way sign |
+
+
+    Scenario: Car - Stop signs
+        Given the node map
+            """
+            a-b-c   d-e-f   g-h-i   j-k-l   m-n-o
+
+            """
+
+        And the ways
+            | nodes   | highway |
+            | abc     | primary |
+            | def     | primary |
+            | ghi     | primary |
+            | jkl     | primary |
+            | mno     | primary |
+
+        And the nodes
+            | node | highway | direction |
+            | e    | stop    |           |
+            | h    | stop    | forward   |
+            | k    | stop    | backward  |
+            | n    | stop    | both      |
+
+        When I route I should get
+            | from | to | time | #         |
+            | a    | c  | 11s  |           |
+            | c    | a  | 11s  |           |
+            | d    | f  | 13s  | stop sign |
+            | f    | d  | 13s  | stop sign |
+            | g    | i  | 13s  | stop sign |
+            | i    | g  | 11s  |           |
+            | j    | l  | 11s  |           |
+            | l    | j  | 13s  | stop sign |
+            | m    | o  | 13s  | stop sign |
+            | o    | m  | 13s  | stop sign |
+
+
+    Scenario: Car - Stop sign on intersection node
+        Given the node map
+            """
+              a      f      k
+            b-c-d  h-g-i  l-m-n
+              e      j      o
+
+            """
+
+        And the ways
+            | nodes | highway   |
+            | bcd   | primary   |
+            | ace   | secondary |
+            | hgi   | primary   |
+            | fgj   | secondary |
+            | lmn   | primary   |
+            | kmo   | secondary |
+
+        And the nodes
+            | node | highway | stop  |
+            | g    | stop    |       |
+            | m    | stop    | minor |
+
+
+        When I route I should get
+
+            # No road has stop signs
+            | from | to | time    | #                    |
+            | a    | b  | 14s +-1 |    turn              |
+            | a    | e  | 13s +-1 | no turn              |
+            | a    | d  | 17s +-1 |    turn              |
+            | e    | d  | 14s +-1 |    turn              |
+            | e    | a  | 13s +-1 | no turn              |
+            | e    | b  | 17s +-1 |    turn              |
+            | d    | a  | 14s +-1 |    turn              |
+            | d    | b  | 11s +-1 | no turn              |
+            | d    | e  | 17s +-1 |    turn              |
+            | b    | e  | 14s +-1 |    turn              |
+            | b    | d  | 11s +-1 | no turn              |
+            | b    | a  | 17s +-1 |    turn              |
+
+            # All roads have stop signs - 2s penalty
+            | f    | h  | 16s +-1 |    turn with stop    |
+            | f    | j  | 15s +-1 | no turn with stop    |
+            | f    | i  | 19s +-1 |    turn with stop    |
+            | j    | i  | 16s +-1 |    turn with stop    |
+            | j    | f  | 15s +-1 | no turn with stop    |
+            | j    | h  | 19s +-1 |    turn with stop    |
+            | i    | f  | 16s +-1 |    turn with stop    |
+            | i    | h  | 13s +-1 | no turn with stop    |
+            | i    | j  | 19s +-1 |    turn with stop    |
+            | h    | j  | 16s +-1 |    turn with stop    |
+            | h    | i  | 13s +-1 | no turn with stop    |
+            | h    | f  | 19s +-1 |    turn with stop    |
+
+            # Minor roads have stop signs - 2s penalty
+            | k    | l  | 16s +-1 |    turn with minor stop |
+            | k    | o  | 15s +-1 | no turn with minor stop |
+            | k    | n  | 19s +-1 |    turn with minor stop |
+            | o    | n  | 16s +-1 |    turn with minor stop |
+            | o    | k  | 15s +-1 | no turn with minor stop |
+            | o    | l  | 19s +-1 |    turn with minor stop |
+            | n    | k  | 14s +-1 |    turn                 |
+            | n    | l  | 11s +-1 | no turn                 |
+            | n    | o  | 17s +-1 |    turn                 |
+            | l    | o  | 14s +-1 |    turn                 |
+            | l    | n  | 11s +-1 | no turn                 |
+            | l    | k  | 17s +-1 |    turn                 |
+
+
+    Scenario: Car - Infer stop sign direction
+        Given a grid size of 5 meters
+
+        Given the node map
+            """
+            a---------sb
+
+            c---------td
+
+            e---------uf
+
+            g---------vh
+
+            """
+
+        And the ways
+            | nodes | highway   |
+            | asb   | primary   |
+            | ctd   | primary   |
+            | euf   | primary   |
+            | gvh   | primary   |
+
+        And the nodes
+            | node | highway | direction |
+            | s    | stop    |           |
+            | t    | stop    | forward   |
+            | u    | stop    | backward  |
+            | v    | stop    | both      |
+
+        When I route I should get
+            | from | to | time | #    |
+            | a    | b  | 3.5s | stop |
+            | b    | a  | 1.5s |      |
+            | c    | d  | 3.5s | stop |
+            | d    | c  | 1.5s |      |
+            | e    | f  | 1.5s |      |
+            | f    | e  | 3.5s | stop |
+            | g    | h  | 3.5s | stop |
+            | h    | g  | 3.5s | stop |
+
+
+    Scenario: Car - Infer stop sign direction 2
+        Given a grid size of 5 meters
+
+        Given the node map
+            """
+
+                       d
+                       |
+            a---------sbt---------c
+                       |
+                       e
+
+            """
+
+        And the ways
+            | nodes | highway |
+            | asbtc | primary |
+            | dbe   | primary |
+
+        And the nodes
+            | node | highway |
+            | s    | stop    |
+            | t    | stop    |
+
+        When I route I should get
+            | from | to | time    | #    |
+            | a    | d  | 9s +- 1 | stop |
+            | a    | c  | 5s +- 1 | stop |
+            | a    | e  | 6s +- 1 | stop |
+            | c    | e  | 9s +- 1 | stop |
+            | c    | a  | 5s +- 1 | stop |
+            | c    | d  | 6s +- 1 | stop |
+            | d    | c  | 7s +- 1 |      |
+            | d    | e  | 1s +- 1 |      |
+            | d    | a  | 4s +- 1 |      |
+            | e    | a  | 7s +- 1 |      |
+            | e    | d  | 1s +- 1 |      |
+            | e    | c  | 4s +- 1 |      |
diff --git a/features/car/traffic_light_penalties.feature b/features/car/traffic_light_penalties.feature
index 2a5ded5f02e..5311599b6df 100644
--- a/features/car/traffic_light_penalties.feature
+++ b/features/car/traffic_light_penalties.feature
@@ -81,7 +81,7 @@ Feature: Car - Handle traffic lights
         Given the profile file "car" initialized with
         """
         profile.properties.weight_name = 'distance'
-        profile.properties.traffic_light_penalty = 100000
+        profile.properties.traffic_signal_penalty = 1000
         """
 
         Given the node map
@@ -107,8 +107,8 @@ Feature: Car - Handle traffic lights
             | b    | traffic_signals |
 
         When I route I should get
-            | from | to | time      | distances | weight | #                                     |
-            | 1    | 2  | 100033.2s | 599.9m,0m | 599.8  | goes via the expensive traffic signal |
+            | from | to | time    | distances | weight | #                                     |
+            | 1    | 2  | 1033.2s | 599.9m,0m | 599.8  | goes via the expensive traffic signal |
 
 
 
@@ -243,7 +243,7 @@ Feature: Car - Handle traffic lights
             | a    | c  | abc,abc | _ibE_ibE?gJ?eJ |
 
 
-    Scenario: Traffic Signal Geometry - reverse signal
+    Scenario: Traffic Signal Geometry - backward signal
         Given the query options
             | overview   | full      |
             | geometries | polyline  |
@@ -259,7 +259,7 @@ Feature: Car - Handle traffic lights
 
         And the nodes
             | node | highway         | traffic_signals:direction |
-            | b    | traffic_signals | reverse                   |
+            | b    | traffic_signals | backward                  |
 
         When I route I should get
             | from | to | route   | geometry       |
diff --git a/features/car/turning_loop.feature b/features/car/turning_loop.feature
new file mode 100644
index 00000000000..1c4d8bb0d54
--- /dev/null
+++ b/features/car/turning_loop.feature
@@ -0,0 +1,32 @@
+@routing @car @turning_loop
+Feature: Car - Handle turning loop
+
+    Background:
+        Given the profile "car"
+
+    Scenario: Car - Must turn around at the first good opportunity
+        Given the node map
+            """
+            a-b-c-d-e-f-g
+            """
+
+        And the ways
+            | nodes | highway |
+            | ab    | primary |
+            | bc    | primary |
+            | cd    | primary |
+            | de    | primary |
+            | ef    | primary |
+            | fg    | primary |
+
+        And the nodes
+            | node | highway         |
+            | b    | turning_loop    |
+            | d    | turning_circle  |
+            | f    | mini_roundabout |
+
+        When I route I should get
+            | waypoints | bearings     | route         | turns                        |
+            | a,a       | 90,10 270,10 | ab,ab,ab      | depart,continue uturn,arrive |
+            | c,a       | 90,10 270,10 | cd,cd,ab      | depart,continue uturn,arrive |
+            | e,a       | 90,10 270,10 | ef,ef,ab      | depart,continue uturn,arrive |
diff --git a/include/extractor/edge_based_graph_factory.hpp b/include/extractor/edge_based_graph_factory.hpp
index 56aec7392c7..dd5454f519a 100644
--- a/include/extractor/edge_based_graph_factory.hpp
+++ b/include/extractor/edge_based_graph_factory.hpp
@@ -23,7 +23,6 @@
 #include "util/typedefs.hpp"
 
 #include "storage/io.hpp"
-#include "traffic_signals.hpp"
 
 #include <cstdint>
 #include <string>
@@ -60,8 +59,6 @@ class EdgeBasedGraphFactory
     explicit EdgeBasedGraphFactory(const util::NodeBasedDynamicGraph &node_based_graph,
                                    EdgeBasedNodeDataContainer &node_data_container,
                                    const CompressedEdgeContainer &compressed_edge_container,
-                                   const std::unordered_set<NodeID> &barrier_nodes,
-                                   const TrafficSignals &traffic_signals,
                                    const std::vector<util::Coordinate> &coordinates,
                                    const NameTable &name_table,
                                    const std::unordered_set<EdgeID> &segregated_edges,
@@ -126,8 +123,6 @@ class EdgeBasedGraphFactory
     const std::vector<util::Coordinate> &m_coordinates;
     const util::NodeBasedDynamicGraph &m_node_based_graph;
 
-    const std::unordered_set<NodeID> &m_barrier_nodes;
-    const TrafficSignals &m_traffic_signals;
     const CompressedEdgeContainer &m_compressed_edge_container;
 
     const NameTable &name_table;
diff --git a/include/extractor/extraction_containers.hpp b/include/extractor/extraction_containers.hpp
index 1711e7cb241..60b9cf64656 100644
--- a/include/extractor/extraction_containers.hpp
+++ b/include/extractor/extraction_containers.hpp
@@ -8,8 +8,6 @@
 #include "extractor/scripting_environment.hpp"
 
 #include "storage/tar_fwd.hpp"
-#include "traffic_lights.hpp"
-#include "traffic_signals.hpp"
 
 #include <unordered_map>
 #include <unordered_set>
@@ -26,19 +24,15 @@ namespace osrm::extractor
 class ExtractionContainers
 {
     using ReferencedWays = std::unordered_map<OSMWayID, NodesOfWay>;
-    using ReferencedTrafficSignals =
-        std::pair<std::unordered_set<OSMNodeID>, std::unordered_multimap<OSMNodeID, OSMNodeID>>;
     // The relationship between way and nodes is lost during node preparation.
-    // We identify the ways and nodes relevant to restrictions/overrides/signals prior to
+    // We identify the ways and nodes relevant to restrictions/overrides/obstacles prior to
     // node processing so that they can be referenced in the preparation phase.
     ReferencedWays IdentifyRestrictionWays();
     ReferencedWays IdentifyManeuverOverrideWays();
-    ReferencedTrafficSignals IdentifyTrafficSignals();
 
     void PrepareNodes();
     void PrepareManeuverOverrides(const ReferencedWays &maneuver_override_ways);
     void PrepareRestrictions(const ReferencedWays &restriction_ways);
-    void PrepareTrafficSignals(const ReferencedTrafficSignals &referenced_traffic_signals);
     void PrepareEdges(ScriptingEnvironment &scripting_environment);
 
     void WriteCharData(const std::string &file_name);
@@ -52,9 +46,7 @@ class ExtractionContainers
     using NameOffsets = std::vector<size_t>;
     using WayIDVector = std::vector<OSMWayID>;
     using WayNodeIDOffsets = std::vector<size_t>;
-    using InputTrafficSignal = std::pair<OSMNodeID, TrafficLightClass::Direction>;
 
-    std::vector<OSMNodeID> barrier_nodes;
     NodeIDVector used_node_id_list;
     NodeVector all_nodes_list;
     EdgeVector all_edges_list;
@@ -67,9 +59,6 @@ class ExtractionContainers
 
     unsigned max_internal_node_id;
 
-    std::vector<InputTrafficSignal> external_traffic_signals;
-    TrafficSignals internal_traffic_signals;
-
     std::vector<NodeBasedEdge> used_edges;
 
     // List of restrictions (conditional and unconditional) before we transform them into the
@@ -81,7 +70,6 @@ class ExtractionContainers
 
     std::vector<InputManeuverOverride> external_maneuver_overrides_list;
     std::vector<UnresolvedManeuverOverride> internal_maneuver_overrides;
-    std::unordered_set<NodeID> used_barrier_nodes;
     NodeVector used_nodes;
 
     ExtractionContainers();
diff --git a/include/extractor/extraction_node.hpp b/include/extractor/extraction_node.hpp
index ad46e9bcefc..189e37c7e3b 100644
--- a/include/extractor/extraction_node.hpp
+++ b/include/extractor/extraction_node.hpp
@@ -1,21 +1,17 @@
 #ifndef EXTRACTION_NODE_HPP
 #define EXTRACTION_NODE_HPP
 
-#include "traffic_lights.hpp"
+#include <osmium/osm/node.hpp>
 
 namespace osrm::extractor
 {
 
 struct ExtractionNode
 {
-    ExtractionNode() : traffic_lights(TrafficLightClass::NONE), barrier(false) {}
-    void clear()
-    {
-        traffic_lights = TrafficLightClass::NONE;
-        barrier = false;
-    }
-    TrafficLightClass::Direction traffic_lights;
-    bool barrier;
+    ExtractionNode() {}
+
+    // the current node
+    const osmium::Node *node;
 };
 } // namespace osrm::extractor
 
diff --git a/include/extractor/extraction_turn.hpp b/include/extractor/extraction_turn.hpp
index 4dafd823cb9..a779c920ed0 100644
--- a/include/extractor/extraction_turn.hpp
+++ b/include/extractor/extraction_turn.hpp
@@ -1,8 +1,10 @@
 #ifndef OSRM_EXTRACTION_TURN_HPP
 #define OSRM_EXTRACTION_TURN_HPP
 
+#include "extractor/graph_compressor.hpp"
 #include "extractor/road_classification.hpp"
 #include "extractor/travel_mode.hpp"
+#include "util/typedefs.hpp"
 
 #include <boost/numeric/conversion/cast.hpp>
 
@@ -13,6 +15,8 @@ namespace osrm::extractor
 
 struct ExtractionTurnLeg
 {
+    using EdgeData = util::NodeBasedDynamicGraph::EdgeData;
+
     ExtractionTurnLeg(bool is_restricted,
                       bool is_motorway,
                       bool is_link,
@@ -20,17 +24,34 @@ struct ExtractionTurnLeg
                       int highway_turn_classification,
                       int access_turn_classification,
                       int speed,
+                      double distance,
                       RoadPriorityClass::Enum priority_class,
                       bool is_incoming,
                       bool is_outgoing)
         : is_restricted(is_restricted), is_motorway(is_motorway), is_link(is_link),
           number_of_lanes(number_of_lanes),
           highway_turn_classification(highway_turn_classification),
-          access_turn_classification(access_turn_classification), speed(speed),
+          access_turn_classification(access_turn_classification), speed(speed), distance(distance),
           priority_class(priority_class), is_incoming(is_incoming), is_outgoing(is_outgoing)
     {
     }
 
+    ExtractionTurnLeg(EdgeData edge_data, bool is_incoming, bool is_outgoing)
+        : ExtractionTurnLeg(edge_data.flags.restricted,
+                            edge_data.flags.road_classification.IsMotorwayClass(),
+                            edge_data.flags.road_classification.IsLinkClass(),
+                            edge_data.flags.road_classification.GetNumberOfLanes(),
+                            edge_data.flags.highway_turn_classification,
+                            edge_data.flags.access_turn_classification,
+                            36.0 * from_alias<double>(edge_data.distance) /
+                                from_alias<double>(edge_data.duration),
+                            from_alias<double>(edge_data.distance),
+                            edge_data.flags.road_classification.GetPriority(),
+                            is_incoming,
+                            is_outgoing)
+    {
+    }
+
     bool is_restricted;
     bool is_motorway;
     bool is_link;
@@ -38,11 +59,25 @@ struct ExtractionTurnLeg
     int highway_turn_classification;
     int access_turn_classification;
     int speed;
+    double distance;
     RoadPriorityClass::Enum priority_class;
     bool is_incoming;
     bool is_outgoing;
 };
 
+// Structure reflected into LUA scripting
+//
+// This structure backs the `turn` parameter in the LUA `process_turn` function.
+// That function is called:
+//
+// 1. from graph_compressor.cpp when compressing nodes with obstacles.  A fake turn with
+//    just one incoming and one outgoing edge will be generated. `number_of_roads` will
+//    be 2. The returned penalties will be added to the compressed edge.
+//
+// 2. from edge_based_graph_factory.cpp during generation of edge-expanded edges. In
+//    this stage real turns will be modelled. The returned penalties will be added to
+//    the edge-expanded edge.
+
 struct ExtractionTurn
 {
     ExtractionTurn(double angle,
@@ -50,6 +85,7 @@ struct ExtractionTurn
                    bool is_u_turn,
                    bool has_traffic_light,
                    bool is_left_hand_driving,
+
                    bool source_restricted,
                    TravelMode source_mode,
                    bool source_is_motorway,
@@ -70,8 +106,13 @@ struct ExtractionTurn
                    int target_speed,
                    RoadPriorityClass::Enum target_priority_class,
 
+                   const ExtractionTurnLeg &source_road,
+                   const ExtractionTurnLeg &target_road,
                    const std::vector<ExtractionTurnLeg> &roads_on_the_right,
-                   const std::vector<ExtractionTurnLeg> &roads_on_the_left)
+                   const std::vector<ExtractionTurnLeg> &roads_on_the_left,
+                   const NodeID from,
+                   const NodeID via,
+                   const NodeID to)
         : angle(180. - angle), number_of_roads(number_of_roads), is_u_turn(is_u_turn),
           has_traffic_light(has_traffic_light), is_left_hand_driving(is_left_hand_driving),
 
@@ -89,11 +130,60 @@ struct ExtractionTurn
           target_access_turn_classification(target_access_turn_classification),
           target_speed(target_speed), target_priority_class(target_priority_class),
 
-          roads_on_the_right(roads_on_the_right), roads_on_the_left(roads_on_the_left), weight(0.),
-          duration(0.)
+          from(from), via(via), to(to),
+
+          source_road(source_road), target_road(target_road),
+          roads_on_the_right(roads_on_the_right), roads_on_the_left(roads_on_the_left),
 
+          weight(0.), duration(0.)
     {
     }
+
+    // to construct a "fake" turn while compressing obstacle nodes
+    // in graph_compressor.cpp
+    ExtractionTurn(NodeID from,
+                   NodeID via,
+                   NodeID to,
+                   const ExtractionTurnLeg::EdgeData &source_edge,
+                   const ExtractionTurnLeg::EdgeData &target_edge,
+                   const std::vector<ExtractionTurnLeg> &roads_on_the_right,
+                   const std::vector<ExtractionTurnLeg> &roads_on_the_left)
+        : ExtractionTurn{0,
+                         2,
+                         false,
+                         true,
+                         false,
+                         // source
+                         false,
+                         TRAVEL_MODE_DRIVING,
+                         false,
+                         false,
+                         1,
+                         0,
+                         0,
+                         0,
+                         0,
+                         // target
+                         false,
+                         TRAVEL_MODE_DRIVING,
+                         false,
+                         false,
+                         1,
+                         0,
+                         0,
+                         0,
+                         0,
+                         // other
+                         ExtractionTurnLeg{source_edge, true, false},
+                         ExtractionTurnLeg{target_edge, false, true},
+                         roads_on_the_right,
+                         roads_on_the_left,
+                         from,
+                         via,
+                         to}
+    {
+    }
+
     const double angle;
     const int number_of_roads;
     const bool is_u_turn;
@@ -122,6 +212,12 @@ struct ExtractionTurn
     const int target_speed;
     const RoadPriorityClass::Enum target_priority_class;
 
+    const NodeID from;
+    const NodeID via;
+    const NodeID to;
+
+    const ExtractionTurnLeg source_road;
+    const ExtractionTurnLeg target_road;
     const std::vector<ExtractionTurnLeg> roads_on_the_right;
     const std::vector<ExtractionTurnLeg> roads_on_the_left;
 
diff --git a/include/extractor/extractor.hpp b/include/extractor/extractor.hpp
index 1d36418aab2..4d30eb7e4b1 100644
--- a/include/extractor/extractor.hpp
+++ b/include/extractor/extractor.hpp
@@ -43,7 +43,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "util/guidance/turn_lanes.hpp"
 
 #include "restriction_graph.hpp"
-#include "traffic_signals.hpp"
 #include "util/typedefs.hpp"
 
 namespace osrm::extractor
@@ -64,8 +63,6 @@ class Extractor
         LaneDescriptionMap turn_lane_map;
         std::vector<TurnRestriction> turn_restrictions;
         std::vector<UnresolvedManeuverOverride> unresolved_maneuver_overrides;
-        TrafficSignals traffic_signals;
-        std::unordered_set<NodeID> barriers;
         std::vector<util::Coordinate> osm_coordinates;
         extractor::PackedOSMIDs osm_node_ids;
         std::vector<NodeBasedEdge> edge_list;
@@ -83,8 +80,6 @@ class Extractor
         const util::NodeBasedDynamicGraph &node_based_graph,
         const std::vector<util::Coordinate> &coordinates,
         const CompressedEdgeContainer &compressed_edge_container,
-        const std::unordered_set<NodeID> &barrier_nodes,
-        const TrafficSignals &traffic_signals,
         const RestrictionGraph &restriction_graph,
         const std::unordered_set<EdgeID> &segregated_edges,
         const NameTable &name_table,
@@ -112,7 +107,6 @@ class Extractor
                               const EdgeBasedNodeDataContainer &edge_based_node_container,
                               const std::vector<util::Coordinate> &node_coordinates,
                               const CompressedEdgeContainer &compressed_edge_container,
-                              const std::unordered_set<NodeID> &barrier_nodes,
                               const RestrictionGraph &restriction_graph,
                               const NameTable &name_table,
                               LaneDescriptionMap lane_description_map,
diff --git a/include/extractor/extractor_callbacks.hpp b/include/extractor/extractor_callbacks.hpp
index 83672f54977..9a493e27409 100644
--- a/include/extractor/extractor_callbacks.hpp
+++ b/include/extractor/extractor_callbacks.hpp
@@ -43,13 +43,13 @@ class ExtractorCallbacks
     using MapVal = unsigned;
     using StringMap = std::unordered_map<MapKey, MapVal>;
     StringMap string_map;
-    ExtractionContainers &external_memory;
     std::unordered_map<std::string, ClassData> &classes_map;
     LaneDescriptionMap &lane_description_map;
     bool fallback_to_duration;
     bool force_split_edges;
 
   public:
+    ExtractionContainers &external_memory;
     using ClassesMap = std::unordered_map<std::string, ClassData>;
 
     explicit ExtractorCallbacks(ExtractionContainers &extraction_containers,
diff --git a/include/extractor/graph_compressor.hpp b/include/extractor/graph_compressor.hpp
index 0e9a1070570..3af6662779d 100644
--- a/include/extractor/graph_compressor.hpp
+++ b/include/extractor/graph_compressor.hpp
@@ -2,10 +2,8 @@
 #define GEOMETRY_COMPRESSOR_HPP
 
 #include "extractor/scripting_environment.hpp"
-#include "util/typedefs.hpp"
-
-#include "traffic_signals.hpp"
 #include "util/node_based_graph.hpp"
+#include "util/typedefs.hpp"
 
 #include <unordered_set>
 #include <vector>
@@ -19,12 +17,10 @@ struct UnresolvedManeuverOverride;
 
 class GraphCompressor
 {
+  public:
     using EdgeData = util::NodeBasedDynamicGraph::EdgeData;
 
-  public:
-    void Compress(const std::unordered_set<NodeID> &barrier_nodes,
-                  TrafficSignals &traffic_signals,
-                  ScriptingEnvironment &scripting_environment,
+    void Compress(ScriptingEnvironment &scripting_environment,
                   std::vector<TurnRestriction> &turn_restrictions,
                   std::vector<UnresolvedManeuverOverride> &maneuver_overrides,
                   util::NodeBasedDynamicGraph &graph,
diff --git a/include/extractor/intersection/intersection_analysis.hpp b/include/extractor/intersection/intersection_analysis.hpp
index 9e0878df5d7..6765b5b52d5 100644
--- a/include/extractor/intersection/intersection_analysis.hpp
+++ b/include/extractor/intersection/intersection_analysis.hpp
@@ -6,6 +6,7 @@
 #include "extractor/intersection/intersection_view.hpp"
 #include "extractor/intersection/mergable_road_detector.hpp"
 #include "extractor/node_restriction_map.hpp"
+#include "extractor/obstacles.hpp"
 #include "extractor/turn_lane_types.hpp"
 
 #include "util/coordinate.hpp"
@@ -26,7 +27,7 @@ IntersectionEdges getOutgoingEdges(const util::NodeBasedDynamicGraph &graph,
 bool isTurnAllowed(const util::NodeBasedDynamicGraph &graph,
                    const EdgeBasedNodeDataContainer &node_data_container,
                    const RestrictionMap &restriction_map,
-                   const std::unordered_set<NodeID> &barrier_nodes,
+                   const ObstacleMap &obstacle_nodes,
                    const IntersectionEdgeGeometries &geometries,
                    const TurnLanesIndexedArray &turn_lanes_data,
                    const IntersectionEdge &from,
@@ -46,14 +47,14 @@ getIntersectionGeometries(const util::NodeBasedDynamicGraph &graph,
 IntersectionView convertToIntersectionView(const util::NodeBasedDynamicGraph &graph,
                                            const EdgeBasedNodeDataContainer &node_data_container,
                                            const RestrictionMap &restriction_map,
-                                           const std::unordered_set<NodeID> &barrier_nodes,
+                                           const ObstacleMap &obstacle_nodes,
                                            const IntersectionEdgeGeometries &edge_geometries,
                                            const TurnLanesIndexedArray &turn_lanes_data,
                                            const IntersectionEdge &incoming_edge,
                                            const IntersectionEdges &outgoing_edges,
                                            const std::unordered_set<EdgeID> &merged_edges);
 
-// Check for restrictions/barriers and generate a list of valid and invalid turns present at
+// Check for restrictions/obstacles and generate a list of valid and invalid turns present at
 // the node reached from `incoming_edge`. The resulting candidates have to be analyzed
 // for their actual instructions later on.
 template <bool USE_CLOSE_COORDINATE>
@@ -62,7 +63,7 @@ IntersectionView getConnectedRoads(const util::NodeBasedDynamicGraph &graph,
                                    const std::vector<util::Coordinate> &node_coordinates,
                                    const extractor::CompressedEdgeContainer &compressed_geometries,
                                    const RestrictionMap &node_restriction_map,
-                                   const std::unordered_set<NodeID> &barrier_nodes,
+                                   const ObstacleMap &obstacle_nodes,
                                    const TurnLanesIndexedArray &turn_lanes_data,
                                    const IntersectionEdge &incoming_edge);
 
@@ -70,15 +71,15 @@ IntersectionView
 getConnectedRoadsForEdgeGeometries(const util::NodeBasedDynamicGraph &graph,
                                    const EdgeBasedNodeDataContainer &node_data_container,
                                    const RestrictionMap &node_restriction_map,
-                                   const std::unordered_set<NodeID> &barrier_nodes,
+                                   const ObstacleMap &obstacle_nodes,
                                    const TurnLanesIndexedArray &turn_lanes_data,
                                    const IntersectionEdge &incoming_edge,
                                    const IntersectionEdgeGeometries &edge_geometries,
                                    const std::unordered_set<EdgeID> &merged_edge_ids);
 
-// Graph Compression cannot compress every setting. For example any barrier/traffic light cannot
+// Graph Compression cannot compress every setting. For example nodes with obstacles cannot
 // be compressed. As a result, a simple road of the form `a ----- b` might end up as having an
-// intermediate intersection, if there is a traffic light in between. If we want to look farther
+// intermediate intersection, if there is an obstacle in between. If we want to look farther
 // down a road, finding the next actual decision requires the look at multiple intersections.
 // Here we follow the road until we either reach a dead end or find the next intersection with
 // more than a single next road. This function skips over degree two nodes to find correct input
diff --git a/include/extractor/intersection/intersection_view.hpp b/include/extractor/intersection/intersection_view.hpp
index 4eb60585602..6cb721b1275 100644
--- a/include/extractor/intersection/intersection_view.hpp
+++ b/include/extractor/intersection/intersection_view.hpp
@@ -166,7 +166,7 @@ template <typename Self> struct EnableIntersectionOps
     }
 
     // Can this be skipped over?
-    auto isTrafficSignalOrBarrier() const { return self()->size() == 2; }
+    auto isObstacle() const { return self()->size() == 2; }
 
     // Checks if there is at least one road available (except UTurn road) on which to continue.
     auto isDeadEnd() const
diff --git a/include/extractor/intersection/mergable_road_detector.hpp b/include/extractor/intersection/mergable_road_detector.hpp
index 1f02afb5675..2686196a264 100644
--- a/include/extractor/intersection/mergable_road_detector.hpp
+++ b/include/extractor/intersection/mergable_road_detector.hpp
@@ -6,6 +6,7 @@
 #include "extractor/intersection/have_identical_names.hpp"
 #include "extractor/name_table.hpp"
 #include "extractor/node_restriction_map.hpp"
+#include "extractor/obstacles.hpp"
 #include "extractor/turn_lane_types.hpp"
 
 #include "guidance/intersection.hpp"
@@ -47,7 +48,7 @@ class MergableRoadDetector
                          const std::vector<util::Coordinate> &node_coordinates,
                          const extractor::CompressedEdgeContainer &compressed_geometries,
                          const RestrictionMap &node_restriction_map,
-                         const std::unordered_set<NodeID> &barrier_nodes,
+                         const ObstacleMap &obstacle_nodes,
                          const TurnLanesIndexedArray &turn_lanes_data,
                          const extractor::NameTable &name_table,
                          const SuffixTable &street_name_suffix_table);
@@ -164,7 +165,7 @@ class MergableRoadDetector
     const std::vector<util::Coordinate> &node_coordinates;
     const extractor::CompressedEdgeContainer &compressed_geometries;
     const RestrictionMap &node_restriction_map;
-    const std::unordered_set<NodeID> &barrier_nodes;
+    const ObstacleMap &obstacle_nodes;
     const TurnLanesIndexedArray &turn_lanes_data;
 
     // name detection
diff --git a/include/extractor/intersection/node_based_graph_walker.hpp b/include/extractor/intersection/node_based_graph_walker.hpp
index f3915ee9f1e..bafd662121f 100644
--- a/include/extractor/intersection/node_based_graph_walker.hpp
+++ b/include/extractor/intersection/node_based_graph_walker.hpp
@@ -30,7 +30,7 @@ class NodeBasedGraphWalker
                          const std::vector<util::Coordinate> &node_coordinates,
                          const extractor::CompressedEdgeContainer &compressed_geometries,
                          const RestrictionMap &node_restriction_map,
-                         const std::unordered_set<NodeID> &barrier_nodes,
+                         const ObstacleMap &obstacle_nodes,
                          const TurnLanesIndexedArray &turn_lanes_data);
 
     /*
@@ -53,7 +53,7 @@ class NodeBasedGraphWalker
     const std::vector<util::Coordinate> &node_coordinates;
     const extractor::CompressedEdgeContainer &compressed_geometries;
     const RestrictionMap &node_restriction_map;
-    const std::unordered_set<NodeID> &barrier_nodes;
+    const ObstacleMap &obstacle_nodes;
     const TurnLanesIndexedArray &turn_lanes_data;
 };
 
@@ -160,7 +160,7 @@ struct IntersectionFinderAccumulator
                                   const std::vector<util::Coordinate> &node_coordinates,
                                   const extractor::CompressedEdgeContainer &compressed_geometries,
                                   const RestrictionMap &node_restriction_map,
-                                  const std::unordered_set<NodeID> &barrier_nodes,
+                                  const ObstacleMap &obstacle_nodes,
                                   const TurnLanesIndexedArray &turn_lanes_data);
     // true if the path has traversed enough distance
     bool terminate();
@@ -182,7 +182,7 @@ struct IntersectionFinderAccumulator
     const std::vector<util::Coordinate> &node_coordinates;
     const extractor::CompressedEdgeContainer &compressed_geometries;
     const RestrictionMap &node_restriction_map;
-    const std::unordered_set<NodeID> &barrier_nodes;
+    const ObstacleMap &obstacle_nodes;
     const TurnLanesIndexedArray &turn_lanes_data;
 };
 
@@ -223,7 +223,7 @@ NodeBasedGraphWalker::TraverseRoad(NodeID current_node_id,
                                                   node_coordinates,
                                                   compressed_geometries,
                                                   node_restriction_map,
-                                                  barrier_nodes,
+                                                  obstacle_nodes,
                                                   turn_lanes_data,
                                                   {current_node_id, current_edge_id});
 
@@ -252,7 +252,7 @@ NodeBasedGraphWalker::TraverseRoad(NodeID current_node_id,
     return {};
 }
 
-struct SkipTrafficSignalBarrierRoadSelector
+struct SkipObstacleRoadSelector
 {
     std::optional<EdgeID> operator()(const NodeID,
                                      const EdgeID,
@@ -260,7 +260,7 @@ struct SkipTrafficSignalBarrierRoadSelector
                                      const util::NodeBasedDynamicGraph &,
                                      const EdgeBasedNodeDataContainer &) const
     {
-        if (intersection.isTrafficSignalOrBarrier())
+        if (intersection.isObstacle())
         {
             return std::make_optional(intersection[1].eid);
         }
diff --git a/include/extractor/node_based_edge.hpp b/include/extractor/node_based_edge.hpp
index 2abb98b205c..d98e2283e64 100644
--- a/include/extractor/node_based_edge.hpp
+++ b/include/extractor/node_based_edge.hpp
@@ -48,6 +48,8 @@ struct NodeBasedEdgeClassification
     {
     }
 
+    bool IsRestricted() const { return restricted; }
+
     bool operator==(const NodeBasedEdgeClassification &other) const
     {
         return (road_classification == other.road_classification) && (forward == other.forward) &&
diff --git a/include/extractor/node_based_graph_factory.hpp b/include/extractor/node_based_graph_factory.hpp
index 79b4b45cd34..8c08e78bd33 100644
--- a/include/extractor/node_based_graph_factory.hpp
+++ b/include/extractor/node_based_graph_factory.hpp
@@ -8,7 +8,6 @@
 #include "extractor/packed_osm_ids.hpp"
 #include "extractor/scripting_environment.hpp"
 
-#include "traffic_signals.hpp"
 #include "util/coordinate.hpp"
 #include "util/node_based_graph.hpp"
 
@@ -23,7 +22,7 @@ namespace osrm::extractor
 // edge-based graph and is also the basic concept for annotating paths. All information from ways
 // that is transferred into the API response is connected to the edges of the node-based graph.
 //
-// The input to the graph creation is the set of edges, traffic signals, barriers, meta-data,...
+// The input to the graph creation is the set of edges, obstacles, meta-data,...
 // which is generated during extraction and stored within the extraction containers.
 class NodeBasedGraphFactory
 {
@@ -35,15 +34,12 @@ class NodeBasedGraphFactory
     NodeBasedGraphFactory(ScriptingEnvironment &scripting_environment,
                           std::vector<TurnRestriction> &turn_restrictions,
                           std::vector<UnresolvedManeuverOverride> &maneuver_overrides,
-                          TrafficSignals &traffic_signals,
-                          std::unordered_set<NodeID> &&barriers,
                           std::vector<util::Coordinate> &&coordinates,
                           extractor::PackedOSMIDs &&osm_node_ids,
                           const std::vector<NodeBasedEdge> &edge_list,
                           std::vector<NodeBasedEdgeAnnotation> &&annotation_data);
 
     auto const &GetGraph() const { return compressed_output_graph; }
-    auto const &GetBarriers() const { return barriers; }
     auto const &GetCompressedEdges() const { return compressed_edge_container; }
     auto const &GetCoordinates() const { return coordinates; }
     auto const &GetAnnotationData() const { return annotation_data; }
@@ -66,8 +62,7 @@ class NodeBasedGraphFactory
     // edges into a single representative form
     void Compress(ScriptingEnvironment &scripting_environment,
                   std::vector<TurnRestriction> &turn_restrictions,
-                  std::vector<UnresolvedManeuverOverride> &maneuver_overrides,
-                  TrafficSignals &traffic_signals);
+                  std::vector<UnresolvedManeuverOverride> &maneuver_overrides);
 
     // Most ways are bidirectional, making the geometry in forward and backward direction the same,
     // except for reversal. We make use of this fact by keeping only one representation of the
@@ -87,8 +82,6 @@ class NodeBasedGraphFactory
     std::vector<NodeBasedEdgeAnnotation> annotation_data;
 
     // General Information about the graph, not used outside of extractor
-    std::unordered_set<NodeID> barriers;
-
     std::vector<util::Coordinate> coordinates;
 
     // data to keep in sync with the node-based graph
diff --git a/include/extractor/obstacles.hpp b/include/extractor/obstacles.hpp
new file mode 100644
index 00000000000..b2ca3039172
--- /dev/null
+++ b/include/extractor/obstacles.hpp
@@ -0,0 +1,211 @@
+/*
+
+Copyright (c) 2025, Project OSRM contributors
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list
+of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this
+list of conditions and the following disclaimer in the documentation and/or
+other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+#ifndef OSRM_EXTRACTOR_OBSTACLES_DATA_HPP_
+#define OSRM_EXTRACTOR_OBSTACLES_DATA_HPP_
+
+#include "util/typedefs.hpp"
+
+#include <osmium/osm/node.hpp>
+#include <tbb/concurrent_vector.h>
+
+#include <ranges>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+namespace osrm::extractor
+{
+// A class that represents an obstacle on the road or a place where you can turn around.
+//
+// This may be a completely impassable obstacle like a barrier, a temporary obstacle
+// like a traffic light or a stop sign, or an obstacle that just slows you down like a
+// traffic calming bump.  The obstacle may be present in both directions or in one
+// direction only.
+//
+// This also represents a good turning point like a mini_roundabout, turning_loop, or
+// turning_circle.
+//
+// Note: A better name would have been 'WayPoint', but that name is ambiguous in the
+// OSRM-context.
+//
+struct Obstacle
+{
+    // The type of an obstacle
+    // Note: must be kept in sync with the initializer_list in obstacles.cpp
+    enum class Type : uint16_t
+    {
+        None = 0x0000,
+        Barrier = 0x0001,
+        TrafficSignals = 0x0002,
+        Stop = 0x0004,
+        GiveWay = 0x0008,
+        Crossing = 0x0010,
+        TrafficCalming = 0x0020,
+        MiniRoundabout = 0x0040,
+        TurningLoop = 0x0080,
+        TurningCircle = 0x0100,
+        StopMinor = 0x0200,
+
+        Turning = MiniRoundabout | TurningLoop | TurningCircle,
+        Incompressible = Barrier | Turning,
+        All = 0xFFFF
+    };
+
+    static const std::initializer_list<std::pair<std::string_view, Type>>
+        enum_type_initializer_list;
+
+    // The directions in which an obstacle applies.
+    // Note: must be kept in sync with the initializer_list in obstacles.cpp
+    enum class Direction : uint8_t
+    {
+        None = 0x0,
+        Forward = 0x1,
+        Backward = 0x2,
+        Both = 0x3
+    };
+
+    static const std::initializer_list<std::pair<std::string_view, Direction>>
+        enum_direction_initializer_list;
+
+    // use overloading instead of default arguments for sol::constructors
+    Obstacle(Type t_type) : type{t_type} {};
+    Obstacle(Type t_type, Direction t_direction) : type{t_type}, direction{t_direction} {};
+    Obstacle(Type t_type, Direction t_direction, float t_duration_penalty, float t_weight_penalty)
+        : type{t_type}, direction{t_direction}, duration{t_duration_penalty},
+          weight{t_weight_penalty} {};
+
+    Type type;
+    Direction direction{Direction::None};
+    float duration{0}; // in seconds
+    float weight{0};   // unit: profile.weight_multiplier
+};
+
+// A class that holds all known nodes with obstacles.
+//
+// This class holds all obstacles, bidirectional and unidirectional ones.  For
+// unidirectional obstacle nodes it also stores the node leading to it.
+//
+// Notes:
+//
+// Before fixupNodes() it uses the external node id (aka. OSMNodeID).
+// After fixupNodes() it uses the internal node id (aka. NodeID).
+//
+class ObstacleMap
+{
+    using NodeIDVector = std::vector<OSMNodeID>;
+    using WayNodeIDOffsets = std::vector<size_t>;
+
+    using OsmFromToObstacle = std::tuple<OSMNodeID, OSMNodeID, Obstacle>;
+    using FromToObstacle = std::tuple<NodeID, NodeID, Obstacle>;
+
+  public:
+    ObstacleMap() = default;
+
+    // Insert an obstacle using the OSM node id.
+    //
+    // This function is thread-safe.
+    void emplace(OSMNodeID osm_node_id, const Obstacle &obstacle)
+    {
+        osm_obstacles.emplace_back(SPECIAL_OSM_NODEID, osm_node_id, obstacle);
+    };
+
+    // Insert an obstacle using internal node ids.
+    //
+    // The obstacle is at 'to'. For bidirectional obstacles set 'from' to SPECIAL_NODEID.
+    // Convenient for testing.
+    void emplace(NodeID from, NodeID to, const Obstacle &obstacle)
+    {
+        obstacles.emplace(to, std::make_tuple(from, to, obstacle));
+    };
+
+    // get all obstacles at node 'to' when coming from node 'from'
+    // pass SPECIAL_NODEID as 'from' to get all obstacles at 'to'
+    // 'type' can be a bitwise-or combination of Obstacle::Type
+    std::vector<Obstacle>
+    get(NodeID from, NodeID to, Obstacle::Type type = Obstacle::Type::All) const
+    {
+        std::vector<Obstacle> result;
+
+        auto [begin, end] = obstacles.equal_range(to);
+        for (auto &[key, value] : std::ranges::subrange(begin, end))
+        {
+            auto &[from_id, to_id, obstacle] = value;
+            if ((from_id == SPECIAL_NODEID || from_id == from) &&
+                (static_cast<uint16_t>(obstacle.type) & static_cast<uint16_t>(type)))
+            {
+                result.push_back(obstacle);
+            }
+        }
+        return result; // vector has move semantics
+    }
+
+    std::vector<Obstacle> get(NodeID to) const
+    {
+        return get(SPECIAL_NODEID, to, Obstacle::Type::All);
+    }
+
+    // is there any obstacle at node 'to'?
+    // inexpensive general test
+    bool any(NodeID to) const { return obstacles.contains(to); }
+
+    // is there any obstacle of type 'type' at node 'to' when coming from node 'from'?
+    // pass SPECIAL_NODEID as 'from' to query all obstacles at 'to'
+    // 'type' can be a bitwise-or combination of Obstacle::Type
+    bool any(NodeID from, NodeID to, Obstacle::Type type = Obstacle::Type::All) const
+    {
+        return any(to) && !get(from, to, type).empty();
+    }
+
+    // Preprocess the obstacles
+    //
+    // For all unidirectional obstacles find the node that leads up to them.  This
+    // function must be called while the vector 'node_ids' is still unsorted /
+    // uncompressed.
+    void preProcess(const NodeIDVector &, const WayNodeIDOffsets &);
+
+    // Convert all external OSMNodeIDs into internal NodeIDs.
+    //
+    // This function must be called when the vector 'node_ids' is already sorted and
+    // compressed.  Call this function once only.
+    void fixupNodes(const NodeIDVector &);
+
+    // Replace a leading node that is going to be deleted during graph
+    // compression with the leading node of the leading node.
+    void compress(NodeID from, NodeID delendus, NodeID to);
+
+  private:
+    // obstacles according to external id
+    tbb::concurrent_vector<OsmFromToObstacle> osm_obstacles;
+
+    // obstacles according to internal id, with a leading node if unidirectional
+    // bidirectional obstacles are stored with a leading node id == SPECIAL_NODE_ID
+    std::unordered_multimap<NodeID, FromToObstacle> obstacles;
+};
+
+} // namespace osrm::extractor
+#endif // OSRM_EXTRACTOR_OBSTACLES_DATA_HPP_
diff --git a/include/extractor/road_classification.hpp b/include/extractor/road_classification.hpp
index 679e1d1f6ca..875e9036dad 100644
--- a/include/extractor/road_classification.hpp
+++ b/include/extractor/road_classification.hpp
@@ -99,7 +99,7 @@ class RoadClassification
     std::uint8_t GetNumberOfLanes() const { return number_of_lanes; }
     void SetNumberOfLanes(const std::uint8_t new_value) { number_of_lanes = new_value; }
 
-    std::uint32_t GetPriority() const { return static_cast<std::uint32_t>(road_priority_class); }
+    RoadPriorityClass::Enum GetPriority() const { return road_priority_class; }
 
     RoadPriorityClass::Enum GetClass() const { return road_priority_class; }
     void SetClass(const RoadPriorityClass::Enum new_value) { road_priority_class = new_value; }
diff --git a/include/extractor/scripting_environment.hpp b/include/extractor/scripting_environment.hpp
index 2a11afc33d1..662aea23527 100644
--- a/include/extractor/scripting_environment.hpp
+++ b/include/extractor/scripting_environment.hpp
@@ -3,6 +3,7 @@
 
 #include "extractor/internal_extractor_edge.hpp"
 #include "extractor/maneuver_override.hpp"
+#include "extractor/obstacles.hpp"
 #include "extractor/profile_properties.hpp"
 #include "extractor/restriction.hpp"
 
@@ -72,6 +73,8 @@ class ScriptingEnvironment
                     std::vector<InputManeuverOverride> &resulting_maneuver_overrides) = 0;
 
     virtual bool HasLocationDependentData() const = 0;
+
+    ObstacleMap m_obstacle_map; // The obstacle map shared by all threads.
 };
 } // namespace extractor
 } // namespace osrm
diff --git a/include/extractor/traffic_lights.hpp b/include/extractor/traffic_lights.hpp
deleted file mode 100644
index 1bb54bd91c3..00000000000
--- a/include/extractor/traffic_lights.hpp
+++ /dev/null
@@ -1,17 +0,0 @@
-#ifndef OSRM_EXTRACTOR_TRAFFIC_LIGHTS_DATA_HPP_
-#define OSRM_EXTRACTOR_TRAFFIC_LIGHTS_DATA_HPP_
-
-namespace osrm::extractor::TrafficLightClass
-{
-// The traffic light annotation is extracted from node tags.
-// The directions in which the traffic light applies are relative to the way containing the node.
-enum Direction
-{
-    NONE = 0,
-    DIRECTION_ALL = 1,
-    DIRECTION_FORWARD = 2,
-    DIRECTION_REVERSE = 3
-};
-} // namespace osrm::extractor::TrafficLightClass
-
-#endif // OSRM_EXTRACTOR_TRAFFIC_LIGHTS_DATA_HPP_
diff --git a/include/extractor/traffic_signals.hpp b/include/extractor/traffic_signals.hpp
deleted file mode 100644
index af0d3daeb19..00000000000
--- a/include/extractor/traffic_signals.hpp
+++ /dev/null
@@ -1,40 +0,0 @@
-#ifndef OSRM_EXTRACTOR_TRAFFIC_SIGNALS_HPP
-#define OSRM_EXTRACTOR_TRAFFIC_SIGNALS_HPP
-
-#include "util/std_hash.hpp"
-#include "util/typedefs.hpp"
-
-#include <unordered_set>
-#include <utility>
-
-namespace osrm::extractor
-{
-
-struct TrafficSignals
-{
-    std::unordered_set<NodeID> bidirectional_nodes;
-    std::unordered_set<std::pair<NodeID, NodeID>> unidirectional_segments;
-
-    inline bool HasSignal(NodeID from, NodeID to) const
-    {
-        return bidirectional_nodes.contains(to) || unidirectional_segments.contains({from, to});
-    }
-
-    void Compress(NodeID from, NodeID via, NodeID to)
-    {
-        bidirectional_nodes.erase(via);
-        if (unidirectional_segments.contains({via, to}))
-        {
-            unidirectional_segments.erase({via, to});
-            unidirectional_segments.insert({from, to});
-        }
-        if (unidirectional_segments.contains({via, from}))
-        {
-            unidirectional_segments.erase({via, from});
-            unidirectional_segments.insert({to, from});
-        }
-    }
-};
-} // namespace osrm::extractor
-
-#endif // OSRM_EXTRACTOR_TRAFFIC_SIGNALS_HPP
diff --git a/include/extractor/turn_path_compressor.hpp b/include/extractor/turn_path_compressor.hpp
index 4a6bbea8987..e4e7f4da175 100644
--- a/include/extractor/turn_path_compressor.hpp
+++ b/include/extractor/turn_path_compressor.hpp
@@ -16,7 +16,7 @@ struct UnresolvedManeuverOverride;
 // OSRM stores turn paths as node -> [node] -> node instead of way -> node -> way (or
 // way->[way]->way) as it is done in OSM. These paths need to match the state of graph
 // compression which we perform in the graph compressor that removes certain degree two nodes from
-// the graph (all but the ones with penalties/barriers, as of the state of writing).
+// the graph (all but the ones with obstacles, as of the state of writing).
 // Since this graph compression is performed after creating the turn paths in the extraction
 // phase, we need to update the involved nodes whenever one of the nodes is compressed.
 //
diff --git a/include/guidance/driveway_handler.hpp b/include/guidance/driveway_handler.hpp
index a33cab9a863..81cc9a0daeb 100644
--- a/include/guidance/driveway_handler.hpp
+++ b/include/guidance/driveway_handler.hpp
@@ -15,7 +15,7 @@ class DrivewayHandler final : public IntersectionHandler
                     const std::vector<util::Coordinate> &coordinates,
                     const extractor::CompressedEdgeContainer &compressed_geometries,
                     const extractor::RestrictionMap &node_restriction_map,
-                    const std::unordered_set<NodeID> &barrier_nodes,
+                    const extractor::ObstacleMap &obstacle_nodes,
                     const extractor::TurnLanesIndexedArray &turn_lanes_data,
                     const extractor::NameTable &name_table,
                     const extractor::SuffixTable &street_name_suffix_table);
diff --git a/include/guidance/guidance_processing.hpp b/include/guidance/guidance_processing.hpp
index e0aea1d5423..a95554120ac 100644
--- a/include/guidance/guidance_processing.hpp
+++ b/include/guidance/guidance_processing.hpp
@@ -7,6 +7,7 @@
 #include "extractor/name_table.hpp"
 #include "extractor/node_data_container.hpp"
 #include "extractor/node_restriction_map.hpp"
+#include "extractor/obstacles.hpp"
 #include "extractor/suffix_table.hpp"
 #include "extractor/turn_lane_types.hpp"
 #include "extractor/way_restriction_map.hpp"
@@ -29,7 +30,7 @@ void annotateTurns(const util::NodeBasedDynamicGraph &node_based_graph,
                    const extractor::EdgeBasedNodeDataContainer &edge_based_node_container,
                    const std::vector<util::Coordinate> &node_coordinates,
                    const extractor::CompressedEdgeContainer &compressed_edge_container,
-                   const std::unordered_set<NodeID> &barrier_nodes,
+                   const extractor::ObstacleMap &obstacle_nodes,
                    const extractor::RestrictionMap &node_restriction_map,
                    const extractor::WayRestrictionMap &way_restriction_map,
                    const extractor::NameTable &name_table,
diff --git a/include/guidance/intersection_handler.hpp b/include/guidance/intersection_handler.hpp
index 2f657ee05ed..9cd130634fc 100644
--- a/include/guidance/intersection_handler.hpp
+++ b/include/guidance/intersection_handler.hpp
@@ -32,7 +32,7 @@ class IntersectionHandler
                         const std::vector<util::Coordinate> &node_coordinates,
                         const extractor::CompressedEdgeContainer &compressed_geometries,
                         const extractor::RestrictionMap &node_restriction_map,
-                        const std::unordered_set<NodeID> &barrier_nodes,
+                        const extractor::ObstacleMap &obstacle_nodes,
                         const extractor::TurnLanesIndexedArray &turn_lanes_data,
                         const extractor::NameTable &name_table,
                         const extractor::SuffixTable &street_name_suffix_table);
@@ -53,7 +53,7 @@ class IntersectionHandler
     const std::vector<util::Coordinate> &node_coordinates;
     const extractor::CompressedEdgeContainer &compressed_geometries;
     const extractor::RestrictionMap &node_restriction_map;
-    const std::unordered_set<NodeID> &barrier_nodes;
+    const extractor::ObstacleMap &obstacle_nodes;
     const extractor::TurnLanesIndexedArray &turn_lanes_data;
     const extractor::NameTable &name_table;
     const extractor::SuffixTable &street_name_suffix_table;
@@ -114,8 +114,8 @@ class IntersectionHandler
         NodeID node;                                            // < node at this intersection
     };
 
-    // Skips over artificial intersections i.e. traffic lights, barriers etc.
-    // Returns the next non-artificial intersection and its node in the node based
+    // Skips over a string of edges that could not be compressed because of obstacles.
+    // Returns the next intersection and its node in the node based
     // graph if an intersection could be found or none otherwise.
     //
     //  a ... tl ... b .. c
diff --git a/include/guidance/motorway_handler.hpp b/include/guidance/motorway_handler.hpp
index 8f399156de1..bda5ffc71ce 100644
--- a/include/guidance/motorway_handler.hpp
+++ b/include/guidance/motorway_handler.hpp
@@ -23,7 +23,7 @@ class MotorwayHandler final : public IntersectionHandler
                     const std::vector<util::Coordinate> &coordinates,
                     const extractor::CompressedEdgeContainer &compressed_geometries,
                     const extractor::RestrictionMap &node_restriction_map,
-                    const std::unordered_set<NodeID> &barrier_nodes,
+                    const extractor::ObstacleMap &obstacle_nodes,
                     const extractor::TurnLanesIndexedArray &turn_lanes_data,
                     const extractor::NameTable &name_table,
                     const extractor::SuffixTable &street_name_suffix_table);
diff --git a/include/guidance/roundabout_handler.hpp b/include/guidance/roundabout_handler.hpp
index 29fda3786f7..150f182226e 100644
--- a/include/guidance/roundabout_handler.hpp
+++ b/include/guidance/roundabout_handler.hpp
@@ -41,7 +41,7 @@ class RoundaboutHandler final : public IntersectionHandler
                       const std::vector<util::Coordinate> &coordinates,
                       const extractor::CompressedEdgeContainer &compressed_geometries,
                       const extractor::RestrictionMap &node_restriction_map,
-                      const std::unordered_set<NodeID> &barrier_nodes,
+                      const extractor::ObstacleMap &obstacle_nodes,
                       const extractor::TurnLanesIndexedArray &turn_lanes_data,
                       const extractor::NameTable &name_table,
                       const extractor::SuffixTable &street_name_suffix_table);
diff --git a/include/guidance/sliproad_handler.hpp b/include/guidance/sliproad_handler.hpp
index 533ac4dcfb2..a7e43f3c66e 100644
--- a/include/guidance/sliproad_handler.hpp
+++ b/include/guidance/sliproad_handler.hpp
@@ -24,7 +24,7 @@ class SliproadHandler final : public IntersectionHandler
                     const std::vector<util::Coordinate> &coordinates,
                     const extractor::CompressedEdgeContainer &compressed_geometries,
                     const extractor::RestrictionMap &node_restriction_map,
-                    const std::unordered_set<NodeID> &barrier_nodes,
+                    const extractor::ObstacleMap &obstacle_nodes,
                     const extractor::TurnLanesIndexedArray &turn_lanes_data,
                     const extractor::NameTable &name_table,
                     const extractor::SuffixTable &street_name_suffix_table);
diff --git a/include/guidance/statistics_handler.hpp b/include/guidance/statistics_handler.hpp
index f0e3401c70a..3e4123754fa 100644
--- a/include/guidance/statistics_handler.hpp
+++ b/include/guidance/statistics_handler.hpp
@@ -28,7 +28,7 @@ class StatisticsHandler final : public IntersectionHandler
                       const std::vector<util::Coordinate> &coordinates,
                       const extractor::CompressedEdgeContainer &compressed_geometries,
                       const extractor::RestrictionMap &node_restriction_map,
-                      const std::unordered_set<NodeID> &barrier_nodes,
+                      const extractor::ObstacleMap &obstacle_nodes,
                       const extractor::TurnLanesIndexedArray &turn_lanes_data,
                       const extractor::NameTable &name_table,
                       const extractor::SuffixTable &street_name_suffix_table)
@@ -37,7 +37,7 @@ class StatisticsHandler final : public IntersectionHandler
                               coordinates,
                               compressed_geometries,
                               node_restriction_map,
-                              barrier_nodes,
+                              obstacle_nodes,
                               turn_lanes_data,
                               name_table,
                               street_name_suffix_table)
diff --git a/include/guidance/suppress_mode_handler.hpp b/include/guidance/suppress_mode_handler.hpp
index 447a3fdb3b0..cf9acde18b9 100644
--- a/include/guidance/suppress_mode_handler.hpp
+++ b/include/guidance/suppress_mode_handler.hpp
@@ -22,7 +22,7 @@ class SuppressModeHandler final : public IntersectionHandler
                         const std::vector<util::Coordinate> &coordinates,
                         const extractor::CompressedEdgeContainer &compressed_geometries,
                         const extractor::RestrictionMap &node_restriction_map,
-                        const std::unordered_set<NodeID> &barrier_nodes,
+                        const extractor::ObstacleMap &obstacle_nodes,
                         const extractor::TurnLanesIndexedArray &turn_lanes_data,
                         const extractor::NameTable &name_table,
                         const extractor::SuffixTable &street_name_suffix_table);
diff --git a/include/guidance/turn_analysis.hpp b/include/guidance/turn_analysis.hpp
index 56a3e5804bc..704ef007dce 100644
--- a/include/guidance/turn_analysis.hpp
+++ b/include/guidance/turn_analysis.hpp
@@ -33,7 +33,7 @@ class TurnAnalysis
                  const std::vector<util::Coordinate> &node_coordinates,
                  const extractor::CompressedEdgeContainer &compressed_edge_container,
                  const extractor::RestrictionMap &restriction_map,
-                 const std::unordered_set<NodeID> &barrier_nodes,
+                 const extractor::ObstacleMap &obstacle_nodes,
                  const extractor::TurnLanesIndexedArray &turn_lanes_data,
                  const extractor::NameTable &name_table,
                  const extractor::SuffixTable &street_name_suffix_table);
diff --git a/include/guidance/turn_discovery.hpp b/include/guidance/turn_discovery.hpp
index 67aaa1259c2..d354e5ee9b8 100644
--- a/include/guidance/turn_discovery.hpp
+++ b/include/guidance/turn_discovery.hpp
@@ -2,6 +2,7 @@
 #define OSRM_GUIDANCE_TURN_DISCOVERY_HPP_
 
 #include "extractor/node_restriction_map.hpp"
+#include "extractor/obstacles.hpp"
 #include "guidance/intersection.hpp"
 #include "guidance/turn_lane_data.hpp"
 #include "util/typedefs.hpp"
@@ -35,7 +36,7 @@ bool findPreviousIntersection(
     const std::vector<util::Coordinate> &node_coordinates,
     const extractor::CompressedEdgeContainer &compressed_geometries,
     const extractor::RestrictionMap &node_restriction_map,
-    const std::unordered_set<NodeID> &barrier_nodes,
+    const extractor::ObstacleMap &obstacle_nodes,
     const extractor::TurnLanesIndexedArray &turn_lanes_data,
     // output parameters, will be in an arbitrary state on failure
     NodeID &result_node,
diff --git a/include/guidance/turn_handler.hpp b/include/guidance/turn_handler.hpp
index 5d4348cab4a..e2ac0564e25 100644
--- a/include/guidance/turn_handler.hpp
+++ b/include/guidance/turn_handler.hpp
@@ -26,7 +26,7 @@ class TurnHandler final : public IntersectionHandler
                 const std::vector<util::Coordinate> &coordinates,
                 const extractor::CompressedEdgeContainer &compressed_geometries,
                 const extractor::RestrictionMap &node_restriction_map,
-                const std::unordered_set<NodeID> &barrier_nodes,
+                const extractor::ObstacleMap &obstacle_nodes,
                 const extractor::TurnLanesIndexedArray &turn_lanes_data,
                 const extractor::NameTable &name_table,
                 const extractor::SuffixTable &street_name_suffix_table);
diff --git a/include/guidance/turn_lane_handler.hpp b/include/guidance/turn_lane_handler.hpp
index b7c9e1a042b..53791a02f98 100644
--- a/include/guidance/turn_lane_handler.hpp
+++ b/include/guidance/turn_lane_handler.hpp
@@ -57,7 +57,7 @@ class TurnLaneHandler
                     const std::vector<util::Coordinate> &node_coordinates,
                     const extractor::CompressedEdgeContainer &compressed_geometries,
                     const extractor::RestrictionMap &node_restriction_map,
-                    const std::unordered_set<NodeID> &barrier_nodes,
+                    const extractor::ObstacleMap &obstacle_nodes,
                     const extractor::TurnLanesIndexedArray &turn_lanes_data,
                     extractor::LaneDescriptionMap &lane_description_map,
                     const TurnAnalysis &turn_analysis,
@@ -78,7 +78,7 @@ class TurnLaneHandler
     const std::vector<util::Coordinate> &node_coordinates;
     const extractor::CompressedEdgeContainer &compressed_geometries;
     const extractor::RestrictionMap &node_restriction_map;
-    const std::unordered_set<NodeID> &barrier_nodes;
+    const extractor::ObstacleMap &obstacle_nodes;
     const extractor::TurnLanesIndexedArray &turn_lanes_data;
 
     std::vector<std::uint32_t> turn_lane_offsets;
diff --git a/profiles/bicycle.lua b/profiles/bicycle.lua
index 2c701e218b3..76335155365 100644
--- a/profiles/bicycle.lua
+++ b/profiles/bicycle.lua
@@ -17,7 +17,7 @@ function setup()
   return {
     properties = {
       u_turn_penalty                = 20,
-      traffic_light_penalty         = 2,
+      traffic_signal_penalty        = 2,
       --weight_name                   = 'cyclability',
       weight_name                   = 'duration',
       process_call_tagless_node     = false,
@@ -706,7 +706,7 @@ function process_turn(profile, turn)
   end
 
   if turn.has_traffic_light then
-     turn.duration = turn.duration + profile.properties.traffic_light_penalty
+     turn.duration = turn.duration + profile.properties.traffic_signal_penalty
   end
   if profile.properties.weight_name == 'cyclability' then
     turn.weight = turn.duration
diff --git a/profiles/car.lua b/profiles/car.lua
index 24079bfaae0..35b4178d9cd 100644
--- a/profiles/car.lua
+++ b/profiles/car.lua
@@ -6,7 +6,7 @@ Set = require('lib/set')
 Sequence = require('lib/sequence')
 Handlers = require("lib/way_handlers")
 Relations = require("lib/relations")
-TrafficSignal = require("lib/traffic_signal")
+Obstacles = require("lib/obstacles")
 find_access_tag = require("lib/access").find_access_tag
 limit = require("lib/maxspeed").limit
 Utils = require("lib/utils")
@@ -27,7 +27,6 @@ function setup()
       continue_straight_at_waypoint  = true,
       use_turn_restrictions          = true,
       left_hand_driving              = false,
-      traffic_light_penalty          = 2,
     },
 
     default_mode              = mode.driving,
@@ -333,7 +332,7 @@ function process_node(profile, node, result, relations)
   local access = find_access_tag(node, profile.access_tags_hierarchy)
   if access then
     if profile.access_tag_blacklist[access] and not profile.restricted_access_tag_list[access] then
-      result.barrier = true
+      obstacle_map:add(node, Obstacle.new(obstacle_type.barrier))
     end
   else
     local barrier = node:get_value_by_key("barrier")
@@ -361,13 +360,12 @@ function process_node(profile, node, result, relations)
                 and not flat_kerb
                 and not highway_crossing_kerb
                 or restricted_by_height then
-        result.barrier = true
+        obstacle_map:add(node, Obstacle.new(obstacle_type.barrier))
       end
     end
   end
 
-  -- check if node is a traffic light
-  result.traffic_lights = TrafficSignal.get_value(node)
+  Obstacles.process_node(profile, node)
 end
 
 function process_way(profile, way, result, relations)
@@ -476,8 +474,24 @@ function process_turn(profile, turn)
   local turn_penalty = profile.turn_penalty
   local turn_bias = turn.is_left_hand_driving and 1. / profile.turn_bias or profile.turn_bias
 
-  if turn.has_traffic_light then
-      turn.duration = profile.properties.traffic_light_penalty
+  for _, obs in pairs(obstacle_map:get(turn.from, turn.via)) do
+    -- disregard a minor stop if entering by the major road
+    -- rationale: if a stop sign is tagged at the center of the intersection with stop=minor
+    -- it should only penalize the minor roads entering the intersection
+    if obs.type == obstacle_type.stop_minor and not Obstacles.entering_by_minor_road(turn) then
+        goto skip
+    end
+    -- heuristic to infer the direction of a stop without an explicit direction tag
+    -- rationale: a stop sign should not be placed farther than 20m from the intersection
+    if turn.number_of_roads == 2
+        and obs.type == obstacle_type.stop
+        and obs.direction == obstacle_direction.none
+        and turn.source_road.distance < 20
+        and turn.target_road.distance > 20 then
+            goto skip
+    end
+    turn.duration = turn.duration + obs.duration
+    ::skip::
   end
 
   if turn.number_of_roads > 2 or turn.source_mode ~= turn.target_mode or turn.is_u_turn then
diff --git a/profiles/foot.lua b/profiles/foot.lua
index fa5f8b6634f..b0a97127210 100644
--- a/profiles/foot.lua
+++ b/profiles/foot.lua
@@ -14,7 +14,7 @@ function setup()
       weight_name                   = 'duration',
       max_speed_for_map_matching    = 40/3.6, -- kmph -> m/s
       call_tagless_node_function    = false,
-      traffic_light_penalty         = 2,
+      traffic_signal_penalty        = 2,
       u_turn_penalty                = 2,
       continue_straight_at_waypoint = false,
       use_turn_restrictions         = false,
@@ -252,7 +252,7 @@ function process_turn (profile, turn)
   end
 
   if turn.has_traffic_light then
-     turn.duration = profile.properties.traffic_light_penalty
+     turn.duration = profile.properties.traffic_signal_penalty
   end
   if profile.properties.weight_name == 'routability' then
       -- penalize turns from non-local access only segments onto local access only tags
diff --git a/profiles/lib/obstacles.lua b/profiles/lib/obstacles.lua
new file mode 100644
index 00000000000..a2ba2ca4aa7
--- /dev/null
+++ b/profiles/lib/obstacles.lua
@@ -0,0 +1,53 @@
+-- Assigns obstacle value to nodes as defined by
+-- include/extractor/obstacles.hpp
+
+local Obstacles = {}
+
+-- process the obstacles at the given node
+-- note: does not process barriers
+function Obstacles.process_node(profile, node)
+    local highway = node:get_value_by_key("highway")
+    if highway then
+        local type = obstacle_type[highway]
+        -- barriers already handled in car.lua
+        if type and type ~= obstacle_type.barrier then
+            local direction = node:get_value_by_key("direction")
+            local weight = 0
+            local duration = 0
+            local minor = false
+
+            if type == obstacle_type.traffic_signals then
+                -- traffic_signals:direction trumps direction
+                direction = node:get_value_by_key("traffic_signals:direction") or direction
+                -- traffic_signal_penalty is deprecated
+                -- but there's still unit_tests using it
+                duration = profile.properties.traffic_signal_penalty or 2
+            end
+            if type == obstacle_type.stop then
+                if node:get_value_by_key("stop") == "minor" then
+                    type = obstacle_type.stop_minor
+                end
+                duration = 2
+            end
+            if type == obstacle_type.give_way then
+                duration = 1
+            end
+            obstacle_map:add(node, Obstacle.new(type, obstacle_direction[direction] or obstacle_direction.none, duration, weight))
+        end
+    end
+end
+
+-- return true if the source road of this turn is a minor road at the intersection
+function Obstacles.entering_by_minor_road(turn)
+    -- implementation: comparing road speeds (anybody has a better idea?)
+    local max_speed = turn.target_speed
+    for _, turn_leg in pairs(turn.roads_on_the_right) do
+        max_speed = math.max(max_speed, turn_leg.speed)
+    end
+    for _, turn_leg in pairs(turn.roads_on_the_left) do
+        max_speed = math.max(max_speed, turn_leg.speed)
+    end
+    return max_speed > turn.source_speed
+end
+
+return Obstacles
diff --git a/profiles/testbot.lua b/profiles/testbot.lua
index a796be21ff5..aabc028a46c 100644
--- a/profiles/testbot.lua
+++ b/profiles/testbot.lua
@@ -15,8 +15,8 @@ function setup()
       max_speed_for_map_matching    = 30/3.6, --km -> m/s
       weight_name                   = 'duration',
       process_call_tagless_node     = false,
-      u_turn_penalty                 = 20,
-      traffic_light_penalty         = 7,     -- seconds
+      u_turn_penalty                = 20,
+      traffic_signal_penalty        = 7,     -- seconds
       use_turn_restrictions         = true
     },
 
@@ -131,7 +131,7 @@ function process_turn (profile, turn)
     turn.weight = turn.weight + profile.properties.u_turn_penalty
   end
   if turn.has_traffic_light then
-     turn.duration = turn.duration + profile.properties.traffic_light_penalty
+     turn.duration = turn.duration + profile.properties.traffic_signal_penalty
   end
 end
 
diff --git a/scripts/check_taginfo.py b/scripts/check_taginfo.py
index c71b46882e3..2e664799f40 100755
--- a/scripts/check_taginfo.py
+++ b/scripts/check_taginfo.py
@@ -19,10 +19,12 @@
 
 valid_strings = [t["key"] for t in taginfo["tags"]]
 valid_strings += [t["value"] for t in taginfo["tags"] if "value" in t]
-valid_strings += [t["value"].lower() for t in taginfo["tags"] if "value" in t] # lower is for max speed
+valid_strings += [
+    t["value"].lower() for t in taginfo["tags"] if "value" in t
+]  # lower is for max speed
 valid_strings = set(valid_strings)
 
-string_regxp = re.compile("\"([\d\w\_:]+)\"")
+string_regxp = re.compile(r"\"([\d\w\_:]+)\"")
 
 profile = None
 with open(profile_path) as f:
@@ -40,7 +42,7 @@
     errors = []
     for token in tokens:
         if token not in WHITELIST and token not in valid_strings:
-            idx = line.find("\""+token+"\"")
+            idx = line.find('"' + token + '"')
             errors.append((idx, token))
     errors = sorted(errors)
     n_errors += len(errors)
@@ -49,7 +51,7 @@
         offset = len(prefix)
         for idx, token in errors:
             sys.stdout.write(prefix + line)
-            marker = " "*(idx+offset) + "~"*(len(token)+2)
+            marker = " " * (idx + offset) + "~" * (len(token) + 2)
             print(marker)
 
 if n_errors > 0:
diff --git a/src/extractor/edge_based_graph_factory.cpp b/src/extractor/edge_based_graph_factory.cpp
index b5ab53e25c7..2a447a2741f 100644
--- a/src/extractor/edge_based_graph_factory.cpp
+++ b/src/extractor/edge_based_graph_factory.cpp
@@ -39,16 +39,13 @@ EdgeBasedGraphFactory::EdgeBasedGraphFactory(
     const util::NodeBasedDynamicGraph &node_based_graph,
     EdgeBasedNodeDataContainer &node_data_container,
     const CompressedEdgeContainer &compressed_edge_container,
-    const std::unordered_set<NodeID> &barrier_nodes,
-    const TrafficSignals &traffic_signals,
     const std::vector<util::Coordinate> &coordinates,
     const NameTable &name_table,
     const std::unordered_set<EdgeID> &segregated_edges,
     const extractor::LaneDescriptionMap &lane_description_map)
     : m_edge_based_node_container(node_data_container), m_connectivity_checksum(0),
       m_number_of_edge_based_nodes(0), m_coordinates(coordinates),
-      m_node_based_graph(node_based_graph), m_barrier_nodes(barrier_nodes),
-      m_traffic_signals(traffic_signals), m_compressed_edge_container(compressed_edge_container),
+      m_node_based_graph(node_based_graph), m_compressed_edge_container(compressed_edge_container),
       name_table(name_table), segregated_edges(segregated_edges),
       lane_description_map(lane_description_map)
 {
@@ -492,7 +489,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
                                                               m_coordinates,
                                                               m_compressed_edge_container,
                                                               unconditional_node_restriction_map,
-                                                              m_barrier_nodes,
+                                                              scripting_environment.m_obstacle_map,
                                                               turn_lanes_data,
                                                               name_table,
                                                               street_name_suffix_table);
@@ -585,76 +582,88 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
                 }
             });
 
+        // connected_roads.begin()
+        // turn
         // Generate edges for either artificial nodes or the main graph
-        const auto generate_edge = [this, &scripting_environment, weight_multiplier](
-                                       // what nodes will be used? In most cases this will be the id
-                                       // stored in the edge_data. In case of duplicated nodes (e.g.
-                                       // due to via-way restrictions), one/both of these might
-                                       // refer to a newly added edge based node
-                                       const auto edge_based_node_from,
-                                       const auto edge_based_node_to,
-                                       // the situation of the turn
-                                       const auto node_along_road_entering,
-                                       const auto node_based_edge_from,
-                                       const auto intersection_node,
-                                       const auto node_based_edge_to,
-                                       const auto &turn_angle,
-                                       const auto &road_legs_on_the_right,
-                                       const auto &road_legs_on_the_left,
-                                       const auto &edge_geometries)
+        const auto generate_edge =
+            [this, &scripting_environment, weight_multiplier](
+                // what nodes will be used? In most cases this will be the id
+                // stored in the edge_data. In case of duplicated nodes (e.g.
+                // due to via-way restrictions), one/both of these might
+                // refer to a newly added edge based node
+                const NodeID edge_based_node_from,
+                const NodeID edge_based_node_to,
+                // the situation of the turn
+                const NodeID node_along_road_entering,
+                const EdgeID node_based_edge_from,
+                const NodeID intersection_node,
+                const EdgeID node_based_edge_to,
+                const double &turn_angle,
+                const ExtractionTurnLeg &incoming_road_leg,
+                const ExtractionTurnLeg &outgoing_road_leg,
+                const std::vector<ExtractionTurnLeg> &road_legs_on_the_right,
+                const std::vector<ExtractionTurnLeg> &road_legs_on_the_left,
+                const intersection::IntersectionEdgeGeometries &edge_geometries) -> EdgeWithData
         {
             const auto &edge_data1 = m_node_based_graph.GetEdgeData(node_based_edge_from);
             const auto &edge_data2 = m_node_based_graph.GetEdgeData(node_based_edge_to);
 
+            const NodeID node_along_road_exiting = m_node_based_graph.GetTarget(node_based_edge_to);
+
             BOOST_ASSERT(nbe_to_ebn_mapping[node_based_edge_from] !=
                          nbe_to_ebn_mapping[node_based_edge_to]);
             BOOST_ASSERT(!edge_data1.reversed);
             BOOST_ASSERT(!edge_data2.reversed);
 
             // compute weight and duration penalties
-            // In theory we shouldn't get a directed traffic light on a turn, as it indicates that
-            // the traffic signal direction was potentially ambiguously annotated on the junction
-            // node But we'll check anyway.
-            const auto is_traffic_light =
-                m_traffic_signals.HasSignal(node_along_road_entering, intersection_node);
-            const auto is_uturn =
+            // In theory we shouldn't get a directed obstacle on a turn, as it indicates that
+            // the obstacle direction was potentially ambiguously annotated on the junction
+            // node. But we'll check anyway.
+            const bool is_traffic_light = scripting_environment.m_obstacle_map.any(
+                node_along_road_entering, intersection_node, Obstacle::Type::TrafficSignals);
+            const bool is_uturn =
                 guidance::getTurnDirection(turn_angle) == guidance::DirectionModifier::UTurn;
 
-            ExtractionTurn extracted_turn(
+            ExtractionTurn extracted_turn{
                 // general info
                 turn_angle,
-                road_legs_on_the_right.size() + road_legs_on_the_left.size() + 2 - is_uturn,
+                static_cast<int>(road_legs_on_the_right.size() + road_legs_on_the_left.size() + 2 -
+                                 is_uturn),
                 is_uturn,
                 is_traffic_light,
                 m_edge_based_node_container.GetAnnotation(edge_data1.annotation_data)
                     .is_left_hand_driving,
                 // source info
-                edge_data1.flags.restricted,
+                edge_data1.flags.IsRestricted(),
                 m_edge_based_node_container.GetAnnotation(edge_data1.annotation_data).travel_mode,
                 edge_data1.flags.road_classification.IsMotorwayClass(),
                 edge_data1.flags.road_classification.IsLinkClass(),
                 edge_data1.flags.road_classification.GetNumberOfLanes(),
                 edge_data1.flags.highway_turn_classification,
                 edge_data1.flags.access_turn_classification,
-                ((double)intersection::findEdgeLength(edge_geometries, node_based_edge_from) /
-                 from_alias<double>(edge_data1.duration)) *
-                    36,
+                static_cast<int>(
+                    intersection::findEdgeLength(edge_geometries, node_based_edge_from) /
+                    from_alias<double>(edge_data1.duration) * 36.0),
                 edge_data1.flags.road_classification.GetPriority(),
                 // target info
-                edge_data2.flags.restricted,
+                edge_data2.flags.IsRestricted(),
                 m_edge_based_node_container.GetAnnotation(edge_data2.annotation_data).travel_mode,
                 edge_data2.flags.road_classification.IsMotorwayClass(),
                 edge_data2.flags.road_classification.IsLinkClass(),
                 edge_data2.flags.road_classification.GetNumberOfLanes(),
                 edge_data2.flags.highway_turn_classification,
                 edge_data2.flags.access_turn_classification,
-                ((double)intersection::findEdgeLength(edge_geometries, node_based_edge_to) /
-                 from_alias<double>(edge_data2.duration)) *
-                    36,
+                static_cast<int>(intersection::findEdgeLength(edge_geometries, node_based_edge_to) /
+                                 from_alias<double>(edge_data2.duration) * 36.0),
                 edge_data2.flags.road_classification.GetPriority(),
                 // connected roads
+                incoming_road_leg,
+                outgoing_road_leg,
                 road_legs_on_the_right,
-                road_legs_on_the_left);
+                road_legs_on_the_left,
+                node_along_road_entering,
+                intersection_node,
+                node_along_road_exiting};
 
             scripting_environment.ProcessTurn(extracted_turn);
 
@@ -718,8 +727,8 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
                 auto buffer = std::make_shared<EdgesPipelineBuffer>();
                 buffer->nodes_processed = intersection_node_range.size();
 
-                for (auto intersection_node = intersection_node_range.begin(),
-                          end = intersection_node_range.end();
+                for (NodeID intersection_node = intersection_node_range.begin(),
+                            end = intersection_node_range.end();
                      intersection_node < end;
                      ++intersection_node)
                 {
@@ -761,32 +770,32 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
                     // an outgoing edge. Therefore, we have to search all connected edges for edges
                     // entering `b`
 
-                    for (const auto &incoming_edge : incoming_edges)
+                    for (const intersection::IntersectionEdge &incoming_edge : incoming_edges)
                     {
                         ++node_based_edge_counter;
 
-                        const auto connected_roads =
-                            extractor::intersection::getConnectedRoadsForEdgeGeometries(
+                        const intersection::IntersectionView connected_roads =
+                            intersection::getConnectedRoadsForEdgeGeometries(
                                 m_node_based_graph,
                                 m_edge_based_node_container,
                                 unconditional_node_restriction_map,
-                                m_barrier_nodes,
+                                scripting_environment.m_obstacle_map,
                                 turn_lanes_data,
                                 incoming_edge,
                                 edge_geometries,
                                 merged_edge_ids);
 
                         // check if this edge is part of a restriction via-way
-                        const auto is_restriction_via_edge =
+                        const bool is_restriction_via_edge =
                             way_restriction_map.IsViaWayEdge(incoming_edge.node, intersection_node);
 
-                        for (const auto &outgoing_edge : outgoing_edges)
+                        for (const intersection::IntersectionEdge &outgoing_edge : outgoing_edges)
                         {
-                            auto is_turn_allowed =
+                            const bool is_turn_allowed =
                                 intersection::isTurnAllowed(m_node_based_graph,
                                                             m_edge_based_node_container,
                                                             unconditional_node_restriction_map,
-                                                            m_barrier_nodes,
+                                                            scripting_environment.m_obstacle_map,
                                                             edge_geometries,
                                                             turn_lanes_data,
                                                             incoming_edge,
@@ -804,10 +813,8 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
                             OSRM_ASSERT(turn != connected_roads.end(),
                                         m_coordinates[intersection_node]);
 
-                            std::vector<ExtractionTurnLeg> road_legs_on_the_right;
-                            std::vector<ExtractionTurnLeg> road_legs_on_the_left;
-
-                            auto get_connected_road_info = [&](const auto &connected_edge)
+                            auto get_connected_road_info =
+                                [&](const auto &connected_edge) -> ExtractionTurnLeg
                             {
                                 const auto &edge_data =
                                     m_node_based_graph.GetEdgeData(connected_edge.eid);
@@ -841,23 +848,30 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
 
                                     is_outgoing = true;
                                 }
+                                double distance = intersection::findEdgeLength(edge_geometries,
+                                                                               connected_edge.eid);
 
-                                return ExtractionTurnLeg(
-                                    edge_data.flags.restricted,
+                                return ExtractionTurnLeg{
+                                    edge_data.flags.IsRestricted(),
                                     edge_data.flags.road_classification.IsMotorwayClass(),
                                     edge_data.flags.road_classification.IsLinkClass(),
                                     edge_data.flags.road_classification.GetNumberOfLanes(),
                                     edge_data.flags.highway_turn_classification,
                                     edge_data.flags.access_turn_classification,
-                                    ((double)intersection::findEdgeLength(edge_geometries,
-                                                                          connected_edge.eid) /
-                                     from_alias<double>(edge_data.duration)) *
-                                        36,
+                                    static_cast<int>(36.0 * distance /
+                                                     from_alias<double>(edge_data.duration)),
+                                    distance,
                                     edge_data.flags.road_classification.GetPriority(),
                                     is_incoming,
-                                    is_outgoing);
+                                    is_outgoing};
                             };
 
+                            ExtractionTurnLeg incoming_road_leg =
+                                get_connected_road_info(connected_roads[0]);
+                            ExtractionTurnLeg outgoing_road_leg = get_connected_road_info(*turn);
+                            std::vector<ExtractionTurnLeg> road_legs_on_the_right;
+                            std::vector<ExtractionTurnLeg> road_legs_on_the_left;
+
                             // all connected roads on the right of a u turn
                             const auto is_uturn = guidance::getTurnDirection(turn->angle) ==
                                                   guidance::DirectionModifier::UTurn;
@@ -959,6 +973,8 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
                                                   outgoing_edge.node,
                                                   outgoing_edge.edge,
                                                   turn->angle,
+                                                  incoming_road_leg,
+                                                  outgoing_road_leg,
                                                   road_legs_on_the_right,
                                                   road_legs_on_the_left,
                                                   edge_geometries);
@@ -1039,6 +1055,8 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
                                                                             outgoing_edge.node,
                                                                             outgoing_edge.edge,
                                                                             turn->angle,
+                                                                            incoming_road_leg,
+                                                                            outgoing_road_leg,
                                                                             road_legs_on_the_right,
                                                                             road_legs_on_the_left,
                                                                             edge_geometries);
@@ -1090,6 +1108,8 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
                                                                             outgoing_edge.node,
                                                                             outgoing_edge.edge,
                                                                             turn->angle,
+                                                                            incoming_road_leg,
+                                                                            outgoing_road_leg,
                                                                             road_legs_on_the_right,
                                                                             road_legs_on_the_left,
                                                                             edge_geometries);
diff --git a/src/extractor/extraction_containers.cpp b/src/extractor/extraction_containers.cpp
index 9445c7c252b..879bb1bebea 100644
--- a/src/extractor/extraction_containers.cpp
+++ b/src/extractor/extraction_containers.cpp
@@ -395,7 +395,9 @@ ExtractionContainers::ExtractionContainers()
 
 /**
  * Processes the collected data and serializes it.
- * At this point nodes are still referenced by their OSM id.
+ *
+ * In this function the nodes will eventually be converted from their 64bit OSM id to the
+ * internal 32bit OSRM id.
  *
  * - Identify nodes of ways used in restrictions and maneuver overrides
  * - Filter nodes list to nodes that are referenced by ways
@@ -408,12 +410,12 @@ void ExtractionContainers::PrepareData(ScriptingEnvironment &scripting_environme
 {
     const auto restriction_ways = IdentifyRestrictionWays();
     const auto maneuver_override_ways = IdentifyManeuverOverrideWays();
-    const auto traffic_signals = IdentifyTrafficSignals();
+    scripting_environment.m_obstacle_map.preProcess(used_node_id_list, way_node_id_offsets);
 
     PrepareNodes();
     PrepareEdges(scripting_environment);
+    scripting_environment.m_obstacle_map.fixupNodes(used_node_id_list);
 
-    PrepareTrafficSignals(traffic_signals);
     PrepareManeuverOverrides(maneuver_override_ways);
     PrepareRestrictions(restriction_ways);
     WriteCharData(name_file_name);
@@ -545,22 +547,6 @@ void ExtractionContainers::PrepareNodes()
         log << "ok, after " << TIMER_SEC(write_nodes) << "s";
     }
 
-    {
-        util::UnbufferedLog log;
-        log << "Writing barrier nodes     ... ";
-        TIMER_START(write_nodes);
-        for (const auto osm_id : barrier_nodes)
-        {
-            const auto node_id = mapExternalToInternalNodeID(
-                used_node_id_list.begin(), used_node_id_list.end(), osm_id);
-            if (node_id != SPECIAL_NODEID)
-            {
-                used_barrier_nodes.emplace(node_id);
-            }
-        }
-        log << "ok, after " << TIMER_SEC(write_nodes) << "s";
-    }
-
     util::Log() << "Processed " << max_internal_node_id << " nodes";
 }
 
@@ -940,49 +926,6 @@ ExtractionContainers::ReferencedWays ExtractionContainers::IdentifyManeuverOverr
     return maneuver_override_ways;
 }
 
-void ExtractionContainers::PrepareTrafficSignals(
-    const ExtractionContainers::ReferencedTrafficSignals &referenced_traffic_signals)
-{
-    const auto &bidirectional_signal_nodes = referenced_traffic_signals.first;
-    const auto &unidirectional_signal_segments = referenced_traffic_signals.second;
-
-    util::UnbufferedLog log;
-    log << "Preparing traffic light signals for " << bidirectional_signal_nodes.size()
-        << " bidirectional, " << unidirectional_signal_segments.size()
-        << " unidirectional nodes ...";
-    TIMER_START(prepare_traffic_signals);
-
-    std::unordered_set<NodeID> bidirectional;
-    std::unordered_set<std::pair<NodeID, NodeID>> unidirectional;
-
-    for (const auto &osm_node : bidirectional_signal_nodes)
-    {
-        const auto node_id = mapExternalToInternalNodeID(
-            used_node_id_list.begin(), used_node_id_list.end(), osm_node);
-        if (node_id != SPECIAL_NODEID)
-        {
-            bidirectional.insert(node_id);
-        }
-    }
-    for (const auto &to_from : unidirectional_signal_segments)
-    {
-        const auto to_node_id = mapExternalToInternalNodeID(
-            used_node_id_list.begin(), used_node_id_list.end(), to_from.first);
-        const auto from_node_id = mapExternalToInternalNodeID(
-            used_node_id_list.begin(), used_node_id_list.end(), to_from.second);
-        if (from_node_id != SPECIAL_NODEID && to_node_id != SPECIAL_NODEID)
-        {
-            unidirectional.insert({from_node_id, to_node_id});
-        }
-    }
-
-    internal_traffic_signals.bidirectional_nodes = std::move(bidirectional);
-    internal_traffic_signals.unidirectional_segments = std::move(unidirectional);
-
-    TIMER_STOP(prepare_traffic_signals);
-    log << "ok, after " << TIMER_SEC(prepare_traffic_signals) << "s";
-}
-
 void ExtractionContainers::PrepareManeuverOverrides(const ReferencedWays &maneuver_override_ways)
 {
     auto const osm_node_to_internal_nbn = [&](auto const osm_node)
@@ -1169,97 +1112,6 @@ ExtractionContainers::ReferencedWays ExtractionContainers::IdentifyRestrictionWa
     return restriction_ways;
 }
 
-ExtractionContainers::ReferencedTrafficSignals ExtractionContainers::IdentifyTrafficSignals()
-{
-    util::UnbufferedLog log;
-    log << "Collecting traffic signal information on " << external_traffic_signals.size()
-        << " signals...";
-    TIMER_START(identify_traffic_signals);
-
-    // Temporary store for nodes containing a unidirectional signal.
-    std::unordered_map<OSMNodeID, TrafficLightClass::Direction> unidirectional_signals;
-
-    // For each node that has a unidirectional traffic signal, we store the node(s)
-    // that lead up to the signal.
-    std::unordered_multimap<OSMNodeID, OSMNodeID> signal_segments;
-
-    std::unordered_set<OSMNodeID> bidirectional_signals;
-
-    const auto mark_signals = [&](auto const &traffic_signal)
-    {
-        if (traffic_signal.second == TrafficLightClass::DIRECTION_FORWARD ||
-            traffic_signal.second == TrafficLightClass::DIRECTION_REVERSE)
-        {
-            unidirectional_signals.insert({traffic_signal.first, traffic_signal.second});
-        }
-        else
-        {
-            BOOST_ASSERT(traffic_signal.second == TrafficLightClass::DIRECTION_ALL);
-            bidirectional_signals.insert(traffic_signal.first);
-        }
-    };
-    std::for_each(external_traffic_signals.begin(), external_traffic_signals.end(), mark_signals);
-
-    // Extract all the segments that lead up to unidirectional traffic signals.
-    const auto set_segments = [&](const size_t way_list_idx, auto const & /*unused*/)
-    {
-        const auto node_start_offset =
-            used_node_id_list.begin() + way_node_id_offsets[way_list_idx];
-        const auto node_end_offset =
-            used_node_id_list.begin() + way_node_id_offsets[way_list_idx + 1];
-
-        for (auto node_it = node_start_offset; node_it < node_end_offset; node_it++)
-        {
-            const auto sig = unidirectional_signals.find(*node_it);
-            if (sig != unidirectional_signals.end())
-            {
-                if (sig->second == TrafficLightClass::DIRECTION_FORWARD)
-                {
-                    if (node_it != node_start_offset)
-                    {
-                        // Previous node leads to signal
-                        signal_segments.insert({*node_it, *(node_it - 1)});
-                    }
-                }
-                else
-                {
-                    BOOST_ASSERT(sig->second == TrafficLightClass::DIRECTION_REVERSE);
-                    if (node_it + 1 != node_end_offset)
-                    {
-                        // Next node leads to signal
-                        signal_segments.insert({*node_it, *(node_it + 1)});
-                    }
-                }
-            }
-        }
-    };
-    util::for_each_indexed(ways_list.cbegin(), ways_list.cend(), set_segments);
-
-    util::for_each_pair(
-        signal_segments,
-        [](const auto pair_a, const auto pair_b)
-        {
-            if (pair_a.first == pair_b.first)
-            {
-                // If a node is appearing multiple times in this map, then it's ambiguous.
-                // The node is an intersection and the traffic direction is being use for multiple
-                // ways. We can't be certain of the original intent. See:
-                // https://wiki.openstreetmap.org/wiki/Key:traffic_signals:direction
-
-                // OSRM will include the signal for all intersecting ways in the specified
-                // direction, but let's flag this as a concern.
-                util::Log(logWARNING)
-                    << "OSM node " << pair_a.first
-                    << " has a unidirectional traffic signal ambiguously applied to multiple ways";
-            }
-        });
-
-    TIMER_STOP(identify_traffic_signals);
-    log << "ok, after " << TIMER_SEC(identify_traffic_signals) << "s";
-
-    return {std::move(bidirectional_signals), std::move(signal_segments)};
-}
-
 void ExtractionContainers::PrepareRestrictions(const ReferencedWays &restriction_ways)
 {
 
diff --git a/src/extractor/extractor.cpp b/src/extractor/extractor.cpp
index 80b327e7179..389d22bcae4 100644
--- a/src/extractor/extractor.cpp
+++ b/src/extractor/extractor.cpp
@@ -176,7 +176,7 @@ std::vector<CompressedNodeBasedGraphEdge> toEdgeList(const util::NodeBasedDynami
  * That includes:
  *  - extracting turn restrictions
  *  - splitting ways into (directional!) edge segments
- *  - checking if nodes are barriers or traffic signal
+ *  - checking if nodes are obstacles, that must be kept
  *  - discarding all tag information: All relevant type information for nodes/ways
  *    is extracted at this point.
  *
@@ -218,8 +218,6 @@ int Extractor::run(ScriptingEnvironment &scripting_environment)
     NodeBasedGraphFactory node_based_graph_factory(scripting_environment,
                                                    parsed_osm_data.turn_restrictions,
                                                    parsed_osm_data.unresolved_maneuver_overrides,
-                                                   parsed_osm_data.traffic_signals,
-                                                   std::move(parsed_osm_data.barriers),
                                                    std::move(parsed_osm_data.osm_coordinates),
                                                    std::move(parsed_osm_data.osm_node_ids),
                                                    parsed_osm_data.edge_list,
@@ -257,7 +255,6 @@ int Extractor::run(ScriptingEnvironment &scripting_environment)
 
     node_based_graph_factory.GetCompressedEdges().PrintStatistics();
 
-    const auto &barrier_nodes = node_based_graph_factory.GetBarriers();
     // stealing the annotation data from the node-based graph
     edge_based_nodes_container =
         EdgeBasedNodeDataContainer({}, std::move(node_based_graph_factory.GetAnnotationData()));
@@ -274,8 +271,6 @@ int Extractor::run(ScriptingEnvironment &scripting_environment)
         BuildEdgeExpandedGraph(node_based_graph,
                                coordinates,
                                node_based_graph_factory.GetCompressedEdges(),
-                               barrier_nodes,
-                               parsed_osm_data.traffic_signals,
                                restriction_graph,
                                segregated_edges,
                                name_table,
@@ -294,7 +289,6 @@ int Extractor::run(ScriptingEnvironment &scripting_environment)
                          edge_based_nodes_container,
                          coordinates,
                          node_based_graph_factory.GetCompressedEdges(),
-                         barrier_nodes,
                          restriction_graph,
                          name_table,
                          std::move(parsed_osm_data.turn_lane_map),
@@ -505,6 +499,7 @@ Extractor::ParsedOSMData Extractor::ParseOSMData(ScriptingEnvironment &scripting
             {
                 extractor_callbacks->ProcessNode(result.first, result.second);
             }
+
             number_of_ways += parsed_buffer.resulting_ways.size();
             for (const auto &result : parsed_buffer.resulting_ways)
             {
@@ -651,8 +646,6 @@ Extractor::ParsedOSMData Extractor::ParseOSMData(ScriptingEnvironment &scripting
     return ParsedOSMData{std::move(turn_lane_map),
                          std::move(extraction_containers.turn_restrictions),
                          std::move(extraction_containers.internal_maneuver_overrides),
-                         std::move(extraction_containers.internal_traffic_signals),
-                         std::move(extraction_containers.used_barrier_nodes),
                          std::move(osm_coordinates),
                          std::move(osm_node_ids),
                          std::move(extraction_containers.used_edges),
@@ -726,8 +719,6 @@ EdgeID Extractor::BuildEdgeExpandedGraph(
     const util::NodeBasedDynamicGraph &node_based_graph,
     const std::vector<util::Coordinate> &coordinates,
     const CompressedEdgeContainer &compressed_edge_container,
-    const std::unordered_set<NodeID> &barrier_nodes,
-    const TrafficSignals &traffic_signals,
     const RestrictionGraph &restriction_graph,
     const std::unordered_set<EdgeID> &segregated_edges,
     const NameTable &name_table,
@@ -747,8 +738,6 @@ EdgeID Extractor::BuildEdgeExpandedGraph(
     EdgeBasedGraphFactory edge_based_graph_factory(node_based_graph,
                                                    edge_based_nodes_container,
                                                    compressed_edge_container,
-                                                   barrier_nodes,
-                                                   traffic_signals,
                                                    coordinates,
                                                    name_table,
                                                    segregated_edges,
@@ -836,7 +825,6 @@ void Extractor::ProcessGuidanceTurns(
     const extractor::EdgeBasedNodeDataContainer &edge_based_node_container,
     const std::vector<util::Coordinate> &node_coordinates,
     const CompressedEdgeContainer &compressed_edge_container,
-    const std::unordered_set<NodeID> &barrier_nodes,
     const RestrictionGraph &restriction_graph,
     const NameTable &name_table,
     LaneDescriptionMap lane_description_map,
@@ -863,7 +851,7 @@ void Extractor::ProcessGuidanceTurns(
                                       edge_based_node_container,
                                       node_coordinates,
                                       compressed_edge_container,
-                                      barrier_nodes,
+                                      scripting_environment.m_obstacle_map,
                                       unconditional_node_restriction_map,
                                       way_restriction_map,
                                       name_table,
diff --git a/src/extractor/extractor_callbacks.cpp b/src/extractor/extractor_callbacks.cpp
index 5e47f15e3c6..b89ce671040 100644
--- a/src/extractor/extractor_callbacks.cpp
+++ b/src/extractor/extractor_callbacks.cpp
@@ -41,10 +41,9 @@ ExtractorCallbacks::ExtractorCallbacks(ExtractionContainers &extraction_containe
                                        std::unordered_map<std::string, ClassData> &classes_map,
                                        LaneDescriptionMap &lane_description_map,
                                        const ProfileProperties &properties)
-    : external_memory(extraction_containers_), classes_map(classes_map),
-      lane_description_map(lane_description_map),
+    : classes_map(classes_map), lane_description_map(lane_description_map),
       fallback_to_duration(properties.fallback_to_duration),
-      force_split_edges(properties.force_split_edges)
+      force_split_edges(properties.force_split_edges), external_memory(extraction_containers_)
 {
     // we reserved 0, 1, 2, 3, 4 for the empty case
     string_map[MapKey("", "", "", "", "")] = 0;
@@ -57,24 +56,13 @@ ExtractorCallbacks::ExtractorCallbacks(ExtractionContainers &extraction_containe
  *
  * warning: caller needs to take care of synchronization!
  */
-void ExtractorCallbacks::ProcessNode(const osmium::Node &input_node,
-                                     const ExtractionNode &result_node)
+void ExtractorCallbacks::ProcessNode(const osmium::Node &input_node, const ExtractionNode &)
 {
-    const auto id = OSMNodeID{static_cast<std::uint64_t>(input_node.id())};
-
+    const auto id = to_alias<OSMNodeID>(input_node.id());
     external_memory.all_nodes_list.push_back(
         QueryNode{util::toFixed(util::UnsafeFloatLongitude{input_node.location().lon()}),
                   util::toFixed(util::UnsafeFloatLatitude{input_node.location().lat()}),
                   id});
-
-    if (result_node.barrier)
-    {
-        external_memory.barrier_nodes.push_back(id);
-    }
-    if (result_node.traffic_lights != TrafficLightClass::NONE)
-    {
-        external_memory.external_traffic_signals.push_back({id, result_node.traffic_lights});
-    }
 }
 
 void ExtractorCallbacks::ProcessRestriction(const InputTurnRestriction &restriction)
diff --git a/src/extractor/graph_compressor.cpp b/src/extractor/graph_compressor.cpp
index 931668f2676..b410b4ab3e2 100644
--- a/src/extractor/graph_compressor.cpp
+++ b/src/extractor/graph_compressor.cpp
@@ -19,9 +19,7 @@ namespace osrm::extractor
 
 static constexpr int SECOND_TO_DECISECOND = 10;
 
-void GraphCompressor::Compress(const std::unordered_set<NodeID> &barrier_nodes,
-                               TrafficSignals &traffic_signals,
-                               ScriptingEnvironment &scripting_environment,
+void GraphCompressor::Compress(ScriptingEnvironment &scripting_environment,
                                std::vector<TurnRestriction> &turn_restrictions,
                                std::vector<UnresolvedManeuverOverride> &maneuver_overrides,
                                util::NodeBasedDynamicGraph &graph,
@@ -30,6 +28,7 @@ void GraphCompressor::Compress(const std::unordered_set<NodeID> &barrier_nodes,
 {
     const unsigned original_number_of_nodes = graph.GetNumberOfNodes();
     const unsigned original_number_of_edges = graph.GetNumberOfEdges();
+    const std::vector<ExtractionTurnLeg> no_other_roads;
 
     TurnPathCompressor turn_path_compressor(turn_restrictions, maneuver_overrides);
 
@@ -76,8 +75,9 @@ void GraphCompressor::Compress(const std::unordered_set<NodeID> &barrier_nodes,
                 continue;
             }
 
-            // don't contract barrier node
-            if (barrier_nodes.end() != barrier_nodes.find(node_v))
+            // don't compress nodes with these obstacle types
+            if (scripting_environment.m_obstacle_map.any(
+                    SPECIAL_NODEID, node_v, Obstacle::Type::Incompressible))
             {
                 continue;
             }
@@ -160,8 +160,7 @@ void GraphCompressor::Compress(const std::unordered_set<NodeID> &barrier_nodes,
                                graph.GetEdgeData(reverse_e1).reversed));
                 /*
                  * Remember Lane Data for compressed parts. This handles scenarios where lane-data
-                 * is
-                 * only kept up until a traffic light.
+                 * is only kept up until a traffic light.
                  *
                  *                |    |
                  * ----------------    |
@@ -173,17 +172,14 @@ void GraphCompressor::Compress(const std::unordered_set<NodeID> &barrier_nodes,
                  *
                  *  u ------- v ---- w
                  *
-                 * Since the edge is compressable, we can transfer:
-                 * "left|right" (uv) and "" (uw) into a string with "left|right" (uw) for the
-                 * compressed
-                 * edge.
-                 * Doing so, we might mess up the point from where the lanes are shown. It should be
-                 * reasonable, since the announcements have to come early anyhow. So there is a
-                 * potential danger in here, but it saves us from adding a lot of additional edges
-                 * for
-                 * turn-lanes. Without this, we would have to treat any turn-lane beginning/ending
-                 * just
-                 * like a barrier.
+                 * Since the edge is compressible, we can transfer: "left|right" (uv)
+                 * and "" (uw) into a string with "left|right" (uw) for the compressed
+                 * edge.  Doing so, we might mess up the point from where the lanes are
+                 * shown. It should be reasonable, since the announcements have to come
+                 * early anyhow. So there is a potential danger in here, but it saves us
+                 * from adding a lot of additional edges for turn-lanes. Without this,
+                 * we would have to treat any turn-lane beginning or ending just like an
+                 * obstacle.
                  */
                 const auto selectAnnotation =
                     [&node_data_container](const AnnotationID front_annotation,
@@ -208,70 +204,9 @@ void GraphCompressor::Compress(const std::unordered_set<NodeID> &barrier_nodes,
                 graph.GetEdgeData(reverse_e2).annotation_data = selectAnnotation(
                     rev_edge_data2.annotation_data, rev_edge_data1.annotation_data);
 
-                // Add node penalty when compress edge crosses a traffic signal
-                const bool has_forward_signal = traffic_signals.HasSignal(node_u, node_v);
-                const bool has_reverse_signal = traffic_signals.HasSignal(node_w, node_v);
-
-                EdgeDuration forward_node_duration_penalty = MAXIMAL_EDGE_DURATION;
-                EdgeWeight forward_node_weight_penalty = INVALID_EDGE_WEIGHT;
-                EdgeDuration reverse_node_duration_penalty = MAXIMAL_EDGE_DURATION;
-                EdgeWeight reverse_node_weight_penalty = INVALID_EDGE_WEIGHT;
-                if (has_forward_signal || has_reverse_signal)
-                {
-                    // we cannot handle this as node penalty, if it depends on turn direction
-                    if (fwd_edge_data1.flags.restricted != fwd_edge_data2.flags.restricted)
-                        continue;
-
-                    // generate an artificial turn for the turn penalty generation
-                    std::vector<ExtractionTurnLeg> roads_on_the_right;
-                    std::vector<ExtractionTurnLeg> roads_on_the_left;
-                    ExtractionTurn extraction_turn(0,
-                                                   2,
-                                                   false,
-                                                   true,
-                                                   false,
-                                                   false,
-                                                   TRAVEL_MODE_DRIVING,
-                                                   false,
-                                                   false,
-                                                   1,
-                                                   0,
-                                                   0,
-                                                   0,
-                                                   0,
-                                                   false,
-                                                   TRAVEL_MODE_DRIVING,
-                                                   false,
-                                                   false,
-                                                   1,
-                                                   0,
-                                                   0,
-                                                   0,
-                                                   0,
-                                                   roads_on_the_right,
-                                                   roads_on_the_left);
-                    scripting_environment.ProcessTurn(extraction_turn);
-
-                    auto update_direction_penalty =
-                        [&extraction_turn, weight_multiplier](
-                            bool signal, EdgeDuration &duration_penalty, EdgeWeight &weight_penalty)
-                    {
-                        if (signal)
-                        {
-                            duration_penalty = to_alias<EdgeDuration>(extraction_turn.duration *
-                                                                      SECOND_TO_DECISECOND);
-                            weight_penalty =
-                                to_alias<EdgeWeight>(extraction_turn.weight * weight_multiplier);
-                        }
-                    };
-
-                    update_direction_penalty(has_forward_signal,
-                                             forward_node_duration_penalty,
-                                             forward_node_weight_penalty);
-                    update_direction_penalty(has_reverse_signal,
-                                             reverse_node_duration_penalty,
-                                             reverse_node_weight_penalty);
-                }
+                // we cannot handle this as node penalty, if it depends on turn direction
+                if (fwd_edge_data1.flags.restricted != fwd_edge_data2.flags.restricted)
+                    continue;
 
                 // Get weights before graph is modified
                 const auto forward_weight1 = fwd_edge_data1.weight;
@@ -302,33 +237,60 @@ void GraphCompressor::Compress(const std::unordered_set<NodeID> &barrier_nodes,
                 BOOST_ASSERT(EdgeWeight{0} != reverse_weight1);
                 BOOST_ASSERT(EdgeWeight{0} != reverse_weight2);
 
-                auto apply_e2_to_e1 = [&graph](EdgeID edge1,
-                                               EdgeID edge2,
-                                               EdgeWeight &weight_penalty,
-                                               EdgeDuration &duration_penalty)
+                struct EdgePenalties
                 {
-                    auto &edge1_data = graph.GetEdgeData(edge1);
-                    const auto &edge2_data = graph.GetEdgeData(edge2);
-                    edge1_data.weight += edge2_data.weight;
-                    edge1_data.duration += edge2_data.duration;
-                    edge1_data.distance += edge2_data.distance;
-                    if (weight_penalty != INVALID_EDGE_WEIGHT &&
-                        duration_penalty != MAXIMAL_EDGE_DURATION)
-                    {
-                        edge1_data.weight += weight_penalty;
-                        edge1_data.duration += duration_penalty;
-                        // Note: no penalties for distances
-                    }
+                    EdgeDuration duration;
+                    EdgeWeight weight;
+                };
+
+                auto update_edge =
+                    [](EdgeData &to, const EdgeData &from, const EdgePenalties &penalties)
+                {
+                    to.weight += from.weight;
+                    to.duration += from.duration;
+                    to.distance += from.distance;
+                    to.weight += penalties.weight;
+                    to.duration += penalties.duration;
+                };
+
+                // Add the obstacle's penalties to the edge when compressing an edge with
+                // an obstacle
+                auto get_obstacle_penalty = [&scripting_environment,
+                                             weight_multiplier,
+                                             no_other_roads](const NodeID from,
+                                                             const NodeID via,
+                                                             const NodeID to,
+                                                             const EdgeData &from_edge,
+                                                             const EdgeData &to_edge,
+                                                             EdgePenalties &penalties)
+                {
+                    // generate an artificial turn for the turn penalty generation
+                    ExtractionTurn fake_turn{
+                        from, via, to, from_edge, to_edge, no_other_roads, no_other_roads};
+                    scripting_environment.ProcessTurn(fake_turn);
+                    penalties.duration +=
+                        to_alias<EdgeDuration>(fake_turn.duration * SECOND_TO_DECISECOND);
+                    penalties.weight += to_alias<EdgeWeight>(fake_turn.weight * weight_multiplier);
                 };
 
-                apply_e2_to_e1(forward_e1,
-                               forward_e2,
-                               forward_node_weight_penalty,
-                               forward_node_duration_penalty);
-                apply_e2_to_e1(reverse_e1,
-                               reverse_e2,
-                               reverse_node_weight_penalty,
-                               reverse_node_duration_penalty);
+                auto &f1_data = graph.GetEdgeData(forward_e1);
+                auto &b1_data = graph.GetEdgeData(reverse_e1);
+                const auto &f2_data = graph.GetEdgeData(forward_e2);
+                const auto &b2_data = graph.GetEdgeData(reverse_e2);
+
+                EdgePenalties forward_penalties{{0}, {0}};
+                EdgePenalties backward_penalties{{0}, {0}};
+
+                if (scripting_environment.m_obstacle_map.any(node_v))
+                {
+                    get_obstacle_penalty(
+                        node_u, node_v, node_w, f1_data, f2_data, forward_penalties);
+                    get_obstacle_penalty(
+                        node_w, node_v, node_u, b1_data, b2_data, backward_penalties);
+                }
+
+                update_edge(f1_data, f2_data, forward_penalties);
+                update_edge(b1_data, b2_data, backward_penalties);
 
                 // extend e1's to targets of e2's
                 graph.SetTarget(forward_e1, node_w);
@@ -341,28 +303,20 @@ void GraphCompressor::Compress(const std::unordered_set<NodeID> &barrier_nodes,
                 // update any involved turn relations
                 turn_path_compressor.Compress(node_u, node_v, node_w);
 
-                // Update traffic signal paths containing compressed node.
-                traffic_signals.Compress(node_u, node_v, node_w);
+                // Update obstacle paths containing the compressed node.
+                scripting_environment.m_obstacle_map.compress(node_u, node_v, node_w);
 
-                // Forward and reversed compressed edge lengths need to match.
-                // Set a dummy empty penalty weight if opposite value exists.
-                auto set_dummy_penalty = [](EdgeWeight &weight_penalty,
-                                            EdgeDuration &duration_penalty,
-                                            EdgeWeight &other_weight_penalty)
+                // Forward and backward penalties must both be valid or both be invalid.
+                auto set_dummy_penalty = [](EdgePenalties &f, EdgePenalties &b)
                 {
-                    if (weight_penalty == INVALID_EDGE_WEIGHT &&
-                        other_weight_penalty != INVALID_EDGE_WEIGHT)
+                    if (f.weight == INVALID_EDGE_WEIGHT && b.weight != INVALID_EDGE_WEIGHT)
                     {
-                        weight_penalty = {0};
-                        duration_penalty = {0};
+                        f.weight = {0};
+                        f.duration = {0};
                     }
                 };
-                set_dummy_penalty(forward_node_weight_penalty,
-                                  forward_node_duration_penalty,
-                                  reverse_node_weight_penalty);
-                set_dummy_penalty(reverse_node_weight_penalty,
-                                  reverse_node_duration_penalty,
-                                  forward_node_weight_penalty);
+                set_dummy_penalty(forward_penalties, backward_penalties);
+                set_dummy_penalty(backward_penalties, forward_penalties);
 
                 // store compressed geometry in container
                 geometry_compressor.CompressEdge(forward_e1,
@@ -373,8 +327,8 @@ void GraphCompressor::Compress(const std::unordered_set<NodeID> &barrier_nodes,
                                                  forward_weight2,
                                                  forward_duration1,
                                                  forward_duration2,
-                                                 forward_node_weight_penalty,
-                                                 forward_node_duration_penalty);
+                                                 forward_penalties.weight,
+                                                 forward_penalties.duration);
                 geometry_compressor.CompressEdge(reverse_e1,
                                                  reverse_e2,
                                                  node_v,
@@ -383,8 +337,8 @@ void GraphCompressor::Compress(const std::unordered_set<NodeID> &barrier_nodes,
                                                  reverse_weight2,
                                                  reverse_duration1,
                                                  reverse_duration2,
-                                                 reverse_node_weight_penalty,
-                                                 reverse_node_duration_penalty);
+                                                 backward_penalties.weight,
+                                                 backward_penalties.duration);
             }
         }
     }
diff --git a/src/extractor/intersection/intersection_analysis.cpp b/src/extractor/intersection/intersection_analysis.cpp
index 2850e06235a..d8b68c644db 100644
--- a/src/extractor/intersection/intersection_analysis.cpp
+++ b/src/extractor/intersection/intersection_analysis.cpp
@@ -434,7 +434,7 @@ bool isTurnRestricted(RestrictionsRange restrictions, const NodeID to)
 bool isTurnAllowed(const util::NodeBasedDynamicGraph &graph,
                    const EdgeBasedNodeDataContainer &node_data_container,
                    const RestrictionMap &restriction_map,
-                   const std::unordered_set<NodeID> &barrier_nodes,
+                   const ObstacleMap &obstacle_nodes,
                    const IntersectionEdgeGeometries &geometries,
                    const TurnLanesIndexedArray &turn_lanes_data,
                    const IntersectionEdge &from,
@@ -506,14 +506,18 @@ bool isTurnAllowed(const util::NodeBasedDynamicGraph &graph,
         }
     }
 
-    // 3) if the intersection has a barrier
-    const bool is_barrier_node = barrier_nodes.find(intersection_node) != barrier_nodes.end();
+    // 3) if the intersection has an obstacle or is a designated turning place
+
+    bool is_barrier_node =
+        obstacle_nodes.any(SPECIAL_NODEID, intersection_node, Obstacle::Type::Barrier);
+    bool is_turning_node =
+        obstacle_nodes.any(SPECIAL_NODEID, intersection_node, Obstacle::Type::Turning);
 
     // Check a U-turn
     if (from.node == destination_node)
     {
-        // Allow U-turns before barrier nodes
-        if (is_barrier_node)
+        // Allow U-turns before barrier nodes or at designated turning places
+        if (is_barrier_node || is_turning_node)
             return true;
 
         // Allow U-turns at dead-ends
@@ -597,7 +601,7 @@ bool isTurnAllowed(const util::NodeBasedDynamicGraph &graph,
 IntersectionView convertToIntersectionView(const util::NodeBasedDynamicGraph &graph,
                                            const EdgeBasedNodeDataContainer &node_data_container,
                                            const RestrictionMap &restriction_map,
-                                           const std::unordered_set<NodeID> &barrier_nodes,
+                                           const ObstacleMap &obstacle_nodes,
                                            const IntersectionEdgeGeometries &edge_geometries,
                                            const TurnLanesIndexedArray &turn_lanes_data,
                                            const IntersectionEdge &incoming_edge,
@@ -625,7 +629,7 @@ IntersectionView convertToIntersectionView(const util::NodeBasedDynamicGraph &gr
         const auto is_turn_allowed = intersection::isTurnAllowed(graph,
                                                                  node_data_container,
                                                                  restriction_map,
-                                                                 barrier_nodes,
+                                                                 obstacle_nodes,
                                                                  edge_geometries,
                                                                  turn_lanes_data,
                                                                  incoming_edge,
@@ -748,7 +752,7 @@ IntersectionView getConnectedRoads(const util::NodeBasedDynamicGraph &graph,
                                    const std::vector<util::Coordinate> &node_coordinates,
                                    const extractor::CompressedEdgeContainer &compressed_geometries,
                                    const RestrictionMap &node_restriction_map,
-                                   const std::unordered_set<NodeID> &barrier_nodes,
+                                   const ObstacleMap &obstacle_nodes,
                                    const TurnLanesIndexedArray &turn_lanes_data,
                                    const IntersectionEdge &incoming_edge)
 {
@@ -760,7 +764,7 @@ IntersectionView getConnectedRoads(const util::NodeBasedDynamicGraph &graph,
     return getConnectedRoadsForEdgeGeometries(graph,
                                               node_data_container,
                                               node_restriction_map,
-                                              barrier_nodes,
+                                              obstacle_nodes,
                                               turn_lanes_data,
                                               incoming_edge,
                                               edge_geometries,
@@ -771,7 +775,7 @@ IntersectionView
 getConnectedRoadsForEdgeGeometries(const util::NodeBasedDynamicGraph &graph,
                                    const EdgeBasedNodeDataContainer &node_data_container,
                                    const RestrictionMap &node_restriction_map,
-                                   const std::unordered_set<NodeID> &barrier_nodes,
+                                   const ObstacleMap &obstacle_nodes,
                                    const TurnLanesIndexedArray &turn_lanes_data,
                                    const IntersectionEdge &incoming_edge,
                                    const IntersectionEdgeGeometries &edge_geometries,
@@ -802,7 +806,7 @@ getConnectedRoadsForEdgeGeometries(const util::NodeBasedDynamicGraph &graph,
     return convertToIntersectionView(graph,
                                      node_data_container,
                                      node_restriction_map,
-                                     barrier_nodes,
+                                     obstacle_nodes,
                                      processed_edge_geometries,
                                      turn_lanes_data,
                                      incoming_edge,
@@ -816,7 +820,7 @@ getConnectedRoads<false>(const util::NodeBasedDynamicGraph &graph,
                          const std::vector<util::Coordinate> &node_coordinates,
                          const extractor::CompressedEdgeContainer &compressed_geometries,
                          const RestrictionMap &node_restriction_map,
-                         const std::unordered_set<NodeID> &barrier_nodes,
+                         const ObstacleMap &obstacle_nodes,
                          const TurnLanesIndexedArray &turn_lanes_data,
                          const IntersectionEdge &incoming_edge);
 
@@ -826,7 +830,7 @@ getConnectedRoads<true>(const util::NodeBasedDynamicGraph &graph,
                         const std::vector<util::Coordinate> &node_coordinates,
                         const extractor::CompressedEdgeContainer &compressed_geometries,
                         const RestrictionMap &node_restriction_map,
-                        const std::unordered_set<NodeID> &barrier_nodes,
+                        const ObstacleMap &obstacle_nodes,
                         const TurnLanesIndexedArray &turn_lanes_data,
                         const IntersectionEdge &incoming_edge);
 
diff --git a/src/extractor/intersection/mergable_road_detector.cpp b/src/extractor/intersection/mergable_road_detector.cpp
index adfb601cb44..69d93d80af3 100644
--- a/src/extractor/intersection/mergable_road_detector.cpp
+++ b/src/extractor/intersection/mergable_road_detector.cpp
@@ -54,13 +54,13 @@ MergableRoadDetector::MergableRoadDetector(
     const std::vector<util::Coordinate> &node_coordinates,
     const extractor::CompressedEdgeContainer &compressed_geometries,
     const RestrictionMap &node_restriction_map,
-    const std::unordered_set<NodeID> &barrier_nodes,
+    const ObstacleMap &obstacle_nodes,
     const extractor::TurnLanesIndexedArray &turn_lanes_data,
     const NameTable &name_table,
     const SuffixTable &street_name_suffix_table)
     : node_based_graph(node_based_graph), node_data_container(node_data_container),
       node_coordinates(node_coordinates), compressed_geometries(compressed_geometries),
-      node_restriction_map(node_restriction_map), barrier_nodes(barrier_nodes),
+      node_restriction_map(node_restriction_map), obstacle_nodes(obstacle_nodes),
       turn_lanes_data(turn_lanes_data), name_table(name_table),
       street_name_suffix_table(street_name_suffix_table),
       coordinate_extractor(node_based_graph, compressed_geometries, node_coordinates)
@@ -180,7 +180,7 @@ bool MergableRoadDetector::IsNarrowTriangle(const NodeID intersection_node,
                                                    node_coordinates,
                                                    compressed_geometries,
                                                    node_restriction_map,
-                                                   barrier_nodes,
+                                                   obstacle_nodes,
                                                    turn_lanes_data),
         right_accumulator(SMALL_RANDOM_HOPLIMIT,
                           node_based_graph,
@@ -188,7 +188,7 @@ bool MergableRoadDetector::IsNarrowTriangle(const NodeID intersection_node,
                           node_coordinates,
                           compressed_geometries,
                           node_restriction_map,
-                          barrier_nodes,
+                          obstacle_nodes,
                           turn_lanes_data);
 
     /* Standard following the straightmost road
@@ -206,7 +206,7 @@ bool MergableRoadDetector::IsNarrowTriangle(const NodeID intersection_node,
                                       node_coordinates,
                                       compressed_geometries,
                                       node_restriction_map,
-                                      barrier_nodes,
+                                      obstacle_nodes,
                                       turn_lanes_data);
     graph_walker.TraverseRoad(intersection_node, lhs.eid, left_accumulator, selector);
     /* if the intersection does not have a right turn, we continue onto the next one once
@@ -286,7 +286,7 @@ bool MergableRoadDetector::IsNarrowTriangle(const NodeID intersection_node,
                                                       node_coordinates,
                                                       compressed_geometries,
                                                       node_restriction_map,
-                                                      barrier_nodes,
+                                                      obstacle_nodes,
                                                       turn_lanes_data);
     graph_walker.TraverseRoad(node_based_graph.GetTarget(left_accumulator.via_edge_id),
                               connector_turn->eid,
@@ -307,7 +307,7 @@ bool MergableRoadDetector::IsCircularShape(const NodeID intersection_node,
                                       node_coordinates,
                                       compressed_geometries,
                                       node_restriction_map,
-                                      barrier_nodes,
+                                      obstacle_nodes,
                                       turn_lanes_data);
     const auto getCoordinatesAlongWay = [&](const EdgeID edge_id, const double max_length)
     {
@@ -351,7 +351,7 @@ bool MergableRoadDetector::IsCircularShape(const NodeID intersection_node,
     //      ----         ----
     //          \       /
     //           -------
-    const auto constexpr CIRCULAR_POLYGON_ISOPERIMETRIC_LOWER_BOUND = 0.85 / (4 * std::numbers::pi);
+    const auto CIRCULAR_POLYGON_ISOPERIMETRIC_LOWER_BOUND = 0.85 / (4 * std::numbers::pi);
     if (connect_again && coordinates_to_the_left.front() == coordinates_to_the_left.back())
     { // if the left and right roads connect again and are closed polygons ...
         const auto area = util::coordinate_calculation::computeArea(coordinates_to_the_left);
@@ -380,7 +380,7 @@ bool MergableRoadDetector::HaveSameDirection(const NodeID intersection_node,
                                       node_coordinates,
                                       compressed_geometries,
                                       node_restriction_map,
-                                      barrier_nodes,
+                                      obstacle_nodes,
                                       turn_lanes_data);
     const auto getCoordinatesAlongWay = [&](const EdgeID edge_id, const double max_length)
     {
@@ -565,7 +565,7 @@ bool MergableRoadDetector::IsLinkRoad(const NodeID intersection_node,
                                                node_coordinates,
                                                compressed_geometries,
                                                node_restriction_map,
-                                               barrier_nodes,
+                                               obstacle_nodes,
                                                turn_lanes_data,
                                                next_intersection_parameters);
     const auto extract_name_id = [this](const MergableRoadData &road)
diff --git a/src/extractor/intersection/node_based_graph_walker.cpp b/src/extractor/intersection/node_based_graph_walker.cpp
index c1ee0fc642e..3f9414a2502 100644
--- a/src/extractor/intersection/node_based_graph_walker.cpp
+++ b/src/extractor/intersection/node_based_graph_walker.cpp
@@ -17,11 +17,11 @@ NodeBasedGraphWalker::NodeBasedGraphWalker(
     const std::vector<util::Coordinate> &node_coordinates,
     const extractor::CompressedEdgeContainer &compressed_geometries,
     const RestrictionMap &node_restriction_map,
-    const std::unordered_set<NodeID> &barrier_nodes,
+    const ObstacleMap &obstacle_nodes,
     const TurnLanesIndexedArray &turn_lanes_data)
     : node_based_graph(node_based_graph), node_data_container(node_data_container),
       node_coordinates(node_coordinates), compressed_geometries(compressed_geometries),
-      node_restriction_map(node_restriction_map), barrier_nodes(barrier_nodes),
+      node_restriction_map(node_restriction_map), obstacle_nodes(obstacle_nodes),
       turn_lanes_data(turn_lanes_data)
 {
 }
@@ -252,12 +252,12 @@ IntersectionFinderAccumulator::IntersectionFinderAccumulator(
     const std::vector<util::Coordinate> &node_coordinates,
     const extractor::CompressedEdgeContainer &compressed_geometries,
     const RestrictionMap &node_restriction_map,
-    const std::unordered_set<NodeID> &barrier_nodes,
+    const ObstacleMap &obstacle_nodes,
     const TurnLanesIndexedArray &turn_lanes_data)
     : hops(0), hop_limit(hop_limit), node_based_graph(node_based_graph),
       node_data_container(node_data_container), node_coordinates(node_coordinates),
       compressed_geometries(compressed_geometries), node_restriction_map(node_restriction_map),
-      barrier_nodes(barrier_nodes), turn_lanes_data(turn_lanes_data)
+      obstacle_nodes(obstacle_nodes), turn_lanes_data(turn_lanes_data)
 {
 }
 
@@ -287,7 +287,7 @@ void IntersectionFinderAccumulator::update(const NodeID from_node,
                                                          node_coordinates,
                                                          compressed_geometries,
                                                          node_restriction_map,
-                                                         barrier_nodes,
+                                                         obstacle_nodes,
                                                          turn_lanes_data,
                                                          {from_node, via_edge});
 }
diff --git a/src/extractor/node_based_graph_factory.cpp b/src/extractor/node_based_graph_factory.cpp
index 9fb50149d4d..b11d39f1a9b 100644
--- a/src/extractor/node_based_graph_factory.cpp
+++ b/src/extractor/node_based_graph_factory.cpp
@@ -17,17 +17,15 @@ NodeBasedGraphFactory::NodeBasedGraphFactory(
     ScriptingEnvironment &scripting_environment,
     std::vector<TurnRestriction> &turn_restrictions,
     std::vector<UnresolvedManeuverOverride> &maneuver_overrides,
-    TrafficSignals &traffic_signals,
-    std::unordered_set<NodeID> &&barriers,
     std::vector<util::Coordinate> &&coordinates,
     extractor::PackedOSMIDs &&osm_node_ids,
     const std::vector<NodeBasedEdge> &edge_list,
     std::vector<NodeBasedEdgeAnnotation> &&annotation_data)
-    : annotation_data(std::move(annotation_data)), barriers(std::move(barriers)),
-      coordinates(std::move(coordinates)), osm_node_ids(std::move(osm_node_ids))
+    : annotation_data(std::move(annotation_data)), coordinates(std::move(coordinates)),
+      osm_node_ids(std::move(osm_node_ids))
 {
     BuildCompressedOutputGraph(edge_list);
-    Compress(scripting_environment, turn_restrictions, maneuver_overrides, traffic_signals);
+    Compress(scripting_environment, turn_restrictions, maneuver_overrides);
     CompressGeometry();
     CompressAnnotationData();
 }
@@ -75,13 +73,10 @@ void NodeBasedGraphFactory::BuildCompressedOutputGraph(const std::vector<NodeBas
 
 void NodeBasedGraphFactory::Compress(ScriptingEnvironment &scripting_environment,
                                      std::vector<TurnRestriction> &turn_restrictions,
-                                     std::vector<UnresolvedManeuverOverride> &maneuver_overrides,
-                                     TrafficSignals &traffic_signals)
+                                     std::vector<UnresolvedManeuverOverride> &maneuver_overrides)
 {
     GraphCompressor graph_compressor;
-    graph_compressor.Compress(barriers,
-                              traffic_signals,
-                              scripting_environment,
+    graph_compressor.Compress(scripting_environment,
                               turn_restrictions,
                               maneuver_overrides,
                               compressed_output_graph,
diff --git a/src/extractor/obstacles.cpp b/src/extractor/obstacles.cpp
new file mode 100644
index 00000000000..b4ca86617f5
--- /dev/null
+++ b/src/extractor/obstacles.cpp
@@ -0,0 +1,159 @@
+/*
+
+Copyright (c) 2025, Project OSRM contributors
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list
+of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this
+list of conditions and the following disclaimer in the documentation and/or
+other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+#include "extractor/obstacles.hpp"
+
+#include "util/log.hpp"
+#include "util/timing_util.hpp"
+
+namespace osrm::extractor
+{
+
+const std::initializer_list<std::pair<std::string_view, Obstacle::Type>>
+    Obstacle::enum_type_initializer_list{{"none", Obstacle::Type::None},
+                                         {"barrier", Obstacle::Type::Barrier},
+                                         {"traffic_signals", Obstacle::Type::TrafficSignals},
+                                         {"stop", Obstacle::Type::Stop},
+                                         {"stop_minor", Obstacle::Type::StopMinor},
+                                         {"give_way", Obstacle::Type::GiveWay},
+                                         {"crossing", Obstacle::Type::Crossing},
+                                         {"traffic_calming", Obstacle::Type::TrafficCalming},
+                                         {"mini_roundabout", Obstacle::Type::MiniRoundabout},
+                                         {"turning_loop", Obstacle::Type::TurningLoop},
+                                         {"turning_circle", Obstacle::Type::TurningCircle}};
+
+const std::initializer_list<std::pair<std::string_view, Obstacle::Direction>>
+    Obstacle::enum_direction_initializer_list{{"none", Obstacle::Direction::None},
+                                              {"forward", Obstacle::Direction::Forward},
+                                              {"backward", Obstacle::Direction::Backward},
+                                              {"both", Obstacle::Direction::Both}};
+
+void ObstacleMap::preProcess(const NodeIDVector &node_ids, const WayNodeIDOffsets &way_node_offsets)
+{
+    util::UnbufferedLog log;
+    log << "Collecting information on " << osm_obstacles.size() << " obstacles...";
+    TIMER_START(preProcess);
+
+    // build a map to speed up the next step
+    // multimap of OSMNodeId to index into way_node_offsets
+    std::unordered_multimap<OSMNodeID, size_t> node2start_index;
+
+    for (size_t i = 0, j = 1; j < way_node_offsets.size(); ++i, ++j)
+    {
+        for (size_t k = way_node_offsets[i]; k < way_node_offsets[j]; ++k)
+        {
+            node2start_index.emplace(node_ids[k], i);
+        }
+    }
+
+    // for each unidirectional obstacle
+    //      for each way that crosses the obstacle
+    //          for each node of the crossing way
+    //              find the node immediately before or after the obstacle
+    //              add the unidirectional obstacle
+    //              note that we don't remove the bidirectional obstacle here,
+    //              but in fixupNodes()
+
+    for (auto &[from_id, to_id, obstacle] : osm_obstacles)
+    {
+        if (obstacle.direction == Obstacle::Direction::Forward ||
+            obstacle.direction == Obstacle::Direction::Backward)
+        {
+            bool forward = obstacle.direction == Obstacle::Direction::Forward;
+            auto [wno_begin, wno_end] = node2start_index.equal_range(to_id);
+            // for each way that crosses the obstacle
+            for (auto wno_iter = wno_begin; wno_iter != wno_end; ++wno_iter)
+            {
+                using NodeIdIterator = NodeIDVector::const_iterator;
+
+                NodeIdIterator begin = node_ids.cbegin() + way_node_offsets[wno_iter->second];
+                NodeIdIterator end = node_ids.cbegin() + way_node_offsets[wno_iter->second + 1];
+                if (forward)
+                    ++begin;
+                else
+                    --end;
+
+                NodeIdIterator node_iter = find(begin, end, to_id);
+                if (node_iter != end)
+                {
+                    osm_obstacles.emplace_back(*(node_iter + (forward ? -1 : 1)), to_id, obstacle);
+                }
+            }
+        }
+    }
+
+    TIMER_STOP(preProcess);
+    log << "ok, after " << TIMER_SEC(preProcess) << "s";
+}
+
+void ObstacleMap::fixupNodes(const NodeIDVector &node_ids)
+{
+    const auto begin = node_ids.cbegin();
+    const auto end = node_ids.cend();
+
+    auto osm_to_internal = [&](const OSMNodeID &osm_node) -> NodeID
+    {
+        if (osm_node == SPECIAL_OSM_NODEID)
+        {
+            return SPECIAL_NODEID;
+        }
+        const auto it = std::lower_bound(begin, end, osm_node);
+        return (it == end || osm_node < *it) ? SPECIAL_NODEID
+                                             : static_cast<NodeID>(std::distance(begin, it));
+    };
+
+    for (const auto &[osm_from, osm_to, obstacle] : osm_obstacles)
+    {
+        if ((obstacle.direction == Obstacle::Direction::Forward ||
+             obstacle.direction == Obstacle::Direction::Backward) &&
+            osm_from == SPECIAL_OSM_NODEID)
+            // drop these bidirectional entries because we have generated an
+            // unidirectional copy of them
+            continue;
+        emplace(osm_to_internal(osm_from), osm_to_internal(osm_to), obstacle);
+    }
+    osm_obstacles.clear();
+}
+
+void ObstacleMap::compress(NodeID node1, NodeID delendus, NodeID node2)
+{
+    auto comp = [this, delendus](NodeID first, NodeID last)
+    {
+        const auto &[begin, end] = obstacles.equal_range(last);
+        for (auto i = begin; i != end; ++i)
+        {
+            auto &[from, to, obstacle] = i->second;
+            if (from == delendus)
+                from = first;
+        }
+    };
+
+    comp(node1, node2);
+    comp(node2, node1);
+}
+
+} // namespace osrm::extractor
diff --git a/src/extractor/scripting_environment_lua.cpp b/src/extractor/scripting_environment_lua.cpp
index 9aaf7977812..ca565751c80 100644
--- a/src/extractor/scripting_environment_lua.cpp
+++ b/src/extractor/scripting_environment_lua.cpp
@@ -6,11 +6,11 @@
 #include "extractor/extraction_segment.hpp"
 #include "extractor/extraction_turn.hpp"
 #include "extractor/extraction_way.hpp"
+#include "extractor/graph_compressor.hpp"
 #include "extractor/internal_extractor_edge.hpp"
 #include "extractor/maneuver_override_relation_parser.hpp"
 #include "extractor/profile_properties.hpp"
 #include "extractor/query_node.hpp"
-#include "extractor/raster_source.hpp"
 #include "extractor/restriction_parser.hpp"
 
 #include "guidance/turn_instruction.hpp"
@@ -99,11 +99,12 @@ void handle_lua_error(const sol::protected_function_result &luares)
     const auto msg = luaerr.what();
     if (msg != nullptr)
     {
-        std::cerr << msg << "\n";
+        // util::Log is thread-safe
+        util::UnbufferedLog(logERROR) << msg << "\n";
     }
     else
     {
-        std::cerr << "unknown error\n";
+        util::UnbufferedLog(logERROR) << "unknown error\n";
     }
     throw util::exception("Lua error (see stderr for traceback)");
 }
@@ -303,40 +304,96 @@ void Sol2ScriptingEnvironment::InitContext(LuaScriptingContext &context)
 
     context.state.new_enum("traffic_lights",
                            "none",
-                           extractor::TrafficLightClass::NONE,
+                           Obstacle::Direction::None,
                            "direction_all",
-                           extractor::TrafficLightClass::DIRECTION_ALL,
+                           Obstacle::Direction::Both,
                            "direction_forward",
-                           extractor::TrafficLightClass::DIRECTION_FORWARD,
+                           Obstacle::Direction::Forward,
                            "direction_reverse",
-                           extractor::TrafficLightClass::DIRECTION_REVERSE);
+                           Obstacle::Direction::Backward);
+
+    context.state.new_enum("obstacle_type", Obstacle::enum_type_initializer_list);
+    context.state.new_enum("obstacle_direction", Obstacle::enum_direction_initializer_list);
+
+    context.state.new_usertype<Obstacle>(
+        "Obstacle",
+        sol::constructors<Obstacle(Obstacle::Type),
+                          Obstacle(Obstacle::Type, Obstacle::Direction),
+                          Obstacle(Obstacle::Type, Obstacle::Direction, float, float)>(),
+        "type",
+        sol::readonly(&Obstacle::type),
+        "direction",
+        sol::readonly(&Obstacle::direction),
+        "duration",
+        sol::readonly(&Obstacle::duration),
+        "weight",
+        sol::readonly(&Obstacle::weight));
+
+    context.state.new_usertype<ObstacleMap>(
+        "ObstacleMap",
+        "add",
+        [](ObstacleMap &obstacles, const osmium::Node &from, Obstacle obstacle)
+        {
+            OSMNodeID id = to_alias<OSMNodeID>(from.id());
+            obstacles.emplace(id, obstacle);
+        },
+        "get",
+        sol::overload([](const ObstacleMap &om, NodeID to) { return om.get(to); },
+                      [](const ObstacleMap &om, NodeID from, NodeID to)
+                      { return om.get(from, to); },
+                      [](const ObstacleMap &om, NodeID from, NodeID to, Obstacle::Type type)
+                      { return om.get(from, to, type); }),
+        "any",
+        sol::overload([](const ObstacleMap &om, NodeID to) { return om.any(to); },
+                      [](const ObstacleMap &om, NodeID from, NodeID to)
+                      { return om.any(from, to, Obstacle::Type::All); },
+                      [](const ObstacleMap &om, NodeID from, NodeID to, Obstacle::Type type)
+                      { return om.any(from, to, type); }));
+
+    context.state["obstacle_map"] = std::ref(m_obstacle_map);
 
     context.state.new_usertype<ExtractionNode>(
         "ResultNode",
+        // for API compatibility only
         "traffic_lights",
-        sol::property([](const ExtractionNode &node) { return node.traffic_lights; },
-                      [](ExtractionNode &node, const sol::object &obj)
-                      {
-                          if (obj.is<bool>())
-                          {
-                              // The old approach of assigning a boolean traffic light
-                              // state to the node is converted to the class enum
-                              // TODO: Make a breaking API change and remove this option.
-                              bool val = obj.as<bool>();
-                              node.traffic_lights = (val) ? TrafficLightClass::DIRECTION_ALL
-                                                          : TrafficLightClass::NONE;
-                              return;
-                          }
-
-                          BOOST_ASSERT(obj.is<TrafficLightClass::Direction>());
-                          {
-                              TrafficLightClass::Direction val =
-                                  obj.as<TrafficLightClass::Direction>();
-                              node.traffic_lights = val;
-                          }
-                      }),
+        sol::property(
+            [&context, this](ExtractionNode &node, const sol::object &obj)
+            {
+                if (obj.is<Obstacle::Direction>())
+                {
+                    m_obstacle_map.emplace(
+                        to_alias<OSMNodeID>(node.node->id()),
+                        Obstacle{Obstacle::Type::TrafficSignals,
+                                 obj.as<Obstacle::Direction>(),
+                                 static_cast<float>(context.properties.GetTrafficSignalPenalty()),
+                                 0});
+                    return;
+                }
+                if (obj.is<bool>() && obj.as<bool>())
+                {
+                    // The old approach of assigning a boolean traffic light
+                    // state to the node
+                    // TODO: Make a breaking API change and remove this option.
+                    m_obstacle_map.emplace(
+                        to_alias<OSMNodeID>(node.node->id()),
+                        Obstacle{Obstacle::Type::TrafficSignals,
+                                 Obstacle::Direction::Both,
+                                 static_cast<float>(context.properties.GetTrafficSignalPenalty()),
+                                 0});
+                }
+            }),
+        // for API compatibility only
         "barrier",
-        &ExtractionNode::barrier);
+        sol::property(
+            [this](ExtractionNode &node, const sol::object &obj)
+            {
+                if (obj.is<bool>() && obj.as<bool>())
+                {
+                    m_obstacle_map.emplace(
+                        to_alias<OSMNodeID>(node.node->id()),
+                        Obstacle{Obstacle::Type::Barrier, Obstacle::Direction::Both});
+                }
+            }));
 
     context.state.new_usertype<RoadClassification>(
         "RoadClassification",
@@ -807,6 +864,8 @@ void Sol2ScriptingEnvironment::InitContext(LuaScriptingContext &context)
             &ExtractionTurnLeg::access_turn_classification,
             "speed",
             &ExtractionTurnLeg::speed,
+            "distance",
+            &ExtractionTurnLeg::distance,
             "priority_class",
             &ExtractionTurnLeg::priority_class,
             "is_incoming",
@@ -865,10 +924,21 @@ void Sol2ScriptingEnvironment::InitContext(LuaScriptingContext &context)
             "target_priority_class",
             &ExtractionTurn::target_priority_class,
 
+            "from",
+            &ExtractionTurn::from,
+            "via",
+            &ExtractionTurn::via,
+            "to",
+            &ExtractionTurn::to,
+            "source_road",
+            &ExtractionTurn::source_road,
+            "target_road",
+            &ExtractionTurn::target_road,
             "roads_on_the_right",
             &ExtractionTurn::roads_on_the_right,
             "roads_on_the_left",
             &ExtractionTurn::roads_on_the_left,
+
             "weight",
             &ExtractionTurn::weight,
             "duration",
@@ -975,13 +1045,13 @@ void Sol2ScriptingEnvironment::ProcessElements(
         case osmium::item_type::node:
         {
             const auto &node = static_cast<const osmium::Node &>(*entity);
-            // NOLINTNEXTLINE(bugprone-use-after-move)
-            result_node.clear();
+            result_node.node = &node;
             if (local_context.has_node_function &&
                 (!node.tags().empty() || local_context.properties.call_tagless_node_function))
             {
                 local_context.ProcessNode(node, result_node, relations);
             }
+            result_node.node = nullptr;
             resulting_nodes.push_back({node, result_node});
         }
         break;
diff --git a/src/guidance/driveway_handler.cpp b/src/guidance/driveway_handler.cpp
index 5e781a9bb30..20cdf25ef5c 100644
--- a/src/guidance/driveway_handler.cpp
+++ b/src/guidance/driveway_handler.cpp
@@ -10,7 +10,7 @@ DrivewayHandler::DrivewayHandler(const util::NodeBasedDynamicGraph &node_based_g
                                  const std::vector<util::Coordinate> &node_coordinates,
                                  const extractor::CompressedEdgeContainer &compressed_geometries,
                                  const extractor::RestrictionMap &node_restriction_map,
-                                 const std::unordered_set<NodeID> &barrier_nodes,
+                                 const extractor::ObstacleMap &obstacle_nodes,
                                  const extractor::TurnLanesIndexedArray &turn_lanes_data,
                                  const extractor::NameTable &name_table,
                                  const extractor::SuffixTable &street_name_suffix_table)
@@ -19,7 +19,7 @@ DrivewayHandler::DrivewayHandler(const util::NodeBasedDynamicGraph &node_based_g
                           node_coordinates,
                           compressed_geometries,
                           node_restriction_map,
-                          barrier_nodes,
+                          obstacle_nodes,
                           turn_lanes_data,
                           name_table,
                           street_name_suffix_table)
diff --git a/src/guidance/guidance_processing.cpp b/src/guidance/guidance_processing.cpp
index 2debabb04f8..d26ba8928f2 100644
--- a/src/guidance/guidance_processing.cpp
+++ b/src/guidance/guidance_processing.cpp
@@ -20,7 +20,7 @@ void annotateTurns(const util::NodeBasedDynamicGraph &node_based_graph,
                    const extractor::EdgeBasedNodeDataContainer &edge_based_node_container,
                    const std::vector<util::Coordinate> &node_coordinates,
                    const extractor::CompressedEdgeContainer &compressed_edge_container,
-                   const std::unordered_set<NodeID> &barrier_nodes,
+                   const extractor::ObstacleMap &obstacle_nodes,
                    const extractor::RestrictionMap &node_restriction_map,
                    const extractor::WayRestrictionMap &way_restriction_map,
                    const extractor::NameTable &name_table,
@@ -41,7 +41,7 @@ void annotateTurns(const util::NodeBasedDynamicGraph &node_based_graph,
                                                                          node_coordinates,
                                                                          compressed_edge_container,
                                                                          node_restriction_map,
-                                                                         barrier_nodes,
+                                                                         obstacle_nodes,
                                                                          turn_lanes_data,
                                                                          name_table,
                                                                          suffix_table);
@@ -51,7 +51,7 @@ void annotateTurns(const util::NodeBasedDynamicGraph &node_based_graph,
                                          node_coordinates,
                                          compressed_edge_container,
                                          node_restriction_map,
-                                         barrier_nodes,
+                                         obstacle_nodes,
                                          turn_lanes_data,
                                          name_table,
                                          suffix_table);
@@ -61,7 +61,7 @@ void annotateTurns(const util::NodeBasedDynamicGraph &node_based_graph,
                                                        node_coordinates,
                                                        compressed_edge_container,
                                                        node_restriction_map,
-                                                       barrier_nodes,
+                                                       obstacle_nodes,
                                                        turn_lanes_data,
                                                        lane_description_map,
                                                        turn_analysis,
@@ -173,7 +173,7 @@ void annotateTurns(const util::NodeBasedDynamicGraph &node_based_graph,
                                 node_based_graph,
                                 edge_based_node_container,
                                 node_restriction_map,
-                                barrier_nodes,
+                                obstacle_nodes,
                                 edge_geometries,
                                 turn_lanes_data,
                                 incoming_edge,
@@ -213,7 +213,7 @@ void annotateTurns(const util::NodeBasedDynamicGraph &node_based_graph,
                                 extractor::intersection::isTurnAllowed(node_based_graph,
                                                                        edge_based_node_container,
                                                                        node_restriction_map,
-                                                                       barrier_nodes,
+                                                                       obstacle_nodes,
                                                                        edge_geometries,
                                                                        turn_lanes_data,
                                                                        incoming_edge,
diff --git a/src/guidance/intersection_handler.cpp b/src/guidance/intersection_handler.cpp
index b11597d5f46..5f839e97a13 100644
--- a/src/guidance/intersection_handler.cpp
+++ b/src/guidance/intersection_handler.cpp
@@ -46,20 +46,20 @@ IntersectionHandler::IntersectionHandler(
     const std::vector<util::Coordinate> &node_coordinates,
     const extractor::CompressedEdgeContainer &compressed_geometries,
     const extractor::RestrictionMap &node_restriction_map,
-    const std::unordered_set<NodeID> &barrier_nodes,
+    const extractor::ObstacleMap &obstacle_nodes,
     const extractor::TurnLanesIndexedArray &turn_lanes_data,
     const extractor::NameTable &name_table,
     const extractor::SuffixTable &street_name_suffix_table)
     : node_based_graph(node_based_graph), node_data_container(node_data_container),
       node_coordinates(node_coordinates), compressed_geometries(compressed_geometries),
-      node_restriction_map(node_restriction_map), barrier_nodes(barrier_nodes),
+      node_restriction_map(node_restriction_map), obstacle_nodes(obstacle_nodes),
       turn_lanes_data(turn_lanes_data), name_table(name_table),
       street_name_suffix_table(street_name_suffix_table), graph_walker(node_based_graph,
                                                                        node_data_container,
                                                                        node_coordinates,
                                                                        compressed_geometries,
                                                                        node_restriction_map,
-                                                                       barrier_nodes,
+                                                                       obstacle_nodes,
                                                                        turn_lanes_data)
 {
 }
@@ -430,7 +430,7 @@ void IntersectionHandler::assignTrivialTurns(const EdgeID via_eid,
 std::optional<IntersectionHandler::IntersectionViewAndNode>
 IntersectionHandler::getNextIntersection(const NodeID at, const EdgeID via) const
 {
-    // We use the intersection generator to jump over traffic signals, barriers. The intersection
+    // We use the intersection generator to jump over obstacles. The intersection
     // generater takes a starting node and a corresponding edge starting at this node. It returns
     // the next non-artificial intersection writing as out param. the source node and the edge
     // for which the target is the next intersection.
@@ -458,12 +458,12 @@ IntersectionHandler::getNextIntersection(const NodeID at, const EdgeID via) cons
                                                                           node_coordinates,
                                                                           compressed_geometries,
                                                                           node_restriction_map,
-                                                                          barrier_nodes,
+                                                                          obstacle_nodes,
                                                                           turn_lanes_data,
                                                                           intersection_parameters);
     auto intersection_node = node_based_graph.GetTarget(intersection_parameters.edge);
 
-    if (intersection.size() <= 2 || intersection.isTrafficSignalOrBarrier())
+    if (intersection.size() <= 2 || intersection.isObstacle())
     {
         return std::nullopt;
     }
diff --git a/src/guidance/motorway_handler.cpp b/src/guidance/motorway_handler.cpp
index 5c85e2edf5f..258124d9963 100644
--- a/src/guidance/motorway_handler.cpp
+++ b/src/guidance/motorway_handler.cpp
@@ -44,7 +44,7 @@ MotorwayHandler::MotorwayHandler(const util::NodeBasedDynamicGraph &node_based_g
                                  const std::vector<util::Coordinate> &coordinates,
                                  const extractor::CompressedEdgeContainer &compressed_geometries,
                                  const extractor::RestrictionMap &node_restriction_map,
-                                 const std::unordered_set<NodeID> &barrier_nodes,
+                                 const extractor::ObstacleMap &obstacle_nodes,
                                  const extractor::TurnLanesIndexedArray &turn_lanes_data,
                                  const extractor::NameTable &name_table,
                                  const extractor::SuffixTable &street_name_suffix_table)
@@ -53,7 +53,7 @@ MotorwayHandler::MotorwayHandler(const util::NodeBasedDynamicGraph &node_based_g
                           coordinates,
                           compressed_geometries,
                           node_restriction_map,
-                          barrier_nodes,
+                          obstacle_nodes,
                           turn_lanes_data,
                           name_table,
                           street_name_suffix_table)
diff --git a/src/guidance/roundabout_handler.cpp b/src/guidance/roundabout_handler.cpp
index 451ebf7bf5c..a1390b5c2c0 100644
--- a/src/guidance/roundabout_handler.cpp
+++ b/src/guidance/roundabout_handler.cpp
@@ -24,7 +24,7 @@ RoundaboutHandler::RoundaboutHandler(
     const std::vector<util::Coordinate> &coordinates,
     const extractor::CompressedEdgeContainer &compressed_geometries,
     const extractor::RestrictionMap &node_restriction_map,
-    const std::unordered_set<NodeID> &barrier_nodes,
+    const extractor::ObstacleMap &obstacle_nodes,
     const extractor::TurnLanesIndexedArray &turn_lanes_data,
     const extractor::NameTable &name_table,
     const extractor::SuffixTable &street_name_suffix_table)
@@ -33,7 +33,7 @@ RoundaboutHandler::RoundaboutHandler(
                           coordinates,
                           compressed_geometries,
                           node_restriction_map,
-                          barrier_nodes,
+                          obstacle_nodes,
                           turn_lanes_data,
                           name_table,
                           street_name_suffix_table),
diff --git a/src/guidance/sliproad_handler.cpp b/src/guidance/sliproad_handler.cpp
index 90a92801f53..d5686632a0b 100644
--- a/src/guidance/sliproad_handler.cpp
+++ b/src/guidance/sliproad_handler.cpp
@@ -22,7 +22,7 @@ SliproadHandler::SliproadHandler(const util::NodeBasedDynamicGraph &node_based_g
                                  const std::vector<util::Coordinate> &node_coordinates,
                                  const extractor::CompressedEdgeContainer &compressed_geometries,
                                  const extractor::RestrictionMap &node_restriction_map,
-                                 const std::unordered_set<NodeID> &barrier_nodes,
+                                 const extractor::ObstacleMap &obstacle_nodes,
                                  const extractor::TurnLanesIndexedArray &turn_lanes_data,
                                  const extractor::NameTable &name_table,
                                  const extractor::SuffixTable &street_name_suffix_table)
@@ -31,7 +31,7 @@ SliproadHandler::SliproadHandler(const util::NodeBasedDynamicGraph &node_based_g
                           node_coordinates,
                           compressed_geometries,
                           node_restriction_map,
-                          barrier_nodes,
+                          obstacle_nodes,
                           turn_lanes_data,
                           name_table,
                           street_name_suffix_table),
@@ -255,9 +255,9 @@ Intersection SliproadHandler::operator()(const NodeID /*nid*/,
             node_coordinates,
             compressed_geometries,
             node_restriction_map,
-            barrier_nodes,
+            obstacle_nodes,
             turn_lanes_data};
-        const extractor::intersection::SkipTrafficSignalBarrierRoadSelector road_selector{};
+        const extractor::intersection::SkipObstacleRoadSelector road_selector{};
         (void)graph_walker.TraverseRoad(intersection_node_id, // start node
                                         sliproad_edge,        // onto edge
                                         intersection_finder,  // accumulator
@@ -585,10 +585,10 @@ Intersection SliproadHandler::operator()(const NodeID /*nid*/,
                         node_coordinates,
                         compressed_geometries,
                         node_restriction_map,
-                        barrier_nodes,
+                        obstacle_nodes,
                         turn_lanes_data,
                         {node_based_graph.GetTarget(sliproad_edge), candidate_road.eid});
-                if (skip_traffic_light_intersection.isTrafficSignalOrBarrier() &&
+                if (skip_traffic_light_intersection.isObstacle() &&
                     node_based_graph.GetTarget(skip_traffic_light_intersection[1].eid) ==
                         main_road_intersection->node)
                 {
@@ -703,7 +703,7 @@ bool SliproadHandler::nextIntersectionIsTooFarAway(const NodeID start, const Edg
 
     extractor::intersection::DistanceToNextIntersectionAccumulator accumulator{
         coordinate_extractor, node_based_graph, threshold};
-    const extractor::intersection::SkipTrafficSignalBarrierRoadSelector selector{};
+    const extractor::intersection::SkipObstacleRoadSelector selector{};
 
     (void)graph_walker.TraverseRoad(start, onto, accumulator, selector);
 
diff --git a/src/guidance/suppress_mode_handler.cpp b/src/guidance/suppress_mode_handler.cpp
index e1356de6b9f..c910732fa6d 100644
--- a/src/guidance/suppress_mode_handler.cpp
+++ b/src/guidance/suppress_mode_handler.cpp
@@ -13,7 +13,7 @@ SuppressModeHandler::SuppressModeHandler(
     const std::vector<util::Coordinate> &coordinates,
     const extractor::CompressedEdgeContainer &compressed_geometries,
     const extractor::RestrictionMap &node_restriction_map,
-    const std::unordered_set<NodeID> &barrier_nodes,
+    const extractor::ObstacleMap &obstacle_nodes,
     const extractor::TurnLanesIndexedArray &turn_lanes_data,
     const extractor::NameTable &name_table,
     const extractor::SuffixTable &street_name_suffix_table)
@@ -22,7 +22,7 @@ SuppressModeHandler::SuppressModeHandler(
                           coordinates,
                           compressed_geometries,
                           node_restriction_map,
-                          barrier_nodes,
+                          obstacle_nodes,
                           turn_lanes_data,
                           name_table,
                           street_name_suffix_table)
diff --git a/src/guidance/turn_analysis.cpp b/src/guidance/turn_analysis.cpp
index 5e51a5cbe27..08b419d361a 100644
--- a/src/guidance/turn_analysis.cpp
+++ b/src/guidance/turn_analysis.cpp
@@ -19,7 +19,7 @@ TurnAnalysis::TurnAnalysis(const util::NodeBasedDynamicGraph &node_based_graph,
                            const std::vector<util::Coordinate> &node_coordinates,
                            const extractor::CompressedEdgeContainer &compressed_edge_container,
                            const extractor::RestrictionMap &restriction_map,
-                           const std::unordered_set<NodeID> &barrier_nodes,
+                           const extractor::ObstacleMap &obstacle_nodes,
                            const extractor::TurnLanesIndexedArray &turn_lanes_data,
                            const extractor::NameTable &name_table,
                            const extractor::SuffixTable &street_name_suffix_table)
@@ -28,7 +28,7 @@ TurnAnalysis::TurnAnalysis(const util::NodeBasedDynamicGraph &node_based_graph,
                                                              node_coordinates,
                                                              compressed_edge_container,
                                                              restriction_map,
-                                                             barrier_nodes,
+                                                             obstacle_nodes,
                                                              turn_lanes_data,
                                                              name_table,
                                                              street_name_suffix_table),
@@ -37,7 +37,7 @@ TurnAnalysis::TurnAnalysis(const util::NodeBasedDynamicGraph &node_based_graph,
                        node_coordinates,
                        compressed_edge_container,
                        restriction_map,
-                       barrier_nodes,
+                       obstacle_nodes,
                        turn_lanes_data,
                        name_table,
                        street_name_suffix_table),
@@ -46,7 +46,7 @@ TurnAnalysis::TurnAnalysis(const util::NodeBasedDynamicGraph &node_based_graph,
                    node_coordinates,
                    compressed_edge_container,
                    restriction_map,
-                   barrier_nodes,
+                   obstacle_nodes,
                    turn_lanes_data,
                    name_table,
                    street_name_suffix_table),
@@ -55,7 +55,7 @@ TurnAnalysis::TurnAnalysis(const util::NodeBasedDynamicGraph &node_based_graph,
                        node_coordinates,
                        compressed_edge_container,
                        restriction_map,
-                       barrier_nodes,
+                       obstacle_nodes,
                        turn_lanes_data,
                        name_table,
                        street_name_suffix_table),
@@ -64,7 +64,7 @@ TurnAnalysis::TurnAnalysis(const util::NodeBasedDynamicGraph &node_based_graph,
                             node_coordinates,
                             compressed_edge_container,
                             restriction_map,
-                            barrier_nodes,
+                            obstacle_nodes,
                             turn_lanes_data,
                             name_table,
                             street_name_suffix_table),
@@ -73,7 +73,7 @@ TurnAnalysis::TurnAnalysis(const util::NodeBasedDynamicGraph &node_based_graph,
                        node_coordinates,
                        compressed_edge_container,
                        restriction_map,
-                       barrier_nodes,
+                       obstacle_nodes,
                        turn_lanes_data,
                        name_table,
                        street_name_suffix_table),
@@ -82,7 +82,7 @@ TurnAnalysis::TurnAnalysis(const util::NodeBasedDynamicGraph &node_based_graph,
                          node_coordinates,
                          compressed_edge_container,
                          restriction_map,
-                         barrier_nodes,
+                         obstacle_nodes,
                          turn_lanes_data,
                          name_table,
                          street_name_suffix_table)
diff --git a/src/guidance/turn_discovery.cpp b/src/guidance/turn_discovery.cpp
index 57b66f8a75c..0911a5f22f0 100644
--- a/src/guidance/turn_discovery.cpp
+++ b/src/guidance/turn_discovery.cpp
@@ -18,7 +18,7 @@ bool findPreviousIntersection(const NodeID node_v,
                               const std::vector<util::Coordinate> &node_coordinates,
                               const extractor::CompressedEdgeContainer &compressed_geometries,
                               const extractor::RestrictionMap &node_restriction_map,
-                              const std::unordered_set<NodeID> &barrier_nodes,
+                              const extractor::ObstacleMap &obstacle_nodes,
                               const extractor::TurnLanesIndexedArray &turn_lanes_data,
                               // output parameters
                               NodeID &result_node,
@@ -74,7 +74,7 @@ bool findPreviousIntersection(const NodeID node_v,
                                                          node_coordinates,
                                                          compressed_geometries,
                                                          node_restriction_map,
-                                                         barrier_nodes,
+                                                         obstacle_nodes,
                                                          turn_lanes_data,
                                                          {node_w, u_turn_at_node_w});
     // Continue along the straightmost turn. If there is no straight turn, we cannot find a valid
@@ -94,7 +94,7 @@ bool findPreviousIntersection(const NodeID node_v,
         node_coordinates,
         compressed_geometries,
         node_restriction_map,
-        barrier_nodes,
+        obstacle_nodes,
         turn_lanes_data,
         {node_v, straightmost_at_v_in_reverse->eid});
 
@@ -120,7 +120,7 @@ bool findPreviousIntersection(const NodeID node_v,
                                                           node_coordinates,
                                                           compressed_geometries,
                                                           node_restriction_map,
-                                                          barrier_nodes,
+                                                          obstacle_nodes,
                                                           turn_lanes_data,
                                                           {node_u, result_via_edge});
     const auto check_via_edge =
diff --git a/src/guidance/turn_handler.cpp b/src/guidance/turn_handler.cpp
index 0a77243604f..5672f62c7b5 100644
--- a/src/guidance/turn_handler.cpp
+++ b/src/guidance/turn_handler.cpp
@@ -110,7 +110,7 @@ TurnHandler::TurnHandler(const util::NodeBasedDynamicGraph &node_based_graph,
                          const std::vector<util::Coordinate> &coordinates,
                          const extractor::CompressedEdgeContainer &compressed_geometries,
                          const extractor::RestrictionMap &node_restriction_map,
-                         const std::unordered_set<NodeID> &barrier_nodes,
+                         const extractor::ObstacleMap &obstacle_nodes,
                          const extractor::TurnLanesIndexedArray &turn_lanes_data,
                          const extractor::NameTable &name_table,
                          const extractor::SuffixTable &street_name_suffix_table)
@@ -119,7 +119,7 @@ TurnHandler::TurnHandler(const util::NodeBasedDynamicGraph &node_based_graph,
                           coordinates,
                           compressed_geometries,
                           node_restriction_map,
-                          barrier_nodes,
+                          obstacle_nodes,
                           turn_lanes_data,
                           name_table,
                           street_name_suffix_table)
diff --git a/src/guidance/turn_lane_handler.cpp b/src/guidance/turn_lane_handler.cpp
index 19d246bc24e..8622d696cf0 100644
--- a/src/guidance/turn_lane_handler.cpp
+++ b/src/guidance/turn_lane_handler.cpp
@@ -34,14 +34,14 @@ TurnLaneHandler::TurnLaneHandler(const util::NodeBasedDynamicGraph &node_based_g
                                  const std::vector<util::Coordinate> &node_coordinates,
                                  const extractor::CompressedEdgeContainer &compressed_geometries,
                                  const extractor::RestrictionMap &node_restriction_map,
-                                 const std::unordered_set<NodeID> &barrier_nodes,
+                                 const extractor::ObstacleMap &obstacle_nodes,
                                  const extractor::TurnLanesIndexedArray &turn_lanes_data,
                                  extractor::LaneDescriptionMap &lane_description_map,
                                  const TurnAnalysis &turn_analysis,
                                  util::guidance::LaneDataIdMap &id_map)
     : node_based_graph(node_based_graph), node_data_container(node_data_container),
       node_coordinates(node_coordinates), compressed_geometries(compressed_geometries),
-      node_restriction_map(node_restriction_map), barrier_nodes(barrier_nodes),
+      node_restriction_map(node_restriction_map), obstacle_nodes(obstacle_nodes),
       turn_lanes_data(turn_lanes_data), lane_description_map(lane_description_map),
       turn_analysis(turn_analysis), id_map(id_map)
 {
@@ -213,7 +213,7 @@ TurnLaneScenario TurnLaneHandler::deduceScenario(const NodeID at,
                                  node_coordinates,
                                  compressed_geometries,
                                  node_restriction_map,
-                                 barrier_nodes,
+                                 obstacle_nodes,
                                  turn_lanes_data,
                                  previous_node,
                                  previous_via_edge,
@@ -268,7 +268,7 @@ TurnLaneScenario TurnLaneHandler::deduceScenario(const NodeID at,
         return TurnLaneScenario::MERGE;
 
     // Dead end streets that don't have any left-tag. This can happen due to the fallbacks for
-    // broken data/barriers.
+    // broken data or obstacles.
     const bool has_non_usable_u_turn = (intersection[0].entry_allowed &&
                                         !hasTag(TurnLaneType::none | TurnLaneType::left |
                                                     TurnLaneType::sharp_left | TurnLaneType::uturn,
@@ -579,7 +579,7 @@ std::pair<LaneDataVector, LaneDataVector> TurnLaneHandler::partitionLaneData(
                                                           node_coordinates,
                                                           compressed_geometries,
                                                           node_restriction_map,
-                                                          barrier_nodes,
+                                                          obstacle_nodes,
                                                           turn_lanes_data,
                                                           {at, straightmost->eid}));
 
diff --git a/taginfo.json b/taginfo.json
index dfe48f96a6f..c2bb0367d30 100644
--- a/taginfo.json
+++ b/taginfo.json
@@ -124,8 +124,21 @@
         {"key": "side_road", "value": "yes", "description": "gets speed penalty"},
         {"key": "side_road", "value": "rotary", "description": "gets speed penalty"},
         {"key": "route", "object_types": ["way"]},
-        {"key": "highway", "value": "traffic_signals", "object_types": ["node"]},
         {"key": "highway", "value": "crossing", "object_types": ["node"]},
+        {"key": "highway", "value": "give_way", "object_types": ["node"]},
+        {"key": "highway", "value": "mini_roundabout", "object_types": ["node"]},
+        {"key": "highway", "value": "stop", "object_types": ["node"]},
+        {"key": "highway", "value": "traffic_calming", "object_types": ["node"]},
+        {"key": "highway", "value": "traffic_signals", "object_types": ["node"]},
+        {"key": "highway", "value": "turning_circle", "object_types": ["node"]},
+        {"key": "highway", "value": "turning_loop", "object_types": ["node"]},
+        {"key": "direction", "value": "forward", "object_types": ["node"]},
+        {"key": "direction", "value": "backward", "object_types": ["node"]},
+        {"key": "direction", "value": "both", "object_types": ["node"]},
+        {"key": "traffic_signals:direction", "value": "forward", "object_types": ["node"]},
+        {"key": "traffic_signals:direction", "value": "backward", "object_types": ["node"]},
+        {"key": "traffic_signals:direction", "value": "both", "object_types": ["node"]},
+        {"key": "stop", "value": "minor", "object_types": ["node"]},
         {"key": "access", "value": "yes"},
         {"key": "access", "value": "motorcar"},
         {"key": "access", "value": "motor_vehicle"},
diff --git a/test/data/profiles/bad_node.lua b/test/data/profiles/bad_node.lua
index 74c730587bc..fd974799841 100644
--- a/test/data/profiles/bad_node.lua
+++ b/test/data/profiles/bad_node.lua
@@ -9,8 +9,8 @@ function setup()
       max_speed_for_map_matching    = 30/3.6, --km -> m/s
       weight_name                   = 'duration',
       process_call_tagless_node     = false,
-      u_turn_penalty                 = 20,
-      traffic_light_penalty         = 7,     -- seconds
+      u_turn_penalty                = 20,
+      traffic_signal_penalty        = 7,     -- seconds
       use_turn_restrictions         = true
     },
 
@@ -128,7 +128,7 @@ function process_turn (profile, turn)
     turn.weight = turn.weight + profile.properties.u_turn_penalty
   end
   if turn.has_traffic_light then
-     turn.duration = turn.duration + profile.properties.traffic_light_penalty
+     turn.duration = turn.duration + profile.properties.traffic_signal_penalty
   end
 end
 
diff --git a/test/data/profiles/bad_segment.lua b/test/data/profiles/bad_segment.lua
index cbb5bac1932..3c33bb542c8 100644
--- a/test/data/profiles/bad_segment.lua
+++ b/test/data/profiles/bad_segment.lua
@@ -9,8 +9,8 @@ function setup()
       max_speed_for_map_matching    = 30/3.6, --km -> m/s
       weight_name                   = 'duration',
       process_call_tagless_node     = false,
-      u_turn_penalty                 = 20,
-      traffic_light_penalty         = 7,     -- seconds
+      u_turn_penalty                = 20,
+      traffic_signal_penalty        = 7,     -- seconds
       use_turn_restrictions         = true
     },
 
@@ -124,7 +124,7 @@ function process_turn (profile, turn)
     turn.weight = turn.weight + profile.properties.u_turn_penalty
   end
   if turn.has_traffic_light then
-     turn.duration = turn.duration + profile.properties.traffic_light_penalty
+     turn.duration = turn.duration + profile.properties.traffic_signal_penalty
   end
 end
 
diff --git a/test/data/profiles/bad_setup.lua b/test/data/profiles/bad_setup.lua
index 72e32cc40ce..2604ca85f03 100644
--- a/test/data/profiles/bad_setup.lua
+++ b/test/data/profiles/bad_setup.lua
@@ -13,8 +13,8 @@ function setup()
       max_speed_for_map_matching    = 30/3.6, --km -> m/s
       weight_name                   = 'duration',
       process_call_tagless_node     = false,
-      u_turn_penalty                 = 20,
-      traffic_light_penalty         = 7,     -- seconds
+      u_turn_penalty                = 20,
+      traffic_signal_penalty        = 7,     -- seconds
       use_turn_restrictions         = true
     },
 
@@ -128,7 +128,7 @@ function process_turn (profile, turn)
     turn.weight = turn.weight + profile.properties.u_turn_penalty
   end
   if turn.has_traffic_light then
-     turn.duration = turn.duration + profile.properties.traffic_light_penalty
+     turn.duration = turn.duration + profile.properties.traffic_signal_penalty
   end
 end
 
diff --git a/test/data/profiles/bad_turn.lua b/test/data/profiles/bad_turn.lua
index 0fecb7d2d07..79e25b29587 100644
--- a/test/data/profiles/bad_turn.lua
+++ b/test/data/profiles/bad_turn.lua
@@ -9,8 +9,8 @@ function setup()
       max_speed_for_map_matching    = 30/3.6, --km -> m/s
       weight_name                   = 'duration',
       process_call_tagless_node     = false,
-      u_turn_penalty                 = 20,
-      traffic_light_penalty         = 7,     -- seconds
+      u_turn_penalty                = 20,
+      traffic_signal_penalty        = 7,     -- seconds
       use_turn_restrictions         = true
     },
 
@@ -128,7 +128,7 @@ function process_turn (profile, turn)
     turn.weight = turn.weight + profile.properties.u_turn_penalty
   end
   if turn.has_traffic_light then
-     turn.duration = turn.duration + profile.properties.traffic_light_penalty
+     turn.duration = turn.duration + profile.properties.traffic_signal_penalty
   end
 end
 
diff --git a/test/data/profiles/bad_way.lua b/test/data/profiles/bad_way.lua
index cb0a3eb7206..f3cc6804ab4 100644
--- a/test/data/profiles/bad_way.lua
+++ b/test/data/profiles/bad_way.lua
@@ -9,8 +9,8 @@ function setup()
       max_speed_for_map_matching    = 30/3.6, --km -> m/s
       weight_name                   = 'duration',
       process_call_tagless_node     = false,
-      u_turn_penalty                 = 20,
-      traffic_light_penalty         = 7,     -- seconds
+      u_turn_penalty                = 20,
+      traffic_signal_penalty        = 7,     -- seconds
       use_turn_restrictions         = true
     },
 
@@ -128,7 +128,7 @@ function process_turn (profile, turn)
     turn.weight = turn.weight + profile.properties.u_turn_penalty
   end
   if turn.has_traffic_light then
-     turn.duration = turn.duration + profile.properties.traffic_light_penalty
+     turn.duration = turn.duration + profile.properties.traffic_signal_penalty
   end
 end
 
diff --git a/unit_tests/extractor/graph_compressor.cpp b/unit_tests/extractor/graph_compressor.cpp
index f70d71b4f91..c12b48a3646 100644
--- a/unit_tests/extractor/graph_compressor.cpp
+++ b/unit_tests/extractor/graph_compressor.cpp
@@ -64,8 +64,6 @@ BOOST_AUTO_TEST_CASE(long_road_test)
     //
     GraphCompressor compressor;
 
-    std::unordered_set<NodeID> barrier_nodes;
-    TrafficSignals traffic_lights;
     std::vector<TurnRestriction> restrictions;
     std::vector<NodeBasedEdgeAnnotation> annotations(1);
     CompressedEdgeContainer container;
@@ -86,14 +84,8 @@ BOOST_AUTO_TEST_CASE(long_road_test)
     BOOST_CHECK(compatible(graph, annotations, 2, 4));
     BOOST_CHECK(compatible(graph, annotations, 4, 6));
 
-    compressor.Compress(barrier_nodes,
-                        traffic_lights,
-                        scripting_environment,
-                        restrictions,
-                        maneuver_overrides,
-                        graph,
-                        annotations,
-                        container);
+    compressor.Compress(
+        scripting_environment, restrictions, maneuver_overrides, graph, annotations, container);
     BOOST_CHECK_EQUAL(graph.FindEdge(0, 1), SPECIAL_EDGEID);
     BOOST_CHECK_EQUAL(graph.FindEdge(1, 2), SPECIAL_EDGEID);
     BOOST_CHECK_EQUAL(graph.FindEdge(2, 3), SPECIAL_EDGEID);
@@ -110,8 +102,6 @@ BOOST_AUTO_TEST_CASE(loop_test)
     //
     GraphCompressor compressor;
 
-    std::unordered_set<NodeID> barrier_nodes;
-    TrafficSignals traffic_lights;
     std::vector<TurnRestriction> restrictions;
     CompressedEdgeContainer container;
     std::vector<NodeBasedEdgeAnnotation> annotations(1);
@@ -146,14 +136,8 @@ BOOST_AUTO_TEST_CASE(loop_test)
     BOOST_CHECK(compatible(graph, annotations, 10, 11));
     BOOST_CHECK(compatible(graph, annotations, 11, 0));
 
-    compressor.Compress(barrier_nodes,
-                        traffic_lights,
-                        scripting_environment,
-                        restrictions,
-                        maneuver_overrides,
-                        graph,
-                        annotations,
-                        container);
+    compressor.Compress(
+        scripting_environment, restrictions, maneuver_overrides, graph, annotations, container);
 
     BOOST_CHECK_EQUAL(graph.FindEdge(5, 0), SPECIAL_EDGEID);
     BOOST_CHECK_EQUAL(graph.FindEdge(0, 1), SPECIAL_EDGEID);
@@ -173,8 +157,6 @@ BOOST_AUTO_TEST_CASE(t_intersection)
     //
     GraphCompressor compressor;
 
-    std::unordered_set<NodeID> barrier_nodes;
-    TrafficSignals traffic_lights;
     std::vector<NodeBasedEdgeAnnotation> annotations(1);
     std::vector<TurnRestriction> restrictions;
     CompressedEdgeContainer container;
@@ -195,14 +177,8 @@ BOOST_AUTO_TEST_CASE(t_intersection)
     BOOST_CHECK(compatible(graph, annotations, 3, 4));
     BOOST_CHECK(compatible(graph, annotations, 4, 5));
 
-    compressor.Compress(barrier_nodes,
-                        traffic_lights,
-                        scripting_environment,
-                        restrictions,
-                        maneuver_overrides,
-                        graph,
-                        annotations,
-                        container);
+    compressor.Compress(
+        scripting_environment, restrictions, maneuver_overrides, graph, annotations, container);
 
     BOOST_CHECK(graph.FindEdge(0, 1) != SPECIAL_EDGEID);
     BOOST_CHECK(graph.FindEdge(1, 2) != SPECIAL_EDGEID);
@@ -216,8 +192,6 @@ BOOST_AUTO_TEST_CASE(street_name_changes)
     //
     GraphCompressor compressor;
 
-    std::unordered_set<NodeID> barrier_nodes;
-    TrafficSignals traffic_lights;
     std::vector<NodeBasedEdgeAnnotation> annotations(2);
     std::vector<TurnRestriction> restrictions;
     CompressedEdgeContainer container;
@@ -234,14 +208,8 @@ BOOST_AUTO_TEST_CASE(street_name_changes)
     BOOST_CHECK(compatible(graph, annotations, 0, 1));
     BOOST_CHECK(compatible(graph, annotations, 2, 3));
 
-    compressor.Compress(barrier_nodes,
-                        traffic_lights,
-                        scripting_environment,
-                        restrictions,
-                        maneuver_overrides,
-                        graph,
-                        annotations,
-                        container);
+    compressor.Compress(
+        scripting_environment, restrictions, maneuver_overrides, graph, annotations, container);
 
     BOOST_CHECK(graph.FindEdge(0, 1) != SPECIAL_EDGEID);
     BOOST_CHECK(graph.FindEdge(1, 2) != SPECIAL_EDGEID);
@@ -254,8 +222,6 @@ BOOST_AUTO_TEST_CASE(direction_changes)
     //
     GraphCompressor compressor;
 
-    std::unordered_set<NodeID> barrier_nodes;
-    TrafficSignals traffic_lights;
     std::vector<NodeBasedEdgeAnnotation> annotations(1);
     std::vector<TurnRestriction> restrictions;
     CompressedEdgeContainer container;
@@ -268,14 +234,8 @@ BOOST_AUTO_TEST_CASE(direction_changes)
     edges[1].data.reversed = true;
 
     Graph graph(5, edges);
-    compressor.Compress(barrier_nodes,
-                        traffic_lights,
-                        scripting_environment,
-                        restrictions,
-                        maneuver_overrides,
-                        graph,
-                        annotations,
-                        container);
+    compressor.Compress(
+        scripting_environment, restrictions, maneuver_overrides, graph, annotations, container);
 
     BOOST_CHECK(graph.FindEdge(0, 1) != SPECIAL_EDGEID);
     BOOST_CHECK(graph.FindEdge(1, 2) != SPECIAL_EDGEID);
diff --git a/unit_tests/extractor/intersection_analysis_tests.cpp b/unit_tests/extractor/intersection_analysis_tests.cpp
index 947359c598a..79b261ac74c 100644
--- a/unit_tests/extractor/intersection_analysis_tests.cpp
+++ b/unit_tests/extractor/intersection_analysis_tests.cpp
@@ -18,8 +18,6 @@ using Graph = util::NodeBasedDynamicGraph;
 
 BOOST_AUTO_TEST_CASE(simple_intersection_connectivity)
 {
-    std::unordered_set<NodeID> barrier_nodes{6};
-    TrafficSignals traffic_lights;
     std::vector<NodeBasedEdgeAnnotation> annotations{
         {EMPTY_NAMEID, 0, INAVLID_CLASS_DATA, TRAVEL_MODE_DRIVING, false},
         {EMPTY_NAMEID, 1, INAVLID_CLASS_DATA, TRAVEL_MODE_DRIVING, false}};
@@ -27,6 +25,7 @@ BOOST_AUTO_TEST_CASE(simple_intersection_connectivity)
     CompressedEdgeContainer container;
     test::MockScriptingEnvironment scripting_environment;
     std::vector<UnresolvedManeuverOverride> maneuver_overrides;
+    scripting_environment.m_obstacle_map.emplace(SPECIAL_NODEID, 6, {Obstacle::Type::Barrier});
 
     TurnLanesIndexedArray turn_lanes_data{{0, 0, 3},
                                           {TurnLaneType::uturn | TurnLaneType::left,
@@ -86,14 +85,8 @@ BOOST_AUTO_TEST_CASE(simple_intersection_connectivity)
 
     Graph graph(8, edges);
 
-    GraphCompressor().Compress(barrier_nodes,
-                               traffic_lights,
-                               scripting_environment,
-                               restrictions,
-                               maneuver_overrides,
-                               graph,
-                               annotations,
-                               container);
+    GraphCompressor().Compress(
+        scripting_environment, restrictions, maneuver_overrides, graph, annotations, container);
 
     REQUIRE_SIZE_RANGE(getIncomingEdges(graph, 2), 3);
     REQUIRE_SIZE_RANGE(getOutgoingEdges(graph, 2), 4);
@@ -115,7 +108,7 @@ BOOST_AUTO_TEST_CASE(simple_intersection_connectivity)
                 result.push_back(isTurnAllowed(graph,
                                                node_data_container,
                                                restriction_map,
-                                               barrier_nodes,
+                                               scripting_environment.m_obstacle_map,
                                                edge_geometries,
                                                turn_lanes_data,
                                                incoming_edge,
@@ -153,8 +146,6 @@ BOOST_AUTO_TEST_CASE(simple_intersection_connectivity)
 
 BOOST_AUTO_TEST_CASE(roundabout_intersection_connectivity)
 {
-    std::unordered_set<NodeID> barrier_nodes;
-    TrafficSignals traffic_lights;
     std::vector<NodeBasedEdgeAnnotation> annotations;
     std::vector<TurnRestriction> restrictions;
     CompressedEdgeContainer container;
@@ -211,14 +202,8 @@ BOOST_AUTO_TEST_CASE(roundabout_intersection_connectivity)
 
     Graph graph(7, edges);
 
-    GraphCompressor().Compress(barrier_nodes,
-                               traffic_lights,
-                               scripting_environment,
-                               restrictions,
-                               maneuver_overrides,
-                               graph,
-                               annotations,
-                               container);
+    GraphCompressor().Compress(
+        scripting_environment, restrictions, maneuver_overrides, graph, annotations, container);
 
     REQUIRE_SIZE_RANGE(getIncomingEdges(graph, 0), 3);
     REQUIRE_SIZE_RANGE(getOutgoingEdges(graph, 0), 6);
@@ -241,7 +226,7 @@ BOOST_AUTO_TEST_CASE(roundabout_intersection_connectivity)
                 result.push_back(isTurnAllowed(graph,
                                                node_data_container,
                                                restriction_map,
-                                               barrier_nodes,
+                                               scripting_environment.m_obstacle_map,
                                                edge_geometries,
                                                turn_lanes_data,
                                                incoming_edge,
@@ -262,13 +247,14 @@ BOOST_AUTO_TEST_CASE(roundabout_intersection_connectivity)
 
 BOOST_AUTO_TEST_CASE(skip_degree_two_nodes)
 {
-    std::unordered_set<NodeID> barrier_nodes{1};
-    TrafficSignals traffic_lights = {{2}, {}};
     std::vector<NodeBasedEdgeAnnotation> annotations(1);
     std::vector<TurnRestriction> restrictions;
     CompressedEdgeContainer container;
     test::MockScriptingEnvironment scripting_environment;
     std::vector<UnresolvedManeuverOverride> maneuver_overrides;
+    scripting_environment.m_obstacle_map.emplace(SPECIAL_NODEID, 1, {Obstacle::Type::Barrier});
+    scripting_environment.m_obstacle_map.emplace(
+        SPECIAL_NODEID, 2, {Obstacle::Type::TrafficSignals});
 
     TurnLanesIndexedArray turn_lanes_data;
 
@@ -312,14 +298,8 @@ BOOST_AUTO_TEST_CASE(skip_degree_two_nodes)
 
     Graph graph(10, edges);
 
-    GraphCompressor().Compress(barrier_nodes,
-                               traffic_lights,
-                               scripting_environment,
-                               restrictions,
-                               maneuver_overrides,
-                               graph,
-                               annotations,
-                               container);
+    GraphCompressor().Compress(
+        scripting_environment, restrictions, maneuver_overrides, graph, annotations, container);
 
     BOOST_CHECK_EQUAL(graph.GetTarget(skipDegreeTwoNodes(graph, {0, 0}).edge), 4);
     BOOST_CHECK_EQUAL(graph.GetTarget(skipDegreeTwoNodes(graph, {4, 7}).edge), 0);
diff --git a/unit_tests/library/extract.cpp b/unit_tests/library/extract.cpp
index 768331c1c22..c4f1f0354f8 100644
--- a/unit_tests/library/extract.cpp
+++ b/unit_tests/library/extract.cpp
@@ -135,28 +135,24 @@ BOOST_AUTO_TEST_CASE(test_segment_runtime_error)
     BOOST_CHECK(boost::algorithm::contains(
         output.str(), "bad_segment.lua:132: attempt to compare number with nil"));
 }
-// NOTE: THIS TEST IS COMMENTED OUT BECAUSE IT FAILS
-// BOOST_AUTO_TEST_CASE(test_turn_runtime_error)
-// {
-//     osrm::ExtractorConfig config;
-//     config.input_path = OSRM_TEST_DATA_DIR "/monaco.osm.pbf";
-//     config.UseDefaultOutputNames(OSRM_TEST_DATA_DIR "/monaco.osm.pbf");
-//     config.profile_path = OSRM_TEST_DATA_DIR "/profiles/bad_turn.lua";
-//     config.small_component_size = 1000;
-//     config.requested_num_threads = std::thread::hardware_concurrency();
-
-//     std::stringstream output;
-
-//     {
-//         redirect_stderr redir(output.rdbuf());
-//         BOOST_CHECK_THROW(osrm::extract(config), osrm::util::exception);
-//     }
-
-//     // We just look for the line number, file name, and error message. This avoids portability
-//     // issues since the output contains the full path to the file, which may change between
-//     systems BOOST_CHECK(boost::algorithm::contains(output.str(),
-//                                            "bad_turn.lua:122: attempt to compare number with
-//                                            nil"));
-// }
+
+BOOST_AUTO_TEST_CASE(test_turn_runtime_error)
+{
+    osrm::ExtractorConfig config;
+    config.input_path = OSRM_TEST_DATA_DIR "/monaco.osm.pbf";
+    config.UseDefaultOutputNames(OSRM_TEST_DATA_DIR "/monaco.osm.pbf");
+    config.profile_path = OSRM_TEST_DATA_DIR "/profiles/bad_turn.lua";
+    config.small_component_size = 1000;
+    config.requested_num_threads = std::thread::hardware_concurrency();
+    std::stringstream output;
+    {
+        redirect_stderr redir(output.rdbuf());
+        BOOST_CHECK_THROW(osrm::extract(config), osrm::util::exception);
+    }
+    // We just look for the line number, file name, and error message. This avoids portability
+    // issues since the output contains the full path to the file, which may change between systems
+    BOOST_CHECK(boost::algorithm::contains(output.str(),
+                                           "bad_turn.lua:122: attempt to compare number with nil"));
+}
 
 BOOST_AUTO_TEST_SUITE_END()