From a2c1ac91afb1365623b5414ba52a3eb75e24e4d7 Mon Sep 17 00:00:00 2001 From: Michael Bell Date: Tue, 30 Aug 2022 10:36:49 +0100 Subject: [PATCH] Support OSM traffic signal directions (#6153) Currently OSRM parses traffic signal nodes without consideration for the direction in which the signal applies. This can lead to duplicated routing penalties, especially when a forward and backward signal are in close proximity on a way. This commit adds support for directed signals to the extraction and graph creation. Signal penalties are only applied in the direction specified by the OSM tag. We add the assignment of traffic directions to the lua scripts, maintaining backwards compatibility with the existing boolean traffic states. As part of the changes to the internal structures used for tracking traffic signals during extraction, we stop serialising/deserialising signals to the `.osrm` file. The traffic signals are only used by `osrm-extract` so whilst this is a data format change, it will not break any existing user processes. --- CHANGELOG.md | 1 + features/car/traffic_light_penalties.feature | 219 +++++++++++++++++- .../extractor/edge_based_graph_factory.hpp | 5 +- include/extractor/extraction_containers.hpp | 14 +- include/extractor/extraction_node.hpp | 12 +- include/extractor/extractor.hpp | 6 +- include/extractor/files.hpp | 5 +- include/extractor/graph_compressor.hpp | 3 +- .../extractor/node_based_graph_factory.hpp | 9 +- include/extractor/traffic_lights.hpp | 25 ++ include/extractor/traffic_signals.hpp | 28 +++ profiles/bicycle.lua | 6 +- profiles/car.lua | 6 +- profiles/foot.lua | 1 + profiles/lib/traffic_signal.lua | 26 +++ profiles/testbot.lua | 10 +- src/extractor/edge_based_graph_factory.cpp | 38 +-- src/extractor/extraction_containers.cpp | 153 ++++++++++-- src/extractor/extractor.cpp | 24 +- src/extractor/extractor_callbacks.cpp | 4 +- src/extractor/graph_compressor.cpp | 117 +++++++--- src/extractor/node_based_graph_factory.cpp | 18 +- src/extractor/scripting_environment_lua.cpp | 40 +++- src/tools/components.cpp | 2 +- unit_tests/extractor/graph_compressor.cpp | 11 +- .../extractor/intersection_analysis_tests.cpp | 6 +- 26 files changed, 649 insertions(+), 140 deletions(-) create mode 100644 include/extractor/traffic_lights.hpp create mode 100644 include/extractor/traffic_signals.hpp create mode 100644 profiles/lib/traffic_signal.lua diff --git a/CHANGELOG.md b/CHANGELOG.md index 0241464a0d5..ecb4109c6a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,7 @@ - FIXED: Improvements to maneuver override processing [#6125](https://github.com/Project-OSRM/osrm-backend/pull/6125) - ADDED: Support snapping to multiple ways at an input location. [#5953](https://github.com/Project-OSRM/osrm-backend/pull/5953) - FIXED: Fix snapping target locations to ways used in turn restrictions. [#6339](https://github.com/Project-OSRM/osrm-backend/pull/6339) + - ADDED: Support OSM traffic signal directions. [#6153](https://github.com/Project-OSRM/osrm-backend/pull/6153) # 5.26.0 - Changes from 5.25.0 diff --git a/features/car/traffic_light_penalties.feature b/features/car/traffic_light_penalties.feature index e8961abb101..37bbad3a770 100644 --- a/features/car/traffic_light_penalties.feature +++ b/features/car/traffic_light_penalties.feature @@ -39,7 +39,113 @@ Feature: Car - Handle traffic lights | k | n | 20.7s | turn with traffic light | - Scenario: Tarrif Signal Geometry + Scenario: Car - Traffic signal direction + Given the node map + """ + a-1-b-2-c + + d-3-e-4-f + + g-5-h-6-i + + j-7-k-8-l + + """ + + And the ways + | nodes | highway | + | abc | primary | + | def | primary | + | ghi | primary | + | jkl | primary | + + And the nodes + | node | highway | traffic_signals:direction | + | e | traffic_signals | | + | h | traffic_signals | forward | + | k | traffic_signals | backward | + + When I route I should get + | from | to | time | # | + | 1 | 2 | 11.1s | no turn with no traffic light | + | 2 | 1 | 11.1s | no turn with no traffic light | + | 3 | 4 | 13.1s | no turn with traffic light | + | 4 | 3 | 13.1s | no turn with traffic light | + | 5 | 6 | 13.1s | no turn with traffic light | + | 6 | 5 | 11.1s | no turn with no traffic light | + | 7 | 8 | 11.1s | no turn with no traffic light | + | 8 | 7 | 13.1s | no turn with traffic light | + + + Scenario: Car - Encounters a traffic light + 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 | primary | + | hgi | primary | + | fgj | primary | + | lmn | primary | + | kmo | primary | + + And the nodes + | node | highway | traffic_signals:direction | + | g | traffic_signals | forward | + | m | traffic_signals | backward | + + + When I route I should get + | from | to | time | # | + | a | d | 21.9s | no turn with no traffic light | + | a | e | 22.2s | no turn with traffic light | + | a | b | 18.7s | turn with no traffic light | + | e | b | 21.9s | no turn with no traffic light | + | e | a | 22.2s | no turn with traffic light | + | e | d | 18.7s | turn with no traffic light | + | d | e | 21.9s | no turn with no traffic light | + | d | b | 11s | no turn with traffic light | + | d | a | 18.7s | turn with no traffic light | + | b | a | 21.9s | no turn with no traffic light | + | b | d | 11s | no turn with traffic light | + | b | e | 18.7s | turn with no traffic light | + + | f | i | 23.9s | no turn with no traffic light | + | f | j | 24.2s | no turn with traffic light | + | f | h | 20.7s | turn with no traffic light | + | j | h | 21.9s | no turn with no traffic light | + | j | f | 22.2s | no turn with traffic light | + | j | i | 18.7s | turn with no traffic light | + | i | j | 21.9s | no turn with no traffic light | + | i | h | 11s | no turn with traffic light | + | i | f | 18.7s | turn with no traffic light | + | h | f | 23.9s | no turn with no traffic light | + | h | i | 13s | no turn with traffic light | + | h | j | 20.7s | turn with no traffic light | + + | k | n | 21.9s | no turn with no traffic light | + | k | o | 22.2s | no turn with traffic light | + | k | l | 18.7s | turn with no traffic light | + | o | l | 23.9s | no turn with no traffic light | + | o | k | 24.2s | no turn with traffic light | + | o | n | 20.7s | turn with no traffic light | + | n | o | 23.9s | no turn with no traffic light | + | n | l | 13s | no turn with traffic light | + | n | k | 20.7s | turn with no traffic light | + | l | k | 21.9s | no turn with no traffic light | + | l | n | 11s | no turn with traffic light | + | l | o | 18.7s | turn with no traffic light | + + + Scenario: Traffic Signal Geometry Given the query options | overview | full | | geometries | polyline | @@ -61,6 +167,53 @@ Feature: Car - Handle traffic lights | from | to | route | geometry | | a | c | abc,abc | _ibE_ibE?gJ?eJ | + + Scenario: Traffic Signal Geometry - forward signal + Given the query options + | overview | full | + | geometries | polyline | + + Given the node map + """ + a - b - c + """ + + And the ways + | nodes | highway | + | abc | primary | + + And the nodes + | node | highway | traffic_signals:direction | + | b | traffic_signals | forward | + + When I route I should get + | from | to | route | geometry | + | a | c | abc,abc | _ibE_ibE?gJ?eJ | + + + Scenario: Traffic Signal Geometry - reverse signal + Given the query options + | overview | full | + | geometries | polyline | + + Given the node map + """ + a - b - c + """ + + And the ways + | nodes | highway | + | abc | primary | + + And the nodes + | node | highway | traffic_signals:direction | + | b | traffic_signals | reverse | + + When I route I should get + | from | to | route | geometry | + | a | c | abc,abc | _ibE_ibE?gJ?eJ | + + @traffic Scenario: Traffic update on the edge with a traffic signal Given the node map @@ -91,3 +244,67 @@ Feature: Car - Handle traffic lights | from | to | route | speed | weights | time | distances | a:datasources | a:nodes | a:speed | a:duration | a:weight | | a | c | abc,abc | 60 km/h | 24.2,0 | 24.2s | 400m,0m | 1:0 | 1:2:3 | 18:18 | 11.1:11.1 | 11.1:11.1 | | c | a | abc,abc | 60 km/h | 24.2,0 | 24.2s | 400m,0m | 0:1 | 3:2:1 | 18:18 | 11.1:11.1 | 11.1:11.1 | + + + @traffic + Scenario: Traffic update on the edge with a traffic signal - forward + Given the node map + """ + a - b - c + """ + + And the ways + | nodes | highway | + | abc | primary | + + + And the nodes + | node | highway | traffic_signals:direction | + | b | traffic_signals | forward | + + And the contract extra arguments "--segment-speed-file {speeds_file}" + And the customize extra arguments "--segment-speed-file {speeds_file}" + And the speed file + """ + 1,2,65 + 2,1,65 + """ + And the query options + | annotations | datasources,nodes,speed,duration,weight | + + When I route I should get + | from | to | route | speed | weights | time | distances | a:datasources | a:nodes | a:speed | a:duration | a:weight | + | a | c | abc,abc | 60 km/h | 24.2,0 | 24.2s | 400m,0m | 1:0 | 1:2:3 | 18:18 | 11.1:11.1 | 11.1:11.1 | + | c | a | abc,abc | 65 km/h | 22.2,0 | 22.2s | 400m,0m | 0:1 | 3:2:1 | 18:18 | 11.1:11.1 | 11.1:11.1 | + + + @traffic + Scenario: Traffic update on the edge with a traffic signal - backward + Given the node map + """ + a - b - c + """ + + And the ways + | nodes | highway | + | abc | primary | + + + And the nodes + | node | highway | traffic_signals:direction | + | b | traffic_signals | backward | + + And the contract extra arguments "--segment-speed-file {speeds_file}" + And the customize extra arguments "--segment-speed-file {speeds_file}" + And the speed file + """ + 1,2,65 + 2,1,65 + """ + And the query options + | annotations | datasources,nodes,speed,duration,weight | + + When I route I should get + | from | to | route | speed | weights | time | distances | a:datasources | a:nodes | a:speed | a:duration | a:weight | + | a | c | abc,abc | 65 km/h | 22.2,0 | 22.2s | 400m,0m | 1:0 | 1:2:3 | 18:18 | 11.1:11.1 | 11.1:11.1 | + | c | a | abc,abc | 60 km/h | 24.2,0 | 24.2s | 400m,0m | 0:1 | 3:2:1 | 18:18 | 11.1:11.1 | 11.1:11.1 | diff --git a/include/extractor/edge_based_graph_factory.hpp b/include/extractor/edge_based_graph_factory.hpp index 01a3122ea7f..9dc0cadf215 100644 --- a/include/extractor/edge_based_graph_factory.hpp +++ b/include/extractor/edge_based_graph_factory.hpp @@ -23,6 +23,7 @@ #include "util/typedefs.hpp" #include "storage/io.hpp" +#include "traffic_signals.hpp" #include #include @@ -68,7 +69,7 @@ class EdgeBasedGraphFactory EdgeBasedNodeDataContainer &node_data_container, const CompressedEdgeContainer &compressed_edge_container, const std::unordered_set &barrier_nodes, - const std::unordered_set &traffic_lights, + const TrafficSignals &traffic_signals, const std::vector &coordinates, const NameTable &name_table, const std::unordered_set &segregated_edges, @@ -134,7 +135,7 @@ class EdgeBasedGraphFactory const util::NodeBasedDynamicGraph &m_node_based_graph; const std::unordered_set &m_barrier_nodes; - const std::unordered_set &m_traffic_lights; + 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 a8b8f776766..b630763fbf9 100644 --- a/include/extractor/extraction_containers.hpp +++ b/include/extractor/extraction_containers.hpp @@ -8,8 +8,11 @@ #include "extractor/scripting_environment.hpp" #include "storage/tar_fwd.hpp" +#include "traffic_lights.hpp" +#include "traffic_signals.hpp" #include +#include namespace osrm { @@ -25,15 +28,19 @@ namespace extractor class ExtractionContainers { using ReferencedWays = std::unordered_map; + using ReferencedTrafficSignals = + std::pair, std::unordered_multimap>; // The relationship between way and nodes is lost during node preparation. - // We identify the ways and nodes relevant to restrictions/overrides prior to + // We identify the ways and nodes relevant to restrictions/overrides/signals 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 WriteNodes(storage::tar::FileWriter &file_out) const; @@ -50,9 +57,9 @@ class ExtractionContainers using NameOffsets = std::vector; using WayIDVector = std::vector; using WayNodeIDOffsets = std::vector; + using InputTrafficSignal = std::pair; std::vector barrier_nodes; - std::vector traffic_signals; NodeIDVector used_node_id_list; NodeVector all_nodes_list; EdgeVector all_edges_list; @@ -65,6 +72,9 @@ class ExtractionContainers unsigned max_internal_node_id; + std::vector external_traffic_signals; + TrafficSignals internal_traffic_signals; + // List of restrictions (conditional and unconditional) before we transform them into the // output types. Input containers reference OSMNodeIDs. We can only transform them to the // correct internal IDs after we've read everything. Without a multi-parse approach, diff --git a/include/extractor/extraction_node.hpp b/include/extractor/extraction_node.hpp index 065ff4a3f0d..9a146d4d3cc 100644 --- a/include/extractor/extraction_node.hpp +++ b/include/extractor/extraction_node.hpp @@ -1,6 +1,8 @@ #ifndef EXTRACTION_NODE_HPP #define EXTRACTION_NODE_HPP +#include "traffic_lights.hpp" + namespace osrm { namespace extractor @@ -8,9 +10,13 @@ namespace extractor struct ExtractionNode { - ExtractionNode() : traffic_lights(false), barrier(false) {} - void clear() { traffic_lights = barrier = false; } - bool traffic_lights; + ExtractionNode() : traffic_lights(TrafficLightClass::NONE), barrier(false) {} + void clear() + { + traffic_lights = TrafficLightClass::NONE; + barrier = false; + } + TrafficLightClass::Direction traffic_lights; bool barrier; }; } // namespace extractor diff --git a/include/extractor/extractor.hpp b/include/extractor/extractor.hpp index 070a3b4ef59..fc103fe6972 100644 --- a/include/extractor/extractor.hpp +++ b/include/extractor/extractor.hpp @@ -43,6 +43,7 @@ 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 @@ -64,7 +65,8 @@ class Extractor std::tuple, - std::vector> + std::vector, + TrafficSignals> ParseOSMData(ScriptingEnvironment &scripting_environment, const unsigned number_of_threads); EdgeID BuildEdgeExpandedGraph( @@ -73,7 +75,7 @@ class Extractor const std::vector &coordinates, const CompressedEdgeContainer &compressed_edge_container, const std::unordered_set &barrier_nodes, - const std::unordered_set &traffic_lights, + const TrafficSignals &traffic_signals, const RestrictionGraph &restriction_graph, const std::unordered_set &segregated_edges, const NameTable &name_table, diff --git a/include/extractor/files.hpp b/include/extractor/files.hpp index 6c0445e9b45..5c71e9de9f9 100644 --- a/include/extractor/files.hpp +++ b/include/extractor/files.hpp @@ -444,10 +444,9 @@ inline void readConditionalRestrictions(const boost::filesystem::path &path, } // reads .osrm file which is a temporary file of osrm-extract -template +template void readRawNBGraph(const boost::filesystem::path &path, BarrierOutIter barriers, - TrafficSignalsOutIter traffic_signals, std::vector &coordinates, PackedOSMIDsT &osm_node_ids, std::vector &edge_list, @@ -471,8 +470,6 @@ void readRawNBGraph(const boost::filesystem::path &path, reader.ReadStreaming("/extractor/barriers", barriers); - reader.ReadStreaming("/extractor/traffic_lights", traffic_signals); - storage::serialization::read(reader, "/extractor/edges", edge_list); storage::serialization::read(reader, "/extractor/annotations", annotations); } diff --git a/include/extractor/graph_compressor.hpp b/include/extractor/graph_compressor.hpp index 9e9ddd03624..1b6d8159a3e 100644 --- a/include/extractor/graph_compressor.hpp +++ b/include/extractor/graph_compressor.hpp @@ -4,6 +4,7 @@ #include "extractor/scripting_environment.hpp" #include "util/typedefs.hpp" +#include "traffic_signals.hpp" #include "util/node_based_graph.hpp" #include @@ -25,7 +26,7 @@ class GraphCompressor public: void Compress(const std::unordered_set &barrier_nodes, - const std::unordered_set &traffic_lights, + const TrafficSignals &traffic_signals, ScriptingEnvironment &scripting_environment, std::vector &turn_restrictions, std::vector &maneuver_overrides, diff --git a/include/extractor/node_based_graph_factory.hpp b/include/extractor/node_based_graph_factory.hpp index e3fc395839e..9e07513c2be 100644 --- a/include/extractor/node_based_graph_factory.hpp +++ b/include/extractor/node_based_graph_factory.hpp @@ -8,6 +8,7 @@ #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" @@ -40,11 +41,11 @@ class NodeBasedGraphFactory NodeBasedGraphFactory(const boost::filesystem::path &input_file, ScriptingEnvironment &scripting_environment, std::vector &turn_restrictions, - std::vector &maneuver_overrides); + std::vector &maneuver_overrides, + const TrafficSignals &traffic_signals); auto const &GetGraph() const { return compressed_output_graph; } auto const &GetBarriers() const { return barriers; } - auto const &GetTrafficSignals() const { return traffic_signals; } auto const &GetCompressedEdges() const { return compressed_edge_container; } auto const &GetCoordinates() const { return coordinates; } auto const &GetAnnotationData() const { return annotation_data; } @@ -68,7 +69,8 @@ class NodeBasedGraphFactory // edges into a single representative form void Compress(ScriptingEnvironment &scripting_environment, std::vector &turn_restrictions, - std::vector &maneuver_overrides); + std::vector &maneuver_overrides, + const TrafficSignals &traffic_signals); // 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 @@ -89,7 +91,6 @@ class NodeBasedGraphFactory // General Information about the graph, not used outside of extractor std::unordered_set barriers; - std::unordered_set traffic_signals; std::vector coordinates; diff --git a/include/extractor/traffic_lights.hpp b/include/extractor/traffic_lights.hpp new file mode 100644 index 00000000000..0c8aa6d001e --- /dev/null +++ b/include/extractor/traffic_lights.hpp @@ -0,0 +1,25 @@ +#ifndef OSRM_EXTRACTOR_TRAFFIC_LIGHTS_DATA_HPP_ +#define OSRM_EXTRACTOR_TRAFFIC_LIGHTS_DATA_HPP_ + +namespace osrm +{ +namespace extractor +{ + +namespace 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 TrafficLightClass + +} // namespace extractor +} // namespace osrm + +#endif // OSRM_EXTRACTOR_TRAFFIC_LIGHTS_DATA_HPP_ diff --git a/include/extractor/traffic_signals.hpp b/include/extractor/traffic_signals.hpp new file mode 100644 index 00000000000..36913092c3d --- /dev/null +++ b/include/extractor/traffic_signals.hpp @@ -0,0 +1,28 @@ +#ifndef OSRM_EXTRACTOR_TRAFFIC_SIGNALS_HPP +#define OSRM_EXTRACTOR_TRAFFIC_SIGNALS_HPP + +#include "util/typedefs.hpp" +#include + +#include + +namespace osrm +{ +namespace extractor +{ + +struct TrafficSignals +{ + std::unordered_set bidirectional_nodes; + std::unordered_set, boost::hash>> + unidirectional_segments; + + inline bool HasSignal(NodeID from, NodeID to) const + { + return bidirectional_nodes.count(to) > 0 || unidirectional_segments.count({from, to}) > 0; + } +}; +} // namespace extractor +} // namespace osrm + +#endif // OSRM_EXTRACTOR_TRAFFIC_SIGNALS_HPP diff --git a/profiles/bicycle.lua b/profiles/bicycle.lua index 58740db48b8..07032206e23 100644 --- a/profiles/bicycle.lua +++ b/profiles/bicycle.lua @@ -5,6 +5,7 @@ api_version = 4 Set = require('lib/set') Sequence = require('lib/sequence') Handlers = require("lib/way_handlers") +TrafficSignal = require("lib/traffic_signal") find_access_tag = require("lib/access").find_access_tag limit = require("lib/maxspeed").limit Measure = require("lib/measure") @@ -235,10 +236,7 @@ function process_node(profile, node, result) end -- check if node is a traffic light - local tag = node:get_value_by_key("highway") - if tag and "traffic_signals" == tag then - result.traffic_lights = true - end + result.traffic_lights = TrafficSignal.get_value(node) end function handle_bicycle_tags(profile,way,result,data) diff --git a/profiles/car.lua b/profiles/car.lua index fddae14d717..770805962d5 100644 --- a/profiles/car.lua +++ b/profiles/car.lua @@ -6,6 +6,7 @@ Set = require('lib/set') Sequence = require('lib/sequence') Handlers = require("lib/way_handlers") Relations = require("lib/relations") +TrafficSignal = require("lib/traffic_signal") find_access_tag = require("lib/access").find_access_tag limit = require("lib/maxspeed").limit Utils = require("lib/utils") @@ -360,10 +361,7 @@ function process_node(profile, node, result, relations) end -- check if node is a traffic light - local tag = node:get_value_by_key("highway") - if "traffic_signals" == tag then - result.traffic_lights = true - end + result.traffic_lights = TrafficSignal.get_value(node) end function process_way(profile, way, result, relations) diff --git a/profiles/foot.lua b/profiles/foot.lua index ea24706c86c..978648954e2 100644 --- a/profiles/foot.lua +++ b/profiles/foot.lua @@ -157,6 +157,7 @@ function process_node(profile, node, result) -- check if node is a traffic light local tag = node:get_value_by_key("highway") if "traffic_signals" == tag then + -- Direction should only apply to vehicles result.traffic_lights = true end end diff --git a/profiles/lib/traffic_signal.lua b/profiles/lib/traffic_signal.lua new file mode 100644 index 00000000000..8356e3500a4 --- /dev/null +++ b/profiles/lib/traffic_signal.lua @@ -0,0 +1,26 @@ +-- Assigns traffic light value to node as defined by +-- include/extractor/traffic_lights.hpp + +local TrafficSignal = {} + +function TrafficSignal.get_value(node) + local tag = node:get_value_by_key("highway") + if "traffic_signals" == tag then + local direction = node:get_value_by_key("traffic_signals:direction") + if direction then + if "forward" == direction then + return traffic_lights.direction_forward + end + if "backward" == direction then + return traffic_lights.direction_reverse + end + end + -- return traffic_lights.direction_all + return true + end + -- return traffic_lights.none + return false +end + +return TrafficSignal + diff --git a/profiles/testbot.lua b/profiles/testbot.lua index 691e5761772..a796be21ff5 100644 --- a/profiles/testbot.lua +++ b/profiles/testbot.lua @@ -4,6 +4,7 @@ -- Primary road: 36km/h = 36000m/3600s = 100m/10s -- Secondary road: 18km/h = 18000m/3600s = 100m/20s -- Tertiary road: 12km/h = 12000m/3600s = 100m/30s +TrafficSignal = require("lib/traffic_signal") api_version = 4 @@ -38,12 +39,9 @@ function setup() end function process_node (profile, node, result) - local traffic_signal = node:get_value_by_key("highway") - - if traffic_signal and traffic_signal == "traffic_signals" then - result.traffic_lights = true - -- TODO: a way to set the penalty value - end + -- check if node is a traffic light + result.traffic_lights = TrafficSignal.get_value(node) + -- TODO: a way to set the penalty value end function process_way (profile, way, result) diff --git a/src/extractor/edge_based_graph_factory.cpp b/src/extractor/edge_based_graph_factory.cpp index 6e15d15e942..a0a8561b73d 100644 --- a/src/extractor/edge_based_graph_factory.cpp +++ b/src/extractor/edge_based_graph_factory.cpp @@ -59,7 +59,7 @@ EdgeBasedGraphFactory::EdgeBasedGraphFactory( EdgeBasedNodeDataContainer &node_data_container, const CompressedEdgeContainer &compressed_edge_container, const std::unordered_set &barrier_nodes, - const std::unordered_set &traffic_lights, + const TrafficSignals &traffic_signals, const std::vector &coordinates, const NameTable &name_table, const std::unordered_set &segregated_edges, @@ -67,7 +67,7 @@ EdgeBasedGraphFactory::EdgeBasedGraphFactory( : 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_lights(traffic_lights), m_compressed_edge_container(compressed_edge_container), + m_traffic_signals(traffic_signals), m_compressed_edge_container(compressed_edge_container), name_table(name_table), segregated_edges(segregated_edges), lane_description_map(lane_description_map) { @@ -623,8 +623,26 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( BOOST_ASSERT(!edge_data1.reversed); BOOST_ASSERT(!edge_data2.reversed); + // We write out the mapping between the edge-expanded edges and the original nodes. + // Since each edge represents a possible maneuver, external programs can use this to + // quickly perform updates to edge weights in order to penalize certain turns. + + // If this edge is 'trivial' -- where the compressed edge corresponds exactly to an + // original OSM segment -- we can pull the turn's preceding node ID directly with + // `node_along_road_entering`; + // otherwise, we need to look up the node immediately preceding the turn from the + // compressed edge container. + const bool isTrivial = m_compressed_edge_container.IsTrivial(node_based_edge_from); + + const auto &from_node = + isTrivial ? node_along_road_entering + : m_compressed_edge_container.GetLastEdgeSourceID(node_based_edge_from); + // compute weight and duration penalties - const auto is_traffic_light = m_traffic_lights.count(intersection_node); + // 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(from_node, intersection_node); const auto is_uturn = guidance::getTurnDirection(turn_angle) == guidance::DirectionModifier::UTurn; @@ -690,20 +708,6 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( true, false}; - // We write out the mapping between the edge-expanded edges and the original nodes. - // Since each edge represents a possible maneuver, external programs can use this to - // quickly perform updates to edge weights in order to penalize certain turns. - - // If this edge is 'trivial' -- where the compressed edge corresponds exactly to an - // original OSM segment -- we can pull the turn's preceding node ID directly with - // `node_along_road_entering`; - // otherwise, we need to look up the node immediately preceding the turn from the - // compressed edge container. - const bool isTrivial = m_compressed_edge_container.IsTrivial(node_based_edge_from); - - const auto &from_node = - isTrivial ? node_along_road_entering - : m_compressed_edge_container.GetLastEdgeSourceID(node_based_edge_from); const auto &to_node = m_compressed_edge_container.GetFirstEdgeTargetID(node_based_edge_to); diff --git a/src/extractor/extraction_containers.cpp b/src/extractor/extraction_containers.cpp index 9ec1b288737..4730441b171 100644 --- a/src/extractor/extraction_containers.cpp +++ b/src/extractor/extraction_containers.cpp @@ -11,6 +11,7 @@ #include "util/exception.hpp" #include "util/exception_utils.hpp" #include "util/for_each_indexed.hpp" +#include "util/for_each_pair.hpp" #include "util/log.hpp" #include "util/timing_util.hpp" @@ -413,6 +414,7 @@ void ExtractionContainers::PrepareData(ScriptingEnvironment &scripting_environme const auto restriction_ways = IdentifyRestrictionWays(); const auto maneuver_override_ways = IdentifyManeuverOverrideWays(); + const auto traffic_signals = IdentifyTrafficSignals(); PrepareNodes(); WriteNodes(writer); @@ -422,6 +424,7 @@ void ExtractionContainers::PrepareData(ScriptingEnvironment &scripting_environme WriteEdges(writer); WriteMetadata(writer); + PrepareTrafficSignals(traffic_signals); PrepareManeuverOverrides(maneuver_override_ways); PrepareRestrictions(restriction_ways); WriteCharData(name_file_name); @@ -911,25 +914,6 @@ void ExtractionContainers::WriteNodes(storage::tar::FileWriter &writer) const log << "ok, after " << TIMER_SEC(write_nodes) << "s"; } - { - util::UnbufferedLog log; - log << "Writing traffic light nodes ... "; - TIMER_START(write_nodes); - std::vector internal_traffic_signals; - for (const auto osm_id : traffic_signals) - { - const auto node_id = mapExternalToInternalNodeID( - used_node_id_list.begin(), used_node_id_list.end(), osm_id); - if (node_id != SPECIAL_NODEID) - { - internal_traffic_signals.push_back(node_id); - } - } - storage::serialization::write( - writer, "/extractor/traffic_lights", internal_traffic_signals); - log << "ok, after " << TIMER_SEC(write_nodes) << "s"; - } - util::Log() << "Processed " << max_internal_node_id << " nodes"; } @@ -983,6 +967,50 @@ 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 bidirectional; + std::unordered_set, boost::hash>> + 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) { @@ -1163,6 +1191,93 @@ 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 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 signal_segments; + + std::unordered_set 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 8e94344c1af..388ee866dc5 100644 --- a/src/extractor/extractor.cpp +++ b/src/extractor/extractor.cpp @@ -75,7 +75,7 @@ void SetClassNames(const std::vector &class_names, if (!class_names.empty()) { // add class names that were never used explicitly on a way - // this makes sure we can correctly validate unkown class names later + // this makes sure we can correctly validate unknown class names later for (const auto &name : class_names) { if (!isValidClassName(name)) @@ -207,7 +207,8 @@ int Extractor::run(ScriptingEnvironment &scripting_environment) LaneDescriptionMap turn_lane_map; std::vector turn_restrictions; std::vector unresolved_maneuver_overrides; - std::tie(turn_lane_map, turn_restrictions, unresolved_maneuver_overrides) = + TrafficSignals traffic_signals; + std::tie(turn_lane_map, turn_restrictions, unresolved_maneuver_overrides, traffic_signals) = ParseOSMData(scripting_environment, number_of_threads); // Transform the node-based graph that OSM is based on into an edge-based graph @@ -229,7 +230,8 @@ int Extractor::run(ScriptingEnvironment &scripting_environment) NodeBasedGraphFactory node_based_graph_factory(config.GetPath(".osrm"), scripting_environment, turn_restrictions, - unresolved_maneuver_overrides); + unresolved_maneuver_overrides, + traffic_signals); NameTable name_table; files::readNames(config.GetPath(".osrm.names"), name_table); @@ -264,7 +266,6 @@ int Extractor::run(ScriptingEnvironment &scripting_environment) node_based_graph_factory.GetCompressedEdges().PrintStatistics(); const auto &barrier_nodes = node_based_graph_factory.GetBarriers(); - const auto &traffic_signals = node_based_graph_factory.GetTrafficSignals(); // stealing the annotation data from the node-based graph edge_based_nodes_container = EdgeBasedNodeDataContainer({}, std::move(node_based_graph_factory.GetAnnotationData())); @@ -360,10 +361,12 @@ int Extractor::run(ScriptingEnvironment &scripting_environment) return 0; } -std:: - tuple, std::vector> - Extractor::ParseOSMData(ScriptingEnvironment &scripting_environment, - const unsigned number_of_threads) +std::tuple, + std::vector, + TrafficSignals> +Extractor::ParseOSMData(ScriptingEnvironment &scripting_environment, + const unsigned number_of_threads) { TIMER_START(extracting); @@ -628,7 +631,8 @@ std:: return std::make_tuple(std::move(turn_lane_map), std::move(extraction_containers.turn_restrictions), - std::move(extraction_containers.internal_maneuver_overrides)); + std::move(extraction_containers.internal_maneuver_overrides), + std::move(extraction_containers.internal_traffic_signals)); } void Extractor::FindComponents(unsigned number_of_edge_based_nodes, @@ -699,7 +703,7 @@ EdgeID Extractor::BuildEdgeExpandedGraph( const std::vector &coordinates, const CompressedEdgeContainer &compressed_edge_container, const std::unordered_set &barrier_nodes, - const std::unordered_set &traffic_signals, + const TrafficSignals &traffic_signals, const RestrictionGraph &restriction_graph, const std::unordered_set &segregated_edges, const NameTable &name_table, diff --git a/src/extractor/extractor_callbacks.cpp b/src/extractor/extractor_callbacks.cpp index 6ecfe83e5f2..7cecd763e2b 100644 --- a/src/extractor/extractor_callbacks.cpp +++ b/src/extractor/extractor_callbacks.cpp @@ -78,9 +78,9 @@ void ExtractorCallbacks::ProcessNode(const osmium::Node &input_node, { external_memory.barrier_nodes.push_back(id); } - if (result_node.traffic_lights) + if (result_node.traffic_lights != TrafficLightClass::NONE) { - external_memory.traffic_signals.push_back(id); + external_memory.external_traffic_signals.push_back({id, result_node.traffic_lights}); } } diff --git a/src/extractor/graph_compressor.cpp b/src/extractor/graph_compressor.cpp index d38266bd0d7..00098556403 100644 --- a/src/extractor/graph_compressor.cpp +++ b/src/extractor/graph_compressor.cpp @@ -19,8 +19,10 @@ namespace osrm namespace extractor { +static constexpr int SECOND_TO_DECISECOND = 10; + void GraphCompressor::Compress(const std::unordered_set &barrier_nodes, - const std::unordered_set &traffic_signals, + const TrafficSignals &traffic_signals, ScriptingEnvironment &scripting_environment, std::vector &turn_restrictions, std::vector &maneuver_overrides, @@ -207,16 +209,20 @@ void GraphCompressor::Compress(const std::unordered_set &barrier_nodes, rev_edge_data2.annotation_data, rev_edge_data1.annotation_data); // Add node penalty when compress edge crosses a traffic signal - const bool has_node_penalty = traffic_signals.find(node_v) != traffic_signals.end(); - EdgeDuration node_duration_penalty = MAXIMAL_EDGE_DURATION; - EdgeWeight node_weight_penalty = INVALID_EDGE_WEIGHT; - if (has_node_penalty) + 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 artifical turn for the turn penalty generation + // generate an artificial turn for the turn penalty generation std::vector roads_on_the_right; std::vector roads_on_the_left; ExtractionTurn extraction_turn(0, @@ -245,8 +251,24 @@ void GraphCompressor::Compress(const std::unordered_set &barrier_nodes, roads_on_the_right, roads_on_the_left); scripting_environment.ProcessTurn(extraction_turn); - node_duration_penalty = extraction_turn.duration * 10; - node_weight_penalty = extraction_turn.weight * weight_multiplier; + + auto update_direction_penalty = + [&extraction_turn, weight_multiplier](bool signal, + EdgeDuration &duration_penalty, + EdgeWeight &weight_penalty) { + if (signal) + { + duration_penalty = extraction_turn.duration * SECOND_TO_DECISECOND; + weight_penalty = 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); } // Get weights before graph is modified @@ -278,27 +300,37 @@ void GraphCompressor::Compress(const std::unordered_set &barrier_nodes, BOOST_ASSERT(0 != reverse_weight1); BOOST_ASSERT(0 != reverse_weight2); - // add weight of e2's to e1 - graph.GetEdgeData(forward_e1).weight += forward_weight2; - graph.GetEdgeData(reverse_e1).weight += reverse_weight2; - - // add duration of e2's to e1 - graph.GetEdgeData(forward_e1).duration += forward_duration2; - graph.GetEdgeData(reverse_e1).duration += reverse_duration2; - - // add distance of e2's to e1 - graph.GetEdgeData(forward_e1).distance += forward_distance2; - graph.GetEdgeData(reverse_e1).distance += reverse_distance2; - - if (node_weight_penalty != INVALID_EDGE_WEIGHT && - node_duration_penalty != MAXIMAL_EDGE_DURATION) - { - graph.GetEdgeData(forward_e1).weight += node_weight_penalty; - graph.GetEdgeData(reverse_e1).weight += node_weight_penalty; - graph.GetEdgeData(forward_e1).duration += node_duration_penalty; - graph.GetEdgeData(reverse_e1).duration += node_duration_penalty; - // Note: no penalties for distances - } + auto apply_e2_to_e1 = [&graph](EdgeID edge, + EdgeWeight weight, + EdgeDuration duration, + EdgeDistance distance, + EdgeDuration &duration_penalty, + EdgeWeight &weight_penalty) { + auto &edge_data = graph.GetEdgeData(edge); + edge_data.weight += weight; + edge_data.duration += duration; + edge_data.distance += distance; + if (weight_penalty != INVALID_EDGE_WEIGHT && + duration_penalty != MAXIMAL_EDGE_DURATION) + { + edge_data.weight += weight_penalty; + edge_data.duration += duration_penalty; + // Note: no penalties for distances + } + }; + + apply_e2_to_e1(forward_e1, + forward_weight2, + forward_duration2, + forward_distance2, + forward_node_weight_penalty, + forward_node_duration_penalty); + apply_e2_to_e1(reverse_e1, + reverse_weight2, + reverse_duration2, + reverse_distance2, + reverse_node_weight_penalty, + reverse_node_duration_penalty); // extend e1's to targets of e2's graph.SetTarget(forward_e1, node_w); @@ -311,6 +343,25 @@ void GraphCompressor::Compress(const std::unordered_set &barrier_nodes, // update any involved turn relations turn_path_compressor.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) { + if (weight_penalty == INVALID_EDGE_WEIGHT && + other_weight_penalty != INVALID_EDGE_WEIGHT) + { + weight_penalty = 0; + duration_penalty = 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); + // store compressed geometry in container geometry_compressor.CompressEdge(forward_e1, forward_e2, @@ -320,8 +371,8 @@ void GraphCompressor::Compress(const std::unordered_set &barrier_nodes, forward_weight2, forward_duration1, forward_duration2, - node_weight_penalty, - node_duration_penalty); + forward_node_weight_penalty, + forward_node_duration_penalty); geometry_compressor.CompressEdge(reverse_e1, reverse_e2, node_v, @@ -330,8 +381,8 @@ void GraphCompressor::Compress(const std::unordered_set &barrier_nodes, reverse_weight2, reverse_duration1, reverse_duration2, - node_weight_penalty, - node_duration_penalty); + reverse_node_weight_penalty, + reverse_node_duration_penalty); } } } diff --git a/src/extractor/node_based_graph_factory.cpp b/src/extractor/node_based_graph_factory.cpp index 51473797d2f..887523de98d 100644 --- a/src/extractor/node_based_graph_factory.cpp +++ b/src/extractor/node_based_graph_factory.cpp @@ -19,10 +19,11 @@ NodeBasedGraphFactory::NodeBasedGraphFactory( const boost::filesystem::path &input_file, ScriptingEnvironment &scripting_environment, std::vector &turn_restrictions, - std::vector &maneuver_overrides) + std::vector &maneuver_overrides, + const TrafficSignals &traffic_signals) { LoadDataFromFile(input_file); - Compress(scripting_environment, turn_restrictions, maneuver_overrides); + Compress(scripting_environment, turn_restrictions, maneuver_overrides, traffic_signals); CompressGeometry(); CompressAnnotationData(); } @@ -31,16 +32,10 @@ NodeBasedGraphFactory::NodeBasedGraphFactory( void NodeBasedGraphFactory::LoadDataFromFile(const boost::filesystem::path &input_file) { auto barriers_iter = inserter(barriers, end(barriers)); - auto traffic_signals_iter = inserter(traffic_signals, end(traffic_signals)); std::vector edge_list; - files::readRawNBGraph(input_file, - barriers_iter, - traffic_signals_iter, - coordinates, - osm_node_ids, - edge_list, - annotation_data); + files::readRawNBGraph( + input_file, barriers_iter, coordinates, osm_node_ids, edge_list, annotation_data); const auto number_of_node_based_nodes = coordinates.size(); if (edge_list.empty()) @@ -80,7 +75,8 @@ void NodeBasedGraphFactory::LoadDataFromFile(const boost::filesystem::path &inpu void NodeBasedGraphFactory::Compress(ScriptingEnvironment &scripting_environment, std::vector &turn_restrictions, - std::vector &maneuver_overrides) + std::vector &maneuver_overrides, + const TrafficSignals &traffic_signals) { GraphCompressor graph_compressor; graph_compressor.Compress(barriers, diff --git a/src/extractor/scripting_environment_lua.cpp b/src/extractor/scripting_environment_lua.cpp index 7441400223e..dca80fc447f 100644 --- a/src/extractor/scripting_environment_lua.cpp +++ b/src/extractor/scripting_environment_lua.cpp @@ -285,11 +285,41 @@ void Sol2ScriptingEnvironment::InitContext(LuaScriptingContext &context) return get_location_tag(context, node.location(), key); }); - context.state.new_usertype("ResultNode", - "traffic_lights", - &ExtractionNode::traffic_lights, - "barrier", - &ExtractionNode::barrier); + context.state.new_enum("traffic_lights", + "none", + extractor::TrafficLightClass::NONE, + "direction_all", + extractor::TrafficLightClass::DIRECTION_ALL, + "direction_forward", + extractor::TrafficLightClass::DIRECTION_FORWARD, + "direction_reverse", + extractor::TrafficLightClass::DIRECTION_REVERSE); + + context.state.new_usertype( + "ResultNode", + "traffic_lights", + sol::property([](const ExtractionNode &node) { return node.traffic_lights; }, + [](ExtractionNode &node, const sol::object &obj) { + if (obj.is()) + { + // 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(); + node.traffic_lights = (val) ? TrafficLightClass::DIRECTION_ALL + : TrafficLightClass::NONE; + return; + } + + BOOST_ASSERT(obj.is()); + { + TrafficLightClass::Direction val = + obj.as(); + node.traffic_lights = val; + } + }), + "barrier", + &ExtractionNode::barrier); context.state.new_usertype( "RoadClassification", diff --git a/src/tools/components.cpp b/src/tools/components.cpp index dcd051518d9..1b43cc215b6 100644 --- a/src/tools/components.cpp +++ b/src/tools/components.cpp @@ -44,7 +44,7 @@ std::size_t loadGraph(const std::string &path, auto nop = boost::make_function_output_iterator([](auto) {}); extractor::files::readRawNBGraph( - path, nop, nop, coordinate_list, osm_node_ids, edge_list, annotation_data); + path, nop, coordinate_list, osm_node_ids, edge_list, annotation_data); // Building a node-based graph for (const auto &input_edge : edge_list) diff --git a/unit_tests/extractor/graph_compressor.cpp b/unit_tests/extractor/graph_compressor.cpp index e49932afb0b..559bde7d60a 100644 --- a/unit_tests/extractor/graph_compressor.cpp +++ b/unit_tests/extractor/graph_compressor.cpp @@ -9,7 +9,6 @@ #include -#include #include #include @@ -66,7 +65,7 @@ BOOST_AUTO_TEST_CASE(long_road_test) GraphCompressor compressor; std::unordered_set barrier_nodes; - std::unordered_set traffic_lights; + TrafficSignals traffic_lights; std::vector restrictions; std::vector annotations(1); CompressedEdgeContainer container; @@ -112,7 +111,7 @@ BOOST_AUTO_TEST_CASE(loop_test) GraphCompressor compressor; std::unordered_set barrier_nodes; - std::unordered_set traffic_lights; + TrafficSignals traffic_lights; std::vector restrictions; CompressedEdgeContainer container; std::vector annotations(1); @@ -175,7 +174,7 @@ BOOST_AUTO_TEST_CASE(t_intersection) GraphCompressor compressor; std::unordered_set barrier_nodes; - std::unordered_set traffic_lights; + TrafficSignals traffic_lights; std::vector annotations(1); std::vector restrictions; CompressedEdgeContainer container; @@ -218,7 +217,7 @@ BOOST_AUTO_TEST_CASE(street_name_changes) GraphCompressor compressor; std::unordered_set barrier_nodes; - std::unordered_set traffic_lights; + TrafficSignals traffic_lights; std::vector annotations(2); std::vector restrictions; CompressedEdgeContainer container; @@ -256,7 +255,7 @@ BOOST_AUTO_TEST_CASE(direction_changes) GraphCompressor compressor; std::unordered_set barrier_nodes; - std::unordered_set traffic_lights; + TrafficSignals traffic_lights; std::vector annotations(1); std::vector restrictions; CompressedEdgeContainer container; diff --git a/unit_tests/extractor/intersection_analysis_tests.cpp b/unit_tests/extractor/intersection_analysis_tests.cpp index 979f60dddcb..40cdece819b 100644 --- a/unit_tests/extractor/intersection_analysis_tests.cpp +++ b/unit_tests/extractor/intersection_analysis_tests.cpp @@ -19,7 +19,7 @@ using Graph = util::NodeBasedDynamicGraph; BOOST_AUTO_TEST_CASE(simple_intersection_connectivity) { std::unordered_set barrier_nodes{6}; - std::unordered_set traffic_lights; + TrafficSignals traffic_lights; std::vector annotations{ {EMPTY_NAMEID, 0, INAVLID_CLASS_DATA, TRAVEL_MODE_DRIVING, false}, {EMPTY_NAMEID, 1, INAVLID_CLASS_DATA, TRAVEL_MODE_DRIVING, false}}; @@ -152,7 +152,7 @@ BOOST_AUTO_TEST_CASE(simple_intersection_connectivity) BOOST_AUTO_TEST_CASE(roundabout_intersection_connectivity) { std::unordered_set barrier_nodes; - std::unordered_set traffic_lights; + TrafficSignals traffic_lights; std::vector annotations; std::vector restrictions; CompressedEdgeContainer container; @@ -259,7 +259,7 @@ BOOST_AUTO_TEST_CASE(roundabout_intersection_connectivity) BOOST_AUTO_TEST_CASE(skip_degree_two_nodes) { std::unordered_set barrier_nodes{1}; - std::unordered_set traffic_lights{2}; + TrafficSignals traffic_lights = {{2}, {}}; std::vector annotations(1); std::vector restrictions; CompressedEdgeContainer container;