From a50c28c70fcd712a682498598872e9076c24fd73 Mon Sep 17 00:00:00 2001 From: Michael Bell Date: Sun, 4 Oct 2020 22:27:31 +0100 Subject: [PATCH] Add support for multiple via-way restrictions Currently OSRM only supports turn restrictions with a single via-node or one via-way. OSM allows for multiple via-ways to represent longer and more complex restrictions. This PR extends the use of duplicate nodes for representng via-way turn restrictions to also support multi via-way restrictions. Effectively, this increases the edge-based graph size by the number of edges in multi via-way restrictions. However, given the low number of these restrictions it has little effect on total graph size. In addition, we add a new step in the extraction phase that constructs a restriction graph to support more complex relationships between restrictions, such as nested restrictions and overlapping restrictions. --- CHANGELOG.md | 2 + features/car/conditional_restrictions.feature | 216 +--- features/car/multi_via_restrictions.feature | 1033 +++++++++++++++++ features/car/restrictions.feature | 78 -- .../extractor/edge_based_graph_factory.hpp | 2 +- include/extractor/extraction_containers.hpp | 40 +- include/extractor/extractor.hpp | 30 +- include/extractor/extractor_callbacks.hpp | 4 +- .../first_and_last_segment_of_way.hpp | 59 - include/extractor/graph_compressor.hpp | 1 - .../intersection/intersection_analysis.hpp | 2 +- .../intersection/mergable_road_detector.hpp | 2 +- .../extractor/node_based_graph_factory.hpp | 2 - include/extractor/node_restriction_map.hpp | 80 ++ include/extractor/nodes_of_way.hpp | 56 + include/extractor/restriction.hpp | 108 +- include/extractor/restriction_compressor.hpp | 19 +- include/extractor/restriction_filter.hpp | 6 +- include/extractor/restriction_graph.hpp | 134 +++ include/extractor/restriction_index.hpp | 101 -- include/extractor/restriction_parser.hpp | 3 +- include/extractor/scripting_environment.hpp | 2 +- .../extractor/scripting_environment_lua.hpp | 2 +- include/extractor/way_restriction_map.hpp | 86 +- include/guidance/guidance_processing.hpp | 1 + include/guidance/turn_analysis.hpp | 2 +- include/guidance/turn_discovery.hpp | 2 +- include/util/for_each_indexed.hpp | 31 + include/util/permutation.hpp | 4 +- include/util/typedefs.hpp | 1 + src/extractor/edge_based_graph_factory.cpp | 209 ++-- src/extractor/extraction_containers.cpp | 614 +++++----- src/extractor/extractor.cpp | 73 +- src/extractor/extractor_callbacks.cpp | 15 +- src/extractor/graph_compressor.cpp | 46 +- .../intersection/intersection_analysis.cpp | 22 +- src/extractor/node_based_graph_factory.cpp | 15 +- src/extractor/restriction_compressor.cpp | 149 ++- src/extractor/restriction_filter.cpp | 74 +- src/extractor/restriction_graph.cpp | 288 +++++ src/extractor/restriction_parser.cpp | 22 +- src/extractor/scripting_environment_lua.cpp | 2 +- src/extractor/way_restriction_map.cpp | 291 ++--- src/guidance/guidance_processing.cpp | 26 +- unit_tests/extractor/graph_compressor.cpp | 10 - .../extractor/intersection_analysis_tests.cpp | 13 +- unit_tests/extractor/restriction_graph.cpp | 597 ++++++++++ .../mocks/mock_scripting_environment.hpp | 2 +- 48 files changed, 3171 insertions(+), 1406 deletions(-) create mode 100644 features/car/multi_via_restrictions.feature delete mode 100644 include/extractor/first_and_last_segment_of_way.hpp create mode 100644 include/extractor/node_restriction_map.hpp create mode 100644 include/extractor/nodes_of_way.hpp create mode 100644 include/extractor/restriction_graph.hpp delete mode 100644 include/extractor/restriction_index.hpp create mode 100644 include/util/for_each_indexed.hpp create mode 100644 src/extractor/restriction_graph.cpp create mode 100644 unit_tests/extractor/restriction_graph.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 8003a438c91..f5b7a98cdff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Unreleased - Changes from 5.23.0 + - Features + - ADDED: Added support for multiple via-way restrictions. [#5907](https://github.com/Project-OSRM/osrm-backend/pull/5907) - Misc: - CHANGED: Unify `.osrm.turn_penalites_index` dump processing same with `.osrm.turn_weight_penalties` and `.osrm.turn_duration_penalties` [#5868](https://github.com/Project-OSRM/osrm-backend/pull/5868) - Infrastructure diff --git a/features/car/conditional_restrictions.feature b/features/car/conditional_restrictions.feature index 23a9e1efe26..c2311067cea 100644 --- a/features/car/conditional_restrictions.feature +++ b/features/car/conditional_restrictions.feature @@ -387,217 +387,37 @@ Feature: Car - Turn restrictions | m | p | mj,jp,jp | @no_turning @conditionals - Scenario: Car - only_right_turn + Scenario: Car - Multiple conditional restrictions applicable to same turn Given the extract extra arguments "--parse-conditional-restrictions" # time stamp for 10am on Tues, 02 May 2017 GMT Given the contract extra arguments "--time-zone-file=test/data/tz/{timezone_names}/guinea.geojson --parse-conditionals-from-now=1493719200" Given the customize extra arguments "--time-zone-file=test/data/tz/{timezone_names}/guinea.geojson --parse-conditionals-from-now=1493719200" - Given the node map - """ - a - d j b - c - """ - And the ways - | nodes | oneway | - | aj | no | - | jc | no | - | bj | yes | - | jd | yes | - - And the relations - | type | way:from | way:to | node:via | restriction:conditional | - | restriction | bj | aj | j | only_right_turn @ (Mo-Su 07:00-14:00) | - - When I route I should get - | from | to | route | - | b | c | bj,aj,aj,jc,jc | - | b | a | bj,aj,aj | - | b | d | bj,aj,aj,jd,jd | - - @no_turning @conditionals - Scenario: Car - No right turn - Given the extract extra arguments "--parse-conditional-restrictions" - # time stamp for 10am on Tues, 02 May 2017 GMT - Given the contract extra arguments "--time-zone-file=test/data/tz/{timezone_names}/guinea.geojson --parse-conditionals-from-now=1493719200" - Given the customize extra arguments "--time-zone-file=test/data/tz/{timezone_names}/guinea.geojson --parse-conditionals-from-now=1493719200" Given the node map """ - a - d j b - c + j + | + k - l - m + | + n """ And the ways - | nodes | oneway | - | aj | no | - | jc | no | - | bj | yes | - | jd | yes | + | nodes | + | kl | + | jl | + | ln | + | lm | And the relations | type | way:from | way:to | node:via | restriction:conditional | - | restriction | bj | aj | j | no_right_turn @ (Mo-Fr 07:00-13:00) | - - When I route I should get - | from | to | route | # | - | b | c | bj,jc,jc | normal turn | - | b | a | bj,jc,jc,aj,aj | avoids right turn | - | b | d | bj,jd,jd | normal maneuver | - - @only_turning @conditionals - Scenario: Car - only_left_turn - Given the extract extra arguments "--parse-conditional-restrictions" - # time stamp for 10am on Tues, 02 May 2017 GMT - Given the contract extra arguments "--time-zone-file=test/data/tz/{timezone_names}/guinea.geojson --parse-conditionals-from-now=1493719200" - Given the customize extra arguments "--time-zone-file=test/data/tz/{timezone_names}/guinea.geojson --parse-conditionals-from-now=1493719200" - Given the node map - """ - a - d j b - c - """ - - And the ways - | nodes | oneway | - | aj | no | - | jc | no | - | bj | yes | - | jd | yes | - - And the relations - | type | way:from | way:to | node:via | restriction:conditional | - | restriction | bj | jc | j | only_left_turn @ (Mo-Fr 07:00-16:00) | - - When I route I should get - | from | to | route | - | b | c | bj,jc,jc | - | b | a | bj,jc,jc,aj,aj | - | b | d | bj,jc,jc,jd,jd | - - @no_turning @conditionals - Scenario: Car - No left turn - Given the extract extra arguments "--parse-conditional-restrictions" - # time stamp for 10am on Tues, 02 May 2017 GMT - Given the contract extra arguments "--time-zone-file=test/data/tz/{timezone_names}/guinea.geojson --parse-conditionals-from-now=1493719200" - Given the customize extra arguments "--time-zone-file=test/data/tz/{timezone_names}/guinea.geojson --parse-conditionals-from-now=1493719200" - Given the node map - """ - a - d j b - c - """ - - And the ways - | nodes | oneway | - | aj | no | - | jc | no | - | bj | yes | - | jd | yes | - - And the relations - | type | way:from | way:to | node:via | restriction:conditional | - | restriction | bj | jc | j | no_left_turn @ (Mo-Su 00:00-23:59) | - - When I route I should get - | from | to | route | - | b | c | bj,aj,aj,jc,jc | - | b | a | bj,aj,aj | - | b | d | bj,jd,jd | - - @no_turning @conditionals - Scenario: Car - Conditional restriction is off - Given the extract extra arguments "--parse-conditional-restrictions" - # time stamp for 10am on Tues, 02 May 2017 GMT - Given the contract extra arguments "--time-zone-file=test/data/tz/{timezone_names}/guinea.geojson --parse-conditionals-from-now=1493719200" - Given the customize extra arguments "--time-zone-file=test/data/tz/{timezone_names}/guinea.geojson --parse-conditionals-from-now=1493719200" - Given the node map - """ - a - d j b - c - """ - - And the ways - | nodes | oneway | - | aj | no | - | jc | no | - | bj | yes | - | jd | yes | - - And the relations - | type | way:from | way:to | node:via | restriction:conditional | - | restriction | bj | aj | j | no_right_turn @ (Mo-Su 16:00-20:00) | - - When I route I should get - | from | to | route | - | b | c | bj,jc,jc | - | b | a | bj,aj,aj | - | b | d | bj,jd,jd | - - @no_turning @conditionals - Scenario: Car - Conditional restriction is on - Given the extract extra arguments "--parse-conditional-restrictions" - # 10am utc, wed - Given the contract extra arguments "--time-zone-file=test/data/tz/{timezone_names}/guinea.geojson --parse-conditionals-from-now=1493805600" - Given the customize extra arguments "--time-zone-file=test/data/tz/{timezone_names}/guinea.geojson --parse-conditionals-from-now=1493805600" - Given the node map - """ - a - d j b - c - """ - - And the ways - | nodes | oneway | - | aj | no | - | jc | no | - | bj | yes | - | jd | yes | - - And the relations - | type | way:from | way:to | node:via | restriction:conditional | - | restriction | jb | aj | j | no_right_turn @ (Mo-Fr 07:00-14:00) | + | restriction | kl | lj | l | only_left_turn @ (Sa-Su 07:00-10:30) | + | restriction | kl | ln | l | only_right_turn @ (Mo-Fr 07:00-10:30) | When I route I should get | from | to | route | - | b | c | bj,jc,jc | - | b | a | bj,jc,jc,aj,aj | - | b | d | bj,jd,jd | + | k | m | kl,ln,ln,lm,lm | - @no_turning @conditionals - Scenario: Car - Conditional restriction with multiple time windows - Given the extract extra arguments "--parse-conditional-restrictions" - # 5pm Wed 02 May, 2017 GMT - Given the contract extra arguments "--time-zone-file=test/data/tz/{timezone_names}/guinea.geojson --parse-conditionals-from-now=1493744400" - Given the customize extra arguments "--time-zone-file=test/data/tz/{timezone_names}/guinea.geojson --parse-conditionals-from-now=1493744400" - - Given the node map - """ - a - p | - \ | - j - | \ - c m - """ - - And the ways - | nodes | oneway | - | aj | no | - | jc | no | - | jp | yes | - | mj | yes | - - And the relations - | type | way:from | way:to | node:via | restriction:conditional | - | restriction | aj | jp | j | no_right_turn @ (Mo-Fr 07:00-11:00,16:00-18:30) | - - When I route I should get - | from | to | route | - | a | p | aj,jc,jc,jp,jp | - | m | p | mj,jp,jp | @restriction-way Scenario: Car - prohibit turn @@ -719,7 +539,7 @@ Feature: Car - Turn restrictions When I route I should get | from | to | route | turns | | a | e | cap south,florida nw,florida nw,florida ne | depart,turn right,continue uturn,arrive | - | f | d | cap north,florida nw,florida nw | depart,turn left,arrive | + | f | d | cap north,florida nw,florida nw | depart,turn left,arrive | | e | c | florida ne,florida nw,cap south,cap south | depart,continue uturn,turn right,arrive | @no_turning @conditionals @@ -796,8 +616,8 @@ Feature: Car - Turn restrictions | nodes | name | | ab | albic | | bc | albic | - | db | dobe | - | be | dobe | + | db | dobe | + | be | dobe | And the relations | type | way:from | way:to | node:via | restriction:conditional | @@ -1047,7 +867,7 @@ Feature: Car - Turn restrictions | type | way:from | node:via | way:to | restriction:conditional | | restriction | be | e | de | no_right_turn @ (Mo-Fr 07:00-11:00) | - # node restrictino is off, way restriction is on + # node restriction is off, way restriction is on When I route I should get | from | to | route | turns | locations | | a | d | ab,be,ef,ef,de,de | depart,turn right,turn left,continue uturn,new name straight,arrive | a,b,e,f,e,d | diff --git a/features/car/multi_via_restrictions.feature b/features/car/multi_via_restrictions.feature new file mode 100644 index 00000000000..42c6c6d42a9 --- /dev/null +++ b/features/car/multi_via_restrictions.feature @@ -0,0 +1,1033 @@ +@routing @car @restrictions + +Feature: Car - Multiple Via Turn restrictions + + Background: Use car routing + Given the profile "car" + Given a grid size of 200 meters + + + @restriction-way @no_turning @overlap + Scenario: Car - Node restriction inside multiple via restriction + Given the node map + """ + 1 2 3 4 5 + a---b---c---d---e---------f---------g + | | | + |7 |8 |9 + | | | + x---h---------i---------j + """ + + And the ways + | nodes | oneway | name | + | ab | yes | forward | + | bc | yes | forward | + | cd | yes | forward | + | de | yes | forward | + | ef | yes | forward | + | fg | yes | forward | + | eh | yes | first | + | fi | yes | second | + | gj | yes | third | + | ih | yes | back | + | ji | yes | back | + | hx | yes | back | + + And the relations + | type | way:from | way:via | way:to | restriction | + | restriction | ab | bc,cd,de,ef | fi | no_right_turn | + + And the relations + | type | way:from | node:via | way:to | restriction | + | restriction | de | e | eh | no_right_turn | + + When I route I should get + | from | to | route | + | 1 | x | forward,third,back,back | + | 2 | x | forward,second,back,back | + | 3 | x | forward,second,back,back | + | 4 | x | forward,second,back,back | + | 5 | x | forward,third,back,back | + | 7 | x | first,back,back | + | 8 | x | second,back,back | + | 9 | x | third,back,back | + + + @restriction-way @no_turning @overlap @conditionals + Scenario: Car - Conditional node restriction inside conditional multiple via restriction + Given the origin -9.2972,10.3811 + # coordinate in Guinée, a country that observes GMT year round + Given the extract extra arguments "--parse-conditional-restrictions" + # time stamp for 10am on Tues, 02 May 2017 GMT + Given the contract extra arguments "--time-zone-file=test/data/tz/{timezone_names}/guinea.geojson --parse-conditionals-from-now=1493719200" + Given the customize extra arguments "--time-zone-file=test/data/tz/{timezone_names}/guinea.geojson --parse-conditionals-from-now=1493719200" + Given the node map + """ + 1 2 3 4 5 + a---b---c---d---e---------f---------g + | | | + |7 |8 |9 + | | | + x---h---------i---------j + """ + + And the ways + | nodes | oneway | name | + | ab | yes | forward | + | bc | yes | forward | + | cd | yes | forward | + | de | yes | forward | + | ef | yes | forward | + | fg | yes | forward | + | eh | yes | first | + | fi | yes | second | + | gj | yes | third | + | ih | yes | back | + | ji | yes | back | + | hx | yes | back | + + And the relations + | type | way:from | way:via | way:to | restriction:conditional | + | restriction | ab | bc,cd,de,ef | fi | no_right_turn @ (Mo-Fr 07:00-10:30) | + + And the relations + | type | way:from | node:via | way:to | restriction:conditional | + | restriction | de | e | eh | no_right_turn @ (Mo-Fr 07:00-10:30) | + | restriction | de | e | eh | only_right_turn @ (Sa-Su 07:00-10:30) | + + When I route I should get + | from | to | route | + | 1 | x | forward,third,back,back | + | 2 | x | forward,second,back,back | + | 3 | x | forward,second,back,back | + | 4 | x | forward,second,back,back | + | 5 | x | forward,third,back,back | + | 7 | x | first,back,back | + | 8 | x | second,back,back | + | 9 | x | third,back,back | + + + @restriction-way @no_turning @overlap + Scenario: Car - Multiple via restriction inside multiple via restriction + Given the node map + """ + 1 2 3 4 5 + a---b---c---d---e---------f---------g + | | | + |7 |8 |9 + | | | + x---h---------i---------j + """ + + And the ways + | nodes | oneway | name | + | ab | yes | forward | + | bc | yes | forward | + | cd | yes | forward | + | de | yes | forward | + | ef | yes | forward | + | fg | yes | forward | + | eh | yes | first | + | fi | yes | second | + | gj | yes | third | + | ih | yes | back | + | ji | yes | back | + | hx | yes | back | + + And the relations + | type | way:from | way:via | way:to | restriction | + | restriction | ab | bc,cd,de,ef | fi | no_right_turn | + | restriction | bc | cd,de | eh | no_right_turn | + + When I route I should get + | from | to | route | + | 1 | x | forward,third,back,back | + | 2 | x | forward,second,back,back | + | 3 | x | forward,first,back,back | + | 4 | x | forward,second,back,back | + | 5 | x | forward,third,back,back | + | 7 | x | first,back,back | + | 8 | x | second,back,back | + | 9 | x | third,back,back | + + + @restriction-way @no_turning @overlap @conditionals + Scenario: Car - Conditional multiple via restriction inside conditional multiple via restriction + + Given a grid size of 200 meters + Given the origin -9.2972,10.3811 + # coordinate in Guinée, a country that observes GMT year round + Given the extract extra arguments "--parse-conditional-restrictions" + # time stamp for 10am on Tues, 02 May 2017 GMT + Given the contract extra arguments "--time-zone-file=test/data/tz/{timezone_names}/guinea.geojson --parse-conditionals-from-now=1493719200" + Given the customize extra arguments "--time-zone-file=test/data/tz/{timezone_names}/guinea.geojson --parse-conditionals-from-now=1493719200" + Given the node map + + """ + 1 2 3 4 5 + a---b---c---d---e---------f---------g + | | | + |7 |8 |9 + | | | + x---h---------i---------j + """ + + And the ways + | nodes | oneway | name | + | ab | yes | forward | + | bc | yes | forward | + | cd | yes | forward | + | de | yes | forward | + | ef | yes | forward | + | fg | yes | forward | + | eh | yes | first | + | fi | yes | second | + | gj | yes | third | + | ih | yes | back | + | ji | yes | back | + | hx | yes | back | + + And the relations + | type | way:from | way:via | way:to | restriction:conditional | + | restriction | ab | bc,cd,de,ef | fi | no_right_turn @ (Mo-Fr 07:00-10:30) | + | restriction | bc | cd,de | eh | no_right_turn @ (Mo-Fr 07:00-10:30) | + | restriction | bc | cd,de | eh | only_right_turn @ (Sa-Su 07:00-10:30) | + + When I route I should get + | from | to | route | + | 1 | x | forward,third,back,back | + | 2 | x | forward,second,back,back | + | 3 | x | forward,first,back,back | + | 4 | x | forward,second,back,back | + | 5 | x | forward,third,back,back | + | 7 | x | first,back,back | + | 8 | x | second,back,back | + | 9 | x | third,back,back | + + + @restriction-way @only_turning @overlap + Scenario: Car - Overlapping multiple via restrictions + Given the node map + """ + a f j + | | | + b---d---e---i---k----m + | | | + c g l + """ + + And the ways + | nodes | oneway | name | + | ab | yes | down | + | cb | yes | up | + | bd | yes | right | + | de | yes | right | + | ef | yes | up | + | eg | yes | down | + | ei | yes | right | + | ik | yes | right | + | kj | yes | up | + | kl | yes | down | + | km | yes | right | + + And the relations + | type | way:from | way:via | way:to | restriction | + | restriction | ab | bd,de | ei | only_straight_on | + | restriction | de | ei,ik | km | only_straight_on | + + When I route I should get + | from | to | route | + | a | f | | + | a | g | | + | a | j | | + | a | l | | + | a | m | down,right,right | + | c | f | up,right,up,up | + | c | g | up,right,down,down | + | c | j | | + | c | l | | + | c | m | up,right,right | + | i | j | right,up,up | + | i | l | right,down,down | + | i | m | right,right | + + + @restriction-way @only_turning @overlap @conditionals + Scenario: Car - Overlapping conditional multiple via restrictions + Given a grid size of 200 meters + Given the origin -9.2972,10.3811 + # coordinate in Guinée, a country that observes GMT year round + Given the extract extra arguments "--parse-conditional-restrictions" + # time stamp for 10am on Tues, 02 May 2017 GMT + Given the contract extra arguments "--time-zone-file=test/data/tz/{timezone_names}/guinea.geojson --parse-conditionals-from-now=1493719200" + Given the customize extra arguments "--time-zone-file=test/data/tz/{timezone_names}/guinea.geojson --parse-conditionals-from-now=1493719200" + Given the node map + """ + a f j + | | | + b---d---e---i---k----m + | | | + c g l + """ + + And the ways + | nodes | oneway | name | + | ab | yes | down | + | cb | yes | up | + | bd | yes | right | + | db | yes | left | + | de | yes | right | + | ed | yes | left | + | ef | yes | up | + | eg | yes | down | + | ei | yes | right | + | ie | yes | left | + | ik | yes | right | + | ki | yes | left | + | kj | yes | up | + | kl | yes | down | + | km | no | end | + + And the relations + | type | way:from | way:via | way:to | restriction:conditional | + | restriction | ab | bd,de | ei | only_straight_on @ (Mo-Fr 07:00-10:30) | + | restriction | ab | bd,de | ef | only_left_turn @ (Sa-Su 07:00-10:30) | + | restriction | de | ei,ik | km | only_straight_on @ (Mo-Fr 07:00-10:30) | + + When I route I should get + | from | to | route | + | a | f | down,right,end,end,left,up,up | + | a | g | down,right,end,end,left,down,down | + | a | j | down,right,end,end,up,up | + | a | l | down,right,end,end,down,down | + | a | m | down,right,end,end | + | c | f | up,right,up,up | + | c | g | up,right,down,down | + | c | j | up,right,end,end,up,up | + | c | l | up,right,end,end,down,down | + | c | m | up,right,end,end | + | i | j | right,up,up | + | i | l | right,down,down | + | i | m | right,end,end | + + + + @restriction-way @only_turning @geometry + Scenario: Car - Multiple via restriction with non-compressable via geometry + Given the node map + """ + a---b---c---d---e---f---g---h + | | | + i j k + """ + + And the ways + | nodes | oneway | name | + | ab | yes | right | + | bcd | yes | right | + | defg | yes | right | + | ci | yes | down | + | ej | yes | down | + | gh | yes | end | + | gk | yes | down | + + And the relations + | type | way:from | way:via | way:to | restriction | + | restriction | ab | bcd,defg | gh | only_straight_on | + + When I route I should get + | from | to | route | + | a | h | right,end,end | + | a | k | | + + @restriction-way @only_turning @geometry + Scenario: Car - Multiple via restriction with non-compressable from/to nodes + Given the node map + """ + a---b---c---d---e---f---g---h---i---j---k---l + | | | | | + m n o p q + """ + + And the ways + | nodes | oneway | name | + | abcdefg | yes | right | + | ghi | yes | right | + | ijkl | yes | end | + | cm | yes | down | + | en | yes | down | + | go | yes | down | + | ip | yes | down | + | kq | yes | down | + + And the relations + | type | way:from | way:via | way:to | restriction | + | restriction | abcdefg | ghi | ijkl | only_straight_on | + + When I route I should get + | from | to | route | + | a | l | right,end,end | + | a | p | | + + @restriction-way @no_turning + Scenario: Car - Long unrestricted route and short restricted route + Given the node map + """ + a------------------------------------b + | | + c--d--e--f--------------------------- + """ + + And the ways + | nodes | + | ac | + | ab | + | bf | + | cd | + | de | + | ef | + + And the relations + | type | way:from | way:via | way:to | restriction | + | restriction | ac | cd | de | no_straight_on | + + When I route I should get + | from | to | route | + | a | f | ab,bf,bf | + + + @restriction-way @overlap @no_turning + Scenario: Car - Junction with multiple via u-turn restrictions + # Example: https://www.openstreetmap.org/#map=19/52.07399/5.09724 + Given the node map + """ + a b + | | + c---d---e---f + | | + g---h---i---j + | | + k l + """ + + And the ways + | nodes | oneway | name | + | ad | yes | down | + | eb | yes | up | + | fe | yes | left | + | ij | yes | right | + | li | yes | up | + | hk | yes | down | + | gh | yes | right | + | dc | yes | left | + | dh | yes | down | + | hi | yes | right | + | ie | yes | up | + | ed | yes | left | + + And the relations + | type | way:from | way:via | way:to | restriction | + | restriction | ad | dh,hi | ie | no_u_turn | + | restriction | li | ie,ed | dh | no_u_turn | + + When I route I should get + | from | to | route | + | a | b | | + | a | c | down,left,left | + | a | k | down,down | + | a | j | down,right,right | + | f | b | left,up,up | + | f | c | left,left | + | f | k | left,down,down | + | f | j | left,down,right,right | + | l | b | up,up | + | l | c | up,left,left | + | l | k | | + | l | j | up,right,right | + | g | b | right,up,up | + | g | c | right,up,left,left | + | g | k | right,down,down | + | g | j | right,right | + + + @restriction-way @overlap @no_turning + Scenario: Car - Junction with multiple via u-turn restrictions, service roads + # Example: https://www.openstreetmap.org/#map=19/48.38566/10.88068 + Given the node map + """ + a b + | | + c---d--e--f---g + | _/| + h__/ | + |\ \ | + i---j-k-l-m---n + | | + o p + """ + + And the ways + | nodes | oneway | name | + | ad | yes | down | + | fb | yes | up | + | gf | yes | left | + | mn | yes | right | + | pm | yes | up | + | jo | yes | down | + | ij | yes | right | + | dc | yes | left | + | dh | yes | down | + | hj | yes | down | + | jkl | yes | right | + | lm | yes | right | + | mf | yes | up | + | fe | yes | left | + | ed | yes | left | + + And the ways + | nodes | oneway | name | highway | access | psv | + | kh | yes | service | service | no | yes | + | lh | no | service | service | no | yes | + | fh | yes | service | service | no | yes | + + And the relations + | type | way:from | way:via | way:to | restriction | + | restriction | hj | jkl,lm | mf | no_u_turn | + | restriction | lm | mf | fe | no_u_turn | + | restriction | mf | fe,ed | dh | no_u_turn | + | restriction | ed | dh,hj | jkl | no_u_turn | + + When I route I should get + | from | to | route | + | a | b | | + | a | c | down,left,left | + | a | o | down,down | + | a | n | down,right,right | + | i | b | right,up,up | + | i | c | | + | i | o | right,down,down | + | i | n | right,right | + | p | b | up,up | + | p | c | up,left,left | + | p | o | | + | p | n | up,right,right | + | g | b | left,up,up | + | g | c | left,left | + | g | o | left,down,down | + | g | n | | + + + @restriction-way @overlap @no_turning + Scenario: Car - Junction with overlapping and duplicate multiple via restrictions + # Example: https://www.openstreetmap.org/#map=19/34.66291/33.01711 + Given the profile file "car" initialized with + """ + profile.properties.left_hand_driving = true + """ + + And the node map + """ + a b + | | + c---d---e---f + | | + g---h---i---j + | | + k l + """ + + And the nodes + | node | highway | + | d | traffic_signals | + | e | traffic_signals | + | h | traffic_signals | + | i | traffic_signals | + + And the ways + | nodes | oneway | name | + | da | yes | up | + | be | yes | down | + | ef | yes | right | + | ji | yes | left | + | il | yes | down | + | kh | yes | up | + | hg | yes | left | + | cd | yes | right | + | hd | yes | up | + | ih | yes | left | + | ei | yes | down | + | de | yes | right | + + And the relations + | type | way:from | way:via | way:to | restriction | + | restriction | be | ei,ih | hd | no_u_turn | + | restriction | ji | ih,hd | de | no_u_turn | + | restriction | kh | hd,de | ei | no_u_turn | + | restriction | cd | de,ei | ih | no_u_turn | + | restriction | hd | de | ei | no_u_turn | + | restriction | de | ei | ih | no_u_turn | + | restriction | ei | ih | hd | no_u_turn | + | restriction | ei | ih,hd | de | no_u_turn | + | restriction | ih | hd | de | no_u_turn | + | restriction | ih | hd,de | ei | no_u_turn | + + When I route I should get + | from | to | route | + | b | a | | + | b | g | down,left,left | + | b | l | down,down | + | b | f | down,right,right | + | j | a | left,up,up | + | j | g | left,left | + | j | l | left,down,down | + | j | f | | + | k | a | up,up | + | k | g | up,left,left | + | k | l | | + | k | f | up,right,right | + | c | a | right,up,up | + | c | g | | + | c | l | right,down,down | + | c | f | right,right | + + + @restriction-way @no_turning + Scenario: Car - Junction with multiple via restriction to side road, traffic lights + # Example: https://www.openstreetmap.org/#map=19/48.23662/16.42545 + Given the node map + """ + e---d + | + f---c---g + | + h---b---i + | + a + """ + + And the nodes + | node | highway | + | c | traffic_signals | + | b | traffic_signals | + + And the ways + | nodes | oneway | name | + | ab | no | up | + | bc | no | up | + | cd | no | up | + | de | no | left | + | hb | yes | right | + | bi | yes | right | + | gc | yes | left | + | cf | yes | left | + + And the relations + | type | way:from | way:via | way:to | restriction | + | restriction | ab | bc,cd | de | no_left_turn | + + When I route I should get + | from | to | route | + | a | e | | + | a | f | up,left,left | + | a | g | | + | a | h | | + | a | i | up,right,right | + + + @restriction-way @overlap @no_turning + Scenario: Car - Many overlapping multiple via restrictions, traffic signals + # Example: https://www.openstreetmap.org/#map=19/48.76987/11.43410 + Given the node map + """ + 8 5 + p______a_______n________o__x + 1| | | + | \ / + r___q____b____ m + | \ \___ _/ 7 + \ c _l____k__w + s \ _/ _/ + \ _d/ __j + \ _/ \ _/ + | ___g e_____i + v__t__/ _/ \ 4 + 6 2/ 3\ + f h + """ + + And the nodes + | node | highway | + | n | traffic_signals | + | m | traffic_signals | + | q | traffic_signals | + + And the ways + | nodes | oneway | + | on | yes | + | na | yes | + | ap | yes | + | pr | yes | + | rqb | yes | + | bl | yes | + | oml | yes | + | ld | yes | + | lk | yes | + | ba | yes | + | bcd | no | + | de | no | + | eh | no | + | ei | no | + | ejk | yes | + | rst | yes | + | dgt | yes | + | fe | yes | + | xo | yes | + | tv | yes | + | kw | yes | + + And the relations + | type | way:from | way:via | way:to | restriction | + | restriction | pr | rqb,bcd | dgt | no_right_turn | + | restriction | rqb | bcd,de | ejk | no_left_turn | + | restriction | rqb | bcd | dgt | no_right_turn | + | restriction | fe | ed | dgt | no_u_turn | + | restriction | fe | ed,dcb | bl | no_right_turn | + | restriction | he | ed,dcb | bl | no_right_turn | + | restriction | oml | ld,de | ejk | no_u_turn | + + And the relations + | type | way:from | node:via | way:to | restriction | + | restriction | ap | p | pr | no_u_turn | + | restriction | rqb | b | ba | no_left_turn | + | restriction | ld | d | dcb | no_right_turn | + | restriction | oml | l | lk | no_left_turn | + | restriction | na | a | ab | no_left_turn | + | restriction | dcb | b | bl | no_right_turn | + | restriction | dcb | b | bcd | no_u_turn | + | restriction | bcd | d | dcb | no_u_turn | + | restriction | bl | l | ld | no_right_turn | + + # Additional relations to prevent u-turns on small roads polluting the results + And the relations + | type | way:from | node:via | way:to | restriction | + | restriction | eh | h | he | no_u_turn | + | restriction | ei | i | ie | no_u_turn | + + When I route I should get + | from | to | route | locations | + | 1 | 6 | pr,rst,tv,tv | _,r,t,_ | + | 1 | 3 | pr,rqb,bcd,de,eh,eh | _,r,b,d,e,_ | + | 1 | 4 | pr,rqb,bcd,de,ei,ei | _,r,b,d,e,_ | + | 1 | 7 | pr,rqb,bl,lk,kw,kw | _,r,b,l,k,_ | + | 1 | 8 | | | + | 2 | 6 | | | + | 2 | 3 | fe,eh,eh | _,e,_ | + | 2 | 4 | fe,ei,ei | _,e,_ | + | 2 | 7 | fe,ejk,kw,kw | _,e,k,_ | + | 2 | 8 | fe,de,bcd,ba,ap,ap | _,e,d,b,a,_ | + | 3 | 6 | eh,de,dgt,tv,tv | _,e,d,t,_ | + | 3 | 4 | eh,ei,ei | _,e,_ | + | 3 | 7 | eh,ejk,kw,kw | _,e,k,_ | + | 3 | 8 | eh,de,bcd,ba,ap,ap | _,e,d,b,a,_ | + | 4 | 6 | ei,de,dgt,tv,tv | _,e,d,t,_ | + | 4 | 3 | ei,eh,eh | _,e,_ | + | 4 | 7 | ei,ejk,kw,kw | _,e,k,_ | + | 4 | 8 | ei,de,bcd,ba,ap,ap | _,e,d,b,a,_ | + | 5 | 6 | xo,oml,ld,dgt,tv,tv | _,o,l,d,t,_ | + | 5 | 3 | xo,oml,ld,de,eh,eh | _,o,l,d,e,_ | + | 5 | 4 | xo,oml,ld,de,ei,ei | _,o,l,d,e,_ | + | 5 | 7 | | | + | 5 | 8 | xo,on,na,ap,ap | _,o,n,a,_ | + + + + @restriction-way @overlap @no_turning + Scenario: Car - Multiple via restriction with start and end on same node + # Example: https://www.openstreetmap.org/#map=19/52.41988/16.96088 + Given the node map + """ + |--g---f---e + a | | + |--b---c---d + + """ + + And the nodes + | node | highway | + | b | traffic_signals | + + And the ways + | nodes | oneway | name | + | abc | yes | enter | + | cd | yes | right | + | de | yes | up | + | ef | yes | left | + | fga | yes | exit | + | fc | yes | down | + + And the relations + | type | way:from | way:via | way:to | restriction | + | restriction | abc | cd,de,ef | fga | no_u_turn | + + When I route I should get + | from | to | route | locations | + | a | g | enter,right,up,left,down,right,up,left,exit,exit | a,c,d,e,f,c,d,e,f,g | + | b | a | enter,right,up,left,down,right,up,left,exit,exit | b,c,d,e,f,c,d,e,f,a | + # This is a correct but not within the spirit of the restriction. + # Does this indicate the restriction is not strong enough? + + + @restriction-way @no_turning + Scenario: Car - Multiple via restriction preventing bypassing main road + # Example: https://www.openstreetmap.org/#map=19/48.72429/21.25912 + Given the node map + """ + a--b--c--d--e--f + \ | + --g--h--i--j + | + k + """ + + And the nodes + | node | highway | + | d | traffic_signals | + | e | traffic_signals | + + And the ways + | nodes | oneway | name | + | ab | yes | main | + | bc | yes | main | + | cd | yes | main | + | de | yes | main | + | ef | yes | main | + | bg | yes | side | + | gh | yes | side | + | hi | yes | side | + | ij | yes | side | + | fj | yes | turn | + | jk | yes | turn | + + + And the relations + | type | way:from | way:via | way:to | restriction | + | restriction | ab | bg,gh,hi,ij | jk | no_right_turn | + + When I route I should get + | from | to | route | + | a | k | main,turn,turn | + + + @restriction-way @overlap @no_turning @only_turning + Scenario: Car - Multiple via restriction with to,via,from sharing same node + # Example: https://www.openstreetmap.org/relation/3972923 + Given the node map + """ + e---d + | | + a---b---c + | + f + """ + + And the ways + | nodes | oneway | + | ab | yes | + | bc | yes | + | cd | yes | + | deb | yes | + | bf | yes | + + + And the relations + | type | way:from | way:via | way:to | restriction | + | restriction | ab | bc,cd,deb | bf | no_u_turn | + + And the relations + | type | way:from | node:via | way:to | restriction | + | restriction | ab | b | bc | only_straight_on | + | restriction | deb | b | bc | no_left_turn | + + When I route I should get + | from | to | route | + | a | f | | + # The last restriction is missing from OSM. Without it, + # it produces the route: ab,bc,cd,deb,bc,cd,deb,bf,bf + + + @restriction-way @except + Scenario: Car - Multiple via restriction, exception applies + # Example: https://www.openstreetmap.org/#map=19/50.04920/19.93251 + Given the node map + """ + a---b---c---d--e + """ + + And the ways + | nodes | oneway | + | ab | yes | + | bc | yes | + | cd | yes | + | de | yes | + + + And the relations + | type | way:from | way:via | way:to | restriction | except | + | restriction | ab | bc,cd | de | no_straight_on | motorcar | + + When I route I should get + | from | to | route | + | a | e | ab,bc,cd,de,de | + + + @restriction-way @except @no_turning + Scenario: Car - Multiple via restriction, exception n/a + # Example: https://www.openstreetmap.org/#map=19/50.04920/19.93251 + Given the node map + """ + a---b---c---d--e + """ + + And the ways + | nodes | oneway | + | ab | yes | + | bc | yes | + | cd | yes | + | de | yes | + + + And the relations + | type | way:from | way:via | way:to | restriction | except | + | restriction | ab | bc,cd | de | no_straight_on | psv;emergency | + + When I route I should get + | from | to | route | + | a | e | | + + + @restriction-way @overlap @only_turning + Scenario: Car - Multiple via restriction overlapping single via restriction + Given the node map + """ + e + | + a---b---c---d + | + f - g + | + h + """ + + And the ways + | nodes | name | + | ab | abcd | + | bc | abcd | + | cd | abcd | + | hf | hfb | + | fb | hfb | + | gf | gf | + | ce | ce | + + And the relations + | type | way:from | way:via | way:to | restriction | + | restriction | ab | bc | ce | only_left_turn | + | restriction | gf | fb,bc | cd | only_u_turn | + + When I route I should get + | from | to | route | turns | locations | + | a | d | abcd,ce,ce,abcd,abcd | depart,turn left,continue uturn,turn left,arrive | a,c,e,c,d | + | a | e | abcd,ce,ce | depart,turn left,arrive | a,c,e | + | a | f | abcd,hfb,hfb | depart,turn right,arrive | a,b,f | + | g | e | gf,hfb,abcd,abcd,ce,ce | depart,turn right,turn right,continue uturn,turn right,arrive | g,f,b,d,c,e | + | g | d | gf,hfb,abcd,abcd | depart,turn right,turn right,arrive | g,f,b,d | + | h | e | hfb,abcd,ce,ce | depart,end of road right,turn left,arrive | h,b,c,e | + | h | d | hfb,abcd,abcd | depart,end of road right,arrive | h,b,d | + + + @restriction-way + Scenario: Ambiguous from/to ways + Given the node map + """ + a + | + b---d---e + | | + c f + """ + + And the ways + | nodes | + | abc | + | bd | + | de | + | ef | + + And the relations + | type | way:from | way:via | way:to | restriction | + | restriction | abc | bd,de | ef | no_right_turn | + | restriction | ef | de,bd | abc | no_right_turn | + + When I route I should get + | from | to | route | locations | + | a | f | abc,bd,de,ef,ef | a,b,d,e,f | + | f | a | ef,de,bd,abc,abc | f,e,d,b,a | + | c | f | abc,bd,de,ef,ef | c,b,d,e,f | + | f | c | ef,de,bd,abc,abc | f,e,d,b,c | + + + @restriction-way + Scenario: Ambiguous via ways + Given the node map + """ + a + | + b---d---e---c + | + f + """ + + And the ways + | nodes | + | ab | + | bd | + | dec | + | ef | + + And the relations + | type | way:from | way:via | way:to | restriction | + | restriction | ab | bd,dec | ef | no_right_turn | + | restriction | ef | dec,bd | ab | no_right_turn | + + When I route I should get + | from | to | route | locations | + | a | f | ab,bd,dec,ef,ef | a,b,d,e,f | + | f | a | ef,dec,bd,ab,ab | f,e,d,b,a | + + + @restriction-way @invalid + Scenario: Badly tagged restrictions + Given the node map + """ + a--b--c--d--e--f + """ + + And the ways + | nodes | oneway | + | ab | yes | + | bc | yes | + | cd | yes | + | de | yes | + | ef | yes | + + And the relations + | type | way:from | way:via | way:to | restriction | + | restriction | ab | cd,de | ef | no_straight_on | + | restriction | ab | bc,de | ef | no_straight_on | + | restriction | ab | bc,cd | ef | no_straight_on | + | restriction | ef | de,cd | bc | no_straight_on | + + When I route I should get + | from | to | route | locations | + | a | f | ab,bc,cd,de,ef,ef | a,b,c,d,e,f | + diff --git a/features/car/restrictions.feature b/features/car/restrictions.feature index 80cb5b95e6b..da76f4dedc0 100644 --- a/features/car/restrictions.feature +++ b/features/car/restrictions.feature @@ -798,82 +798,6 @@ Feature: Car - Turn restrictions | from | to | route | | a | d | ab,be,de,de | - @restriction-way - Scenario: Multi Way restriction - Given the node map - """ - k j - | | - h - - g - f - - e - | | - | | - a - - b - c - - d - | | - l i - """ - - And the ways - | nodes | name | oneway | - | ab | horiz | yes | - | bc | horiz | yes | - | cd | horiz | yes | - | ef | horiz | yes | - | fg | horiz | yes | - | gh | horiz | yes | - | ic | vert | yes | - | cf | vert | yes | - | fj | vert | yes | - | kg | vert | yes | - | gb | vert | yes | - | bl | vert | yes | - - And the relations - | type | way:from | way:via | way:to | restriction | - | restriction | ab | bc,cf,fg | gh | no_u_turn | - - When I route I should get - | from | to | route | - | a | h | horiz,vert,horiz,horiz | - - @restriction-way - Scenario: Multi-Way overlapping single-way - Given the node map - """ - e - | - a - b - c - d - | - f - g - | - h - """ - - And the ways - | nodes | name | - | ab | abcd | - | bc | abcd | - | cd | abcd | - | hf | hfb | - | fb | hfb | - | gf | gf | - | ce | ce | - - And the relations - | type | way:from | way:via | way:to | restriction | - | restriction | ab | bc | ce | only_left_turn | - | restriction | gf | fb,bc | cd | only_u_turn | - - When I route I should get - | from | to | route | turns | locations | - | a | d | abcd,ce,ce,abcd,abcd | depart,turn left,continue uturn,turn left,arrive | a,c,e,c,d | - | a | e | abcd,ce,ce | depart,turn left,arrive | a,c,e | - | a | f | abcd,hfb,hfb | depart,turn right,arrive | a,b,f | - | g | e | gf,hfb,abcd,ce,ce | depart,turn right,turn right,turn left,arrive | g,f,b,c,e | - | g | d | gf,hfb,abcd,abcd | depart,turn right,turn right,arrive | g,f,b,d | - | h | e | hfb,abcd,ce,ce | depart,end of road right,turn left,arrive | h,b,c,e | - | h | d | hfb,abcd,abcd | depart,end of road right,arrive | h,b,d | - - @restriction-way Scenario: Car - prohibit turn, traffic lights Given the node map @@ -984,8 +908,6 @@ Feature: Car - Turn restrictions | restriction | ab | bge | de | no_right_turn | | restriction | bc | bge | ef | no_left_turn | - # this case is currently not handling the via-way restrictions and we need support for looking across traffic signals. - # It is mainly included to show limitations and to prove that we don't crash hard here When I route I should get | from | to | route | | a | d | ab,bge,ef,ef,de,de | diff --git a/include/extractor/edge_based_graph_factory.hpp b/include/extractor/edge_based_graph_factory.hpp index 9736cebf607..01a3122ea7f 100644 --- a/include/extractor/edge_based_graph_factory.hpp +++ b/include/extractor/edge_based_graph_factory.hpp @@ -12,8 +12,8 @@ #include "extractor/name_table.hpp" #include "extractor/nbg_to_ebg.hpp" #include "extractor/node_data_container.hpp" +#include "extractor/node_restriction_map.hpp" #include "extractor/query_node.hpp" -#include "extractor/restriction_index.hpp" #include "extractor/turn_lane_types.hpp" #include "extractor/way_restriction_map.hpp" diff --git a/include/extractor/extraction_containers.hpp b/include/extractor/extraction_containers.hpp index 13f949c9411..a8b8f776766 100644 --- a/include/extractor/extraction_containers.hpp +++ b/include/extractor/extraction_containers.hpp @@ -1,14 +1,16 @@ #ifndef EXTRACTION_CONTAINERS_HPP #define EXTRACTION_CONTAINERS_HPP -#include "extractor/first_and_last_segment_of_way.hpp" #include "extractor/internal_extractor_edge.hpp" +#include "extractor/nodes_of_way.hpp" #include "extractor/query_node.hpp" #include "extractor/restriction.hpp" #include "extractor/scripting_environment.hpp" #include "storage/tar_fwd.hpp" +#include + namespace osrm { namespace extractor @@ -22,9 +24,16 @@ namespace extractor */ class ExtractionContainers { + using ReferencedWays = std::unordered_map; + // The relationship between way and nodes is lost during node preparation. + // We identify the ways and nodes relevant to restrictions/overrides prior to + // node processing so that they can be referenced in the preparation phase. + ReferencedWays IdentifyRestrictionWays(); + ReferencedWays IdentifyManeuverOverrideWays(); + void PrepareNodes(); - void PrepareManeuverOverrides(); - void PrepareRestrictions(); + void PrepareManeuverOverrides(const ReferencedWays &maneuver_override_ways); + void PrepareRestrictions(const ReferencedWays &restriction_ways); void PrepareEdges(ScriptingEnvironment &scripting_environment); void WriteNodes(storage::tar::FileWriter &file_out) const; @@ -37,9 +46,10 @@ class ExtractionContainers using NodeVector = std::vector; using EdgeVector = std::vector; using AnnotationDataVector = std::vector; - using WayIDStartEndVector = std::vector; using NameCharData = std::vector; - using NameOffsets = std::vector; + using NameOffsets = std::vector; + using WayIDVector = std::vector; + using WayNodeIDOffsets = std::vector; std::vector barrier_nodes; std::vector traffic_signals; @@ -49,20 +59,18 @@ class ExtractionContainers AnnotationDataVector all_edges_annotation_data_list; NameCharData name_char_data; NameOffsets name_offsets; - // an adjacency array containing all turn lane masks - WayIDStartEndVector way_start_end_id_list; + WayIDVector ways_list; + // Offsets into used nodes for each way_list entry + WayNodeIDOffsets way_node_id_offsets; unsigned max_internal_node_id; - // list of restrictions 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, we have to remember the output restrictions - // before converting them to the internal formats - std::vector restrictions_list; - - // turn restrictions split into conditional and unconditional turn restrictions - std::vector conditional_turn_restrictions; - std::vector unconditional_turn_restrictions; + // 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, + // we have to remember the output restrictions before converting them to the internal formats + std::vector restrictions_list; + std::vector turn_restrictions; std::vector external_maneuver_overrides_list; std::vector internal_maneuver_overrides; diff --git a/include/extractor/extractor.hpp b/include/extractor/extractor.hpp index 2e333b7cc42..070a3b4ef59 100644 --- a/include/extractor/extractor.hpp +++ b/include/extractor/extractor.hpp @@ -42,6 +42,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "util/guidance/entry_class.hpp" #include "util/guidance/turn_lanes.hpp" +#include "restriction_graph.hpp" #include "util/typedefs.hpp" namespace osrm @@ -63,7 +64,6 @@ class Extractor std::tuple, - std::vector, std::vector> ParseOSMData(ScriptingEnvironment &scripting_environment, const unsigned number_of_threads); @@ -74,8 +74,7 @@ class Extractor const CompressedEdgeContainer &compressed_edge_container, const std::unordered_set &barrier_nodes, const std::unordered_set &traffic_lights, - const std::vector &turn_restrictions, - const std::vector &conditional_turn_restrictions, + const RestrictionGraph &restriction_graph, const std::unordered_set &segregated_edges, const NameTable &name_table, const std::vector &maneuver_overrides, @@ -97,23 +96,16 @@ class Extractor EdgeBasedNodeDataContainer &nodes_container) const; void BuildRTree(std::vector edge_based_node_segments, const std::vector &coordinates); - std::shared_ptr LoadRestrictionMap(); - void WriteConditionalRestrictions( - const std::string &path, - std::vector &conditional_turn_restrictions); - - void ProcessGuidanceTurns( - const util::NodeBasedDynamicGraph &node_based_graph, - const EdgeBasedNodeDataContainer &edge_based_node_container, - const std::vector &node_coordinates, - const CompressedEdgeContainer &compressed_edge_container, - const std::unordered_set &barrier_nodes, - const std::vector &turn_restrictions, - const std::vector &conditional_turn_restrictions, - const NameTable &name_table, - LaneDescriptionMap lane_description_map, - ScriptingEnvironment &scripting_environment); + void ProcessGuidanceTurns(const util::NodeBasedDynamicGraph &node_based_graph, + const EdgeBasedNodeDataContainer &edge_based_node_container, + const std::vector &node_coordinates, + const CompressedEdgeContainer &compressed_edge_container, + const std::unordered_set &barrier_nodes, + const RestrictionGraph &restriction_graph, + const NameTable &name_table, + LaneDescriptionMap lane_description_map, + ScriptingEnvironment &scripting_environment); }; } // namespace extractor } // namespace osrm diff --git a/include/extractor/extractor_callbacks.hpp b/include/extractor/extractor_callbacks.hpp index 6d8080d233c..9939a49631c 100644 --- a/include/extractor/extractor_callbacks.hpp +++ b/include/extractor/extractor_callbacks.hpp @@ -47,7 +47,7 @@ struct ExtractionNode; struct ExtractionWay; struct ExtractionRelation; struct ProfileProperties; -struct InputConditionalTurnRestriction; +struct InputTurnRestriction; struct InputManeuverOverride; /** @@ -87,7 +87,7 @@ class ExtractorCallbacks void ProcessNode(const osmium::Node ¤t_node, const ExtractionNode &result_node); // warning: caller needs to take care of synchronization! - void ProcessRestriction(const InputConditionalTurnRestriction &restriction); + void ProcessRestriction(const InputTurnRestriction &restriction); // warning: caller needs to take care of synchronization! void ProcessWay(const osmium::Way ¤t_way, const ExtractionWay &result_way); diff --git a/include/extractor/first_and_last_segment_of_way.hpp b/include/extractor/first_and_last_segment_of_way.hpp deleted file mode 100644 index 7fc512db696..00000000000 --- a/include/extractor/first_and_last_segment_of_way.hpp +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef FIRST_AND_LAST_SEGMENT_OF_WAY_HPP -#define FIRST_AND_LAST_SEGMENT_OF_WAY_HPP - -#include "util/typedefs.hpp" - -#include -#include - -namespace osrm -{ -namespace extractor -{ - -struct FirstAndLastSegmentOfWay -{ - OSMWayID way_id; - OSMNodeID first_segment_source_id; - OSMNodeID first_segment_target_id; - OSMNodeID last_segment_source_id; - OSMNodeID last_segment_target_id; - - FirstAndLastSegmentOfWay() - : way_id(SPECIAL_OSM_WAYID), first_segment_source_id(SPECIAL_OSM_NODEID), - first_segment_target_id(SPECIAL_OSM_NODEID), last_segment_source_id(SPECIAL_OSM_NODEID), - last_segment_target_id(SPECIAL_OSM_NODEID) - { - } - - FirstAndLastSegmentOfWay(OSMWayID w, OSMNodeID fs, OSMNodeID ft, OSMNodeID ls, OSMNodeID lt) - : way_id(std::move(w)), first_segment_source_id(std::move(fs)), - first_segment_target_id(std::move(ft)), last_segment_source_id(std::move(ls)), - last_segment_target_id(std::move(lt)) - { - } - - static FirstAndLastSegmentOfWay min_value() - { - return {MIN_OSM_WAYID, MIN_OSM_NODEID, MIN_OSM_NODEID, MIN_OSM_NODEID, MIN_OSM_NODEID}; - } - static FirstAndLastSegmentOfWay max_value() - { - return {MAX_OSM_WAYID, MAX_OSM_NODEID, MAX_OSM_NODEID, MAX_OSM_NODEID, MAX_OSM_NODEID}; - } -}; - -struct FirstAndLastSegmentOfWayCompare -{ - using value_type = FirstAndLastSegmentOfWay; - bool operator()(const FirstAndLastSegmentOfWay &a, const FirstAndLastSegmentOfWay &b) const - { - return a.way_id < b.way_id; - } - value_type max_value() { return FirstAndLastSegmentOfWay::max_value(); } - value_type min_value() { return FirstAndLastSegmentOfWay::min_value(); } -}; -} // namespace extractor -} // namespace osrm - -#endif /* FIRST_AND_LAST_SEGMENT_OF_WAY_HPP */ diff --git a/include/extractor/graph_compressor.hpp b/include/extractor/graph_compressor.hpp index 159e606ef7e..9a976e327e3 100644 --- a/include/extractor/graph_compressor.hpp +++ b/include/extractor/graph_compressor.hpp @@ -28,7 +28,6 @@ class GraphCompressor const std::unordered_set &traffic_lights, ScriptingEnvironment &scripting_environment, std::vector &turn_restrictions, - std::vector &conditional_turn_restrictions, std::vector &maneuver_overrides, util::NodeBasedDynamicGraph &graph, const std::vector &node_data_container, diff --git a/include/extractor/intersection/intersection_analysis.hpp b/include/extractor/intersection/intersection_analysis.hpp index f64f3eaf1a8..6dafcb7bc26 100644 --- a/include/extractor/intersection/intersection_analysis.hpp +++ b/include/extractor/intersection/intersection_analysis.hpp @@ -5,7 +5,7 @@ #include "extractor/intersection/intersection_edge.hpp" #include "extractor/intersection/intersection_view.hpp" #include "extractor/intersection/mergable_road_detector.hpp" -#include "extractor/restriction_index.hpp" +#include "extractor/node_restriction_map.hpp" #include "extractor/turn_lane_types.hpp" #include "util/coordinate.hpp" diff --git a/include/extractor/intersection/mergable_road_detector.hpp b/include/extractor/intersection/mergable_road_detector.hpp index 5f5ae535989..4871ad8292e 100644 --- a/include/extractor/intersection/mergable_road_detector.hpp +++ b/include/extractor/intersection/mergable_road_detector.hpp @@ -5,7 +5,7 @@ #include "extractor/intersection/coordinate_extractor.hpp" #include "extractor/intersection/have_identical_names.hpp" #include "extractor/name_table.hpp" -#include "extractor/restriction_index.hpp" +#include "extractor/node_restriction_map.hpp" #include "extractor/turn_lane_types.hpp" #include "guidance/intersection.hpp" diff --git a/include/extractor/node_based_graph_factory.hpp b/include/extractor/node_based_graph_factory.hpp index 77f6cb9af60..e3fc395839e 100644 --- a/include/extractor/node_based_graph_factory.hpp +++ b/include/extractor/node_based_graph_factory.hpp @@ -40,7 +40,6 @@ class NodeBasedGraphFactory NodeBasedGraphFactory(const boost::filesystem::path &input_file, ScriptingEnvironment &scripting_environment, std::vector &turn_restrictions, - std::vector &conditional_turn_restrictions, std::vector &maneuver_overrides); auto const &GetGraph() const { return compressed_output_graph; } @@ -69,7 +68,6 @@ class NodeBasedGraphFactory // edges into a single representative form void Compress(ScriptingEnvironment &scripting_environment, std::vector &turn_restrictions, - std::vector &conditional_turn_restrictions, std::vector &maneuver_overrides); // Most ways are bidirectional, making the geometry in forward and backward direction the same, diff --git a/include/extractor/node_restriction_map.hpp b/include/extractor/node_restriction_map.hpp new file mode 100644 index 00000000000..f2c9f049c11 --- /dev/null +++ b/include/extractor/node_restriction_map.hpp @@ -0,0 +1,80 @@ +#ifndef OSRM_EXTRACTOR_NODE_RESTRICTION_MAP_HPP_ +#define OSRM_EXTRACTOR_NODE_RESTRICTION_MAP_HPP_ + +#include "extractor/restriction.hpp" +#include "restriction_graph.hpp" +#include "util/typedefs.hpp" + +#include +#include + +#include +#include + +namespace osrm +{ +namespace extractor +{ + +// Allows easy check for whether a node restriction is present at a given intersection +template class NodeRestrictionMap +{ + public: + NodeRestrictionMap(const RestrictionGraph &restriction_graph) + : restriction_graph(restriction_graph), index_filter(RestrictionFilter{}) + { + } + + // Find all restrictions applicable to (from,via,*) turns + auto Restrictions(NodeID from, NodeID via) const + { + return getRange(from, via) | boost::adaptors::filtered(index_filter); + }; + + // Find all restrictions applicable to (from,via,to) turns + auto Restrictions(NodeID from, NodeID via, NodeID to) const + { + const auto turnFilter = [this, to](const auto &restriction) { + return index_filter(restriction) && restriction->IsTurnRestricted(to); + }; + return getRange(from, via) | boost::adaptors::filtered(turnFilter); + }; + + private: + RestrictionGraph::RestrictionRange getRange(NodeID from, NodeID via) const + { + const auto start_node_it = restriction_graph.start_edge_to_node.find({from, via}); + if (start_node_it != restriction_graph.start_edge_to_node.end()) + { + return restriction_graph.GetRestrictions(start_node_it->second); + } + return {}; + } + const RestrictionGraph &restriction_graph; + const RestrictionFilter index_filter; +}; + +// Restriction filters to use as index defaults +struct ConditionalOnly +{ + bool operator()(const TurnRestriction *restriction) const + { + return !restriction->IsUnconditional(); + }; +}; + +struct UnconditionalOnly +{ + bool operator()(const TurnRestriction *restriction) const + { + return restriction->IsUnconditional(); + }; +}; + +using RestrictionMap = NodeRestrictionMap; +using ConditionalRestrictionMap = NodeRestrictionMap; + +} // namespace extractor +} // namespace osrm + +#endif // OSRM_EXTRACTOR_NODE_RESTRICTION_MAP_HPP_ diff --git a/include/extractor/nodes_of_way.hpp b/include/extractor/nodes_of_way.hpp new file mode 100644 index 00000000000..d7f19c70598 --- /dev/null +++ b/include/extractor/nodes_of_way.hpp @@ -0,0 +1,56 @@ +#ifndef NODES_OF_WAY_HPP +#define NODES_OF_WAY_HPP + +#include "util/typedefs.hpp" + +#include +#include +#include + +namespace osrm +{ +namespace extractor +{ + +// NodesOfWay contains the ordered nodes of a way and provides convenience functions for getting +// nodes in the first and last segments. +struct NodesOfWay +{ + OSMWayID way_id; + std::vector node_ids; + + NodesOfWay() : way_id(SPECIAL_OSM_WAYID), node_ids{SPECIAL_OSM_NODEID, SPECIAL_OSM_NODEID} {} + + NodesOfWay(OSMWayID w, std::vector ns) : way_id(w), node_ids(std::move(ns)) {} + + static NodesOfWay min_value() { return {MIN_OSM_WAYID, {MIN_OSM_NODEID, MIN_OSM_NODEID}}; } + static NodesOfWay max_value() { return {MAX_OSM_WAYID, {MAX_OSM_NODEID, MAX_OSM_NODEID}}; } + + const OSMNodeID &first_segment_source_id() const + { + BOOST_ASSERT(!node_ids.empty()); + return node_ids[0]; + } + + const OSMNodeID &first_segment_target_id() const + { + BOOST_ASSERT(node_ids.size() > 1); + return node_ids[1]; + } + + const OSMNodeID &last_segment_source_id() const + { + BOOST_ASSERT(node_ids.size() > 1); + return node_ids[node_ids.size() - 2]; + } + + const OSMNodeID &last_segment_target_id() const + { + BOOST_ASSERT(!node_ids.empty()); + return node_ids[node_ids.size() - 1]; + } +}; +} // namespace extractor +} // namespace osrm + +#endif /* NODES_OF_WAY_HPP */ diff --git a/include/extractor/restriction.hpp b/include/extractor/restriction.hpp index a05fccdc0f7..8d7f64657ff 100644 --- a/include/extractor/restriction.hpp +++ b/include/extractor/restriction.hpp @@ -30,17 +30,19 @@ struct InputNodeRestriction OSMWayID to; }; -// A restriction that uses a single via-way in between +// A restriction that uses one or more via-way in between // -// f - e - d +// e - f - g +// | +// d // | // a - b - c // -// ab via be to ef -- no u turn +// ab via bd,df to fe -- no u turn struct InputWayRestriction { OSMWayID from; - OSMWayID via; + std::vector via; OSMWayID to; }; @@ -57,6 +59,9 @@ struct InputTurnRestriction // keep in the same order as the turn restrictions below mapbox::util::variant node_or_way; bool is_only; + // We represent conditional and unconditional restrictions with the same structure. + // Unconditional restrictions will have empty conditions. + std::vector condition; OSMWayID From() const { @@ -102,13 +107,15 @@ struct InputTurnRestriction return mapbox::util::get(node_or_way); } }; -struct InputConditionalTurnRestriction : InputTurnRestriction -{ - std::vector condition; -}; // OSRM manages restrictions based on node IDs which refer to the last node along the edge. Note // that this has the side-effect of not allowing parallel edges! +// +// a - b - c +// | +// d +// +// ab via b to bd struct NodeRestriction { NodeID from; @@ -131,39 +138,46 @@ struct NodeRestriction // compression happening in the graph creation process which would make it difficult to track // way-ids over a series of operations. Having access to the nodes directly allows look-up of the // edges in the processed structures +// +// e - f - g +// | +// d +// | +// a - b - c +// +// ab via bd,df to fe -- no u turn struct WayRestriction { - // a way restriction in OSRM is essentially a dual node turn restriction; - // - // | | - // c -x- b - // | | - // d a - // - // from ab via bxc to cd: no_uturn - // - // Technically, we would need only a,b,c,d to describe the full turn in terms of nodes. When - // parsing the relation, though, we do not know about the final representation in the node-based - // graph for the restriction. In case of a traffic light, for example, we might end up with bxc - // not being compressed to bc. For that reason, we need to maintain two node restrictions in - // case a way restrction is not fully collapsed - NodeRestriction in_restriction; - NodeRestriction out_restriction; + // A way restriction in OSRM needs to track all nodes that make up the via ways. Whilst most + // of these nodes will be removed by compression, some nodes will contain features that need to + // be considered when routing (e.g. intersections, nested restrictions, etc). + NodeID from; + std::vector via; + NodeID to; + + // check if all parts of the restriction reference an actual node + bool Valid() const + { + return from != SPECIAL_NODEID && to != SPECIAL_NODEID && via.size() >= 2 && + std::all_of(via.begin(), via.end(), [](NodeID i) { return i != SPECIAL_NODEID; }); + }; bool operator==(const WayRestriction &other) const { - return std::tie(in_restriction, out_restriction) == - std::tie(other.in_restriction, other.out_restriction); + return std::tie(from, via, to) == std::tie(other.from, other.via, other.to); } }; // Wrapper for turn restrictions that gives more information on its type / handles the switch -// between node/way/multi-way restrictions +// between node/way restrictions struct TurnRestriction { // keep in the same order as the turn restrictions above mapbox::util::variant node_or_way; bool is_only; + // We represent conditional and unconditional restrictions with the same structure. + // Unconditional restrictions will have empty conditions. + std::vector condition; // construction for NodeRestrictions explicit TurnRestriction(NodeRestriction node_restriction, bool is_only = false) @@ -179,9 +193,40 @@ struct TurnRestriction explicit TurnRestriction() { - node_or_way = NodeRestriction{SPECIAL_EDGEID, SPECIAL_NODEID, SPECIAL_EDGEID}; + node_or_way = NodeRestriction{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID}; } + NodeID To() const + { + return node_or_way.which() == RestrictionType::NODE_RESTRICTION + ? mapbox::util::get(node_or_way).to + : mapbox::util::get(node_or_way).to; + } + + NodeID From() const + { + return node_or_way.which() == RestrictionType::NODE_RESTRICTION + ? mapbox::util::get(node_or_way).from + : mapbox::util::get(node_or_way).from; + } + + NodeID FirstVia() const + { + if (node_or_way.which() == RestrictionType::NODE_RESTRICTION) + { + return mapbox::util::get(node_or_way).via; + } + else + { + BOOST_ASSERT(!mapbox::util::get(node_or_way).via.empty()); + return mapbox::util::get(node_or_way).via[0]; + } + } + + bool IsTurnRestricted(NodeID to) const { return is_only ? To() != to : To() == to; } + + bool IsUnconditional() const { return condition.empty(); } + WayRestriction &AsWayRestriction() { BOOST_ASSERT(node_or_way.which() == RestrictionType::WAY_RESTRICTION); @@ -218,7 +263,7 @@ struct TurnRestriction if (node_or_way.which() == RestrictionType::WAY_RESTRICTION) { auto const &restriction = AsWayRestriction(); - return restriction.in_restriction.Valid() && restriction.out_restriction.Valid(); + return restriction.Valid(); } else { @@ -245,11 +290,6 @@ struct TurnRestriction } } }; - -struct ConditionalTurnRestriction : TurnRestriction -{ - std::vector condition; -}; } // namespace extractor } // namespace osrm diff --git a/include/extractor/restriction_compressor.hpp b/include/extractor/restriction_compressor.hpp index 42675e7161c..500ec18e3cc 100644 --- a/include/extractor/restriction_compressor.hpp +++ b/include/extractor/restriction_compressor.hpp @@ -16,8 +16,8 @@ namespace extractor struct NodeRestriction; struct TurnRestriction; -// OSRM stores restrictions in the form node -> node -> node instead of way -> node -> way (or -// way->way->way) as it is done in OSM. These restrictions need to match the state of graph +// OSRM stores restrictions as node -> [node] -> node instead of way -> node -> way (or +// way->[way]->way) as it is done in OSM. These restrictions 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). // Since this graph compression ins performed after creating the restrictions in the extraction @@ -29,19 +29,22 @@ class RestrictionCompressor { public: RestrictionCompressor(std::vector &restrictions, - std::vector &conditional_turn_restrictions, std::vector &maneuver_overrides); // account for the compression of `from-via-to` into `from-to` void Compress(const NodeID from, const NodeID via, const NodeID to); private: - // a turn restriction is given as `from star via node to end`. Edges ending at `head` being + // A turn restriction is given as `from star via node to end`. Edges ending at `head` being // contracted move the head pointer to their respective head. Edges starting at tail move the - // tail values to their respective tails. Way turn restrictions are represented by two - // node-restrictions, so we can focus on them alone - boost::unordered_multimap starts; - boost::unordered_multimap ends; + // tail values to their respective tails. + // Via nodes that are compressed are removed from the restriction representation. + // We do not compress the first and last via nodes of a restriction as they act as + // entrance/exit points into the restriction graph. For a node restriction, the first and last + // via nodes are the same. + boost::unordered_multimap starts; + boost::unordered_multimap vias; + boost::unordered_multimap ends; boost::unordered_multimap maneuver_starts; boost::unordered_multimap maneuver_ends; diff --git a/include/extractor/restriction_filter.hpp b/include/extractor/restriction_filter.hpp index 0b23e307666..2bdc8831e94 100644 --- a/include/extractor/restriction_filter.hpp +++ b/include/extractor/restriction_filter.hpp @@ -14,10 +14,8 @@ namespace extractor // To avoid handling invalid restrictions / creating unnecessary duplicate nodes for via-ways, we do // a pre-flight check for restrictions and remove all invalid restrictions from the data. Use as // `restrictions = removeInvalidRestrictions(std::move(restrictions))` -std::vector -removeInvalidRestrictions(std::vector, - const util::NodeBasedDynamicGraph &); - +std::vector removeInvalidRestrictions(std::vector, + const util::NodeBasedDynamicGraph &); } // namespace extractor } // namespace osrm diff --git a/include/extractor/restriction_graph.hpp b/include/extractor/restriction_graph.hpp new file mode 100644 index 00000000000..7f880b1fc08 --- /dev/null +++ b/include/extractor/restriction_graph.hpp @@ -0,0 +1,134 @@ +#ifndef OSRM_EXTRACTOR_RESTRICTION_GRAPH_HPP_ +#define OSRM_EXTRACTOR_RESTRICTION_GRAPH_HPP_ + +#include +#include + +#include "extractor/restriction_filter.hpp" +#include "util/node_based_graph.hpp" +#include "util/typedefs.hpp" + +namespace osrm +{ +namespace extractor +{ + +namespace restriction_graph_details +{ +struct transferBuilder; +struct pathBuilder; +} // namespace restriction_graph_details + +struct RestrictionEdge +{ + NodeID node_based_to; + RestrictionID target; + bool is_transfer; +}; + +struct RestrictionNode +{ + size_t restrictions_begin_idx; + size_t num_restrictions; + size_t edges_begin_idx; + size_t num_edges; +}; + +/** + * The restriction graph is used to represent all possible restrictions within the routing graph. + * The graph uses an edge-based node representation. Each node represents a compressed + * node-based edge along a restriction path. + * + * Given a list of turn restrictions, the graph is created in multiple steps. + * + * INPUT + * a -> b -> d: no_e + * a -> b -> c: only_d + * b -> c -> d: only_f + * b -> c: no_g + * e -> b -> d: no_g + * + * Step 1: create a disjoint union of prefix trees for all restriction paths. + * The restriction instructions are added to the final node in its path. + * + * STEP 1 + * (a,b) -> (b,d,[no_e]) + * \->(b,c,[only_d]) + * + * (b,c,[no_g]) -> (c,d,[only_f]) + * (e,b) -> (b,d,[no_g]) + * + * Step 2: add transfers between restriction paths that overlap. + * We do this by traversing each restriction path, tracking where the suffix of our current path + * matches the prefix of any other. If it does, there's opportunity to transfer to the suffix + * restriction path *if* the transfer would not be restricted *and* that edge does not take us + * further on our current path. Nested restrictions are also added from any of the suffix paths. + * + * STEP 2 + * (a,b) -> (b,d,[no_e]) + * \->(b,c,[only_d,no_g]) + * \ + * (b,c,[no_g]) -> \-> (c,d,[only_f]) + * + * (e,b) -> (b,d,[no_g]) + * If a transfer applies to multiple suffix paths, we only add the edge to the largest suffix path. + * This ensures we correctly track all overlapping paths. + * + * + * Step 3: The nodes are split into + * start nodes - compressed edges that are entry points into a restriction path. + * via nodes - compressed edges that are intermediate steps along a restriction path. + * Start nodes and via nodes are indexed by the compressed node-based edge for easy retrieval + * + * STEP 3 + * Start Node Index + * (a,b) => (a,b) + * (b,c) => (b,c,[no_g]) + * (e,b) => (e,b) + * + * Via Nodes Index + * (b,c) => (b,c,[only_d,no_g]) + * (b,d) => (b,d,[no_e]) , (b,d,[no_g]) + * (c,d) => (c,d,[only_f]) + * + * Duplicate Nodes: + * There is a 1-1 mapping between restriction graph via nodes and edge-based-graph duplicate nodes. + * This relationship is used in the creation of restrictions in the routing edge-based graph. + * */ +struct RestrictionGraph +{ + friend restriction_graph_details::pathBuilder; + friend restriction_graph_details::transferBuilder; + friend RestrictionGraph constructRestrictionGraph(const std::vector &); + + using EdgeRange = boost::iterator_range::const_iterator>; + using RestrictionRange = + boost::iterator_range::const_iterator>; + using EdgeKey = std::pair; + + // Helper functions for iterating over node restrictions and edges + EdgeRange GetEdges(RestrictionID id) const; + RestrictionRange GetRestrictions(RestrictionID id) const; + + // A compressed node-based edge can only have one start node in the restriction graph. + boost::unordered_map start_edge_to_node{}; + // A compressed node-based edge can have multiple via nodes in the restriction graph + // (as the compressed edge can appear in paths with different prefixes). + boost::unordered_multimap via_edge_to_node{}; + std::vector nodes; + // TODO: Investigate reusing DynamicGraph. Currently it requires specific attributes + // (e.g. reversed, weight) that would not make sense for restrictions. + std::vector edges; + std::vector restrictions; + size_t num_via_nodes{}; + + private: + RestrictionGraph() = default; +}; + +RestrictionGraph constructRestrictionGraph(const std::vector &turn_restrictions); + +} // namespace extractor +} // namespace osrm + +#endif // OSRM_EXTRACTOR_RESTRICTION_GRAPH_HPP_ diff --git a/include/extractor/restriction_index.hpp b/include/extractor/restriction_index.hpp deleted file mode 100644 index 350a89d5ea0..00000000000 --- a/include/extractor/restriction_index.hpp +++ /dev/null @@ -1,101 +0,0 @@ -#ifndef OSRM_EXTRACTOR_RESTRICTION_INDEX_HPP_ -#define OSRM_EXTRACTOR_RESTRICTION_INDEX_HPP_ - -#include "extractor/restriction.hpp" -#include "util/typedefs.hpp" - -#include - -#include -#include - -namespace osrm -{ -namespace extractor -{ - -// allows easy check for whether a node intersection is present at a given intersection -template class RestrictionIndex -{ - public: - using value_type = restriction_type; - - template - RestrictionIndex(std::vector &restrictions, extractor_type extractor); - - bool IsIndexed(NodeID first, NodeID second) const; - - auto Restrictions(NodeID first, NodeID second) const - { - return restriction_hash.equal_range(std::make_pair(first, second)); - }; - - auto Size() const { return restriction_hash.size(); } - - private: - boost::unordered_multimap, restriction_type *> restriction_hash; -}; - -template -template -RestrictionIndex::RestrictionIndex(std::vector &restrictions, - extractor_type extractor) -{ - // build a multi-map - for (auto &restriction : restrictions) - restriction_hash.insert(std::make_pair(extractor(restriction), &restriction)); -} - -template -bool RestrictionIndex::IsIndexed(const NodeID first, const NodeID second) const -{ - return restriction_hash.count(std::make_pair(first, second)); -} - -struct IndexNodeByFromAndVia -{ - std::pair operator()(const TurnRestriction &restriction) - { - const auto &node = restriction.AsNodeRestriction(); - return std::make_pair(node.from, node.via); - }; -}; - -// check wheter a turn is restricted within a restriction_index -template -std::pair -isRestricted(const NodeID from, - const NodeID via, - const NodeID to, - const restriction_map_type &restriction_map) -{ - const auto range = restriction_map.Restrictions(from, via); - - // check if a given node_restriction is targeting node - const auto to_is_restricted = [to](const auto &pair) { - const auto &restriction = *pair.second; - if (restriction.Type() == RestrictionType::NODE_RESTRICTION) - { - auto const &as_node = restriction.AsNodeRestriction(); - auto const restricted = restriction.is_only ? (to != as_node.to) : (to == as_node.to); - - return restricted; - } - return false; - }; - - auto itr = std::find_if(range.first, range.second, to_is_restricted); - - if (itr != range.second) - return {true, itr->second}; - else - return {false, NULL}; -} - -using RestrictionMap = RestrictionIndex; -using ConditionalRestrictionMap = RestrictionIndex; - -} // namespace extractor -} // namespace osrm - -#endif // OSRM_EXTRACTOR_RESTRICTION_INDEX_HPP_ diff --git a/include/extractor/restriction_parser.hpp b/include/extractor/restriction_parser.hpp index cb8304617c4..bf3f264554b 100644 --- a/include/extractor/restriction_parser.hpp +++ b/include/extractor/restriction_parser.hpp @@ -44,8 +44,7 @@ class RestrictionParser RestrictionParser(bool use_turn_restrictions, bool parse_conditionals, std::vector &restrictions); - boost::optional - TryParse(const osmium::Relation &relation) const; + boost::optional TryParse(const osmium::Relation &relation) const; private: bool ShouldIgnoreRestriction(const std::string &except_tag_string) const; diff --git a/include/extractor/scripting_environment.hpp b/include/extractor/scripting_environment.hpp index 2de5d96386d..888a75eee91 100644 --- a/include/extractor/scripting_environment.hpp +++ b/include/extractor/scripting_environment.hpp @@ -68,7 +68,7 @@ class ScriptingEnvironment const ExtractionRelationContainer &relations, std::vector> &resulting_nodes, std::vector> &resulting_ways, - std::vector &resulting_restrictions, + std::vector &resulting_restrictions, std::vector &resulting_maneuver_overrides) = 0; virtual bool HasLocationDependentData() const = 0; diff --git a/include/extractor/scripting_environment_lua.hpp b/include/extractor/scripting_environment_lua.hpp index 575f586fd2d..793636dd42b 100644 --- a/include/extractor/scripting_environment_lua.hpp +++ b/include/extractor/scripting_environment_lua.hpp @@ -92,7 +92,7 @@ class Sol2ScriptingEnvironment final : public ScriptingEnvironment const ExtractionRelationContainer &relations, std::vector> &resulting_nodes, std::vector> &resulting_ways, - std::vector &resulting_restrictions, + std::vector &resulting_restrictions, std::vector &resulting_maneuver_overrides) override; bool HasLocationDependentData() const override { return !location_dependent_data.empty(); } diff --git a/include/extractor/way_restriction_map.hpp b/include/extractor/way_restriction_map.hpp index 636c12747a7..da29aff674b 100644 --- a/include/extractor/way_restriction_map.hpp +++ b/include/extractor/way_restriction_map.hpp @@ -8,82 +8,66 @@ #include #include "extractor/restriction.hpp" -#include "extractor/restriction_index.hpp" +#include "extractor/restriction_graph.hpp" #include "util/integer_range.hpp" #include "util/typedefs.hpp" -// Given the compressed representation of via-way turn restrictions, we provide a fast access into -// the restrictions to indicate which turns may be restricted due to a way in between namespace osrm { namespace extractor { -// The WayRestrictionMap uses ConditionalTurnRestrictions in general. Most restrictions will have -// empty conditions, though. +// Given the compressed representation of via-way turn restrictions, we provide a fast access into +// the restrictions to indicate which turns may be restricted due to a way in between. class WayRestrictionMap { public: - struct ViaWay + struct ViaEdge { NodeID from; NodeID to; }; - WayRestrictionMap(const std::vector &conditional_restrictions); + WayRestrictionMap(const RestrictionGraph &restriction_graph); - // Check if an edge between two nodes is a restricted turn. The check needs to be performed to + // Check if an edge between two nodes is part of a via-way. The check needs to be performed to // find duplicated nodes during the creation of edge-based-edges - bool IsViaWay(const NodeID from, const NodeID to) const; + bool IsViaWayEdge(NodeID from, NodeID to) const; - // Every via-way results in a duplicated node that is required in the edge-based-graph. This - // count is essentially the same as the number of valid via-way restrictions (except for - // non-only restrictions that share the same in/via combination) + // There is a bijection between restriction graph via-nodes and edge-based duplicated nodes. + // This counts the number of duplicated nodes contributed by the way restrictions. std::size_t NumberOfDuplicatedNodes() const; - // Returns a representative for each duplicated node, consisting of the representative ID (first - // ID of the nodes restrictions) and the from/to vertices of the via-way - // This is used to construct edge based nodes that act as intermediate nodes. - std::vector DuplicatedNodeRepresentatives() const; + // For each restriction graph via-node, we return its node-based edge representation (from,to). + // This is used to create the duplicate node in the edge based graph. + std::vector DuplicatedViaEdges() const; + + // Access all duplicated NodeIDs from the restriction graph via-node represented by (from,to) + std::vector DuplicatedNodeIDs(NodeID from, NodeID to) const; + + // Check whether a turn onto a given node is restricted, when coming from a duplicated node + bool IsRestricted(DuplicatedNodeID duplicated_node, NodeID to) const; - // Access all duplicated NodeIDs for a set of nodes indicating a via way - std::vector DuplicatedNodeIDs(const NodeID from, const NodeID to) const; + // Get the restrictions resulting in ^ IsRestricted. Requires IsRestricted to evaluate to true + std::vector GetRestrictions(DuplicatedNodeID duplicated_node, + NodeID to) const; - // check whether a turn onto a given node is restricted, when coming from a duplicated node - bool IsRestricted(DuplicatedNodeID duplicated_node, const NodeID to) const; - // Get the restriction resulting in ^ IsRestricted. Requires IsRestricted to evaluate to true - const ConditionalTurnRestriction &GetRestriction(DuplicatedNodeID duplicated_node, - const NodeID to) const; + // Changes edge_based_node to the correct duplicated_node_id in case node_based_from, + // node_based_via, node_based_to can be identified as the start of a way restriction. + NodeID RemapIfRestrictionStart(NodeID edge_based_node, + NodeID node_based_from, + NodeID node_based_via, + NodeID node_based_to, + NodeID number_of_edge_based_nodes) const; - // changes edge_based_node to the correct duplicated_node_id in case node_based_from, - // node_based_via, node_based_to can be identified with a restriction group - NodeID RemapIfRestricted(const NodeID edge_based_node, - const NodeID node_based_from, - const NodeID node_based_via, - const NodeID node_based_to, - const NodeID number_of_edge_based_nodes) const; + // Changes edge_based_node to the correct duplicated_node_id in case node_based_from, + // node_based_via, node_based_to can be identified as successive via steps of a way restriction. + NodeID RemapIfRestrictionVia(NodeID edge_based_target_node, + NodeID edge_based_via_node, + NodeID node_based_to, + NodeID number_of_edge_based_nodes) const; private: - DuplicatedNodeID AsDuplicatedNodeID(const RestrictionID restriction_id) const; - - // access all restrictions that have the same starting way and via way. Any duplicated node - // represents the same in-way + via-way combination. This vector contains data about all - // restrictions and their assigned duplicated nodes. It indicates the minimum restriciton ID - // that is represented by the next node. The ID of a node is defined as the position of the - // lower bound of the restrictions ID within this array - // - // a - b - // | - // y - c - x - // - // restriction nodes | restriction id - // a - b - c - x : 5 - // a - b - c - y : 6 - // - // EBN: 0 . | 2 | 3 | 4 ... - // duplicated node groups: ... | 5 | 7 | ... - std::vector duplicated_node_groups; - std::vector restriction_data; - RestrictionIndex restriction_starts; + const RestrictionGraph &restriction_graph; }; } // namespace extractor diff --git a/include/guidance/guidance_processing.hpp b/include/guidance/guidance_processing.hpp index 26122e6cead..6d2aadf7164 100644 --- a/include/guidance/guidance_processing.hpp +++ b/include/guidance/guidance_processing.hpp @@ -6,6 +6,7 @@ #include "extractor/compressed_edge_container.hpp" #include "extractor/name_table.hpp" #include "extractor/node_data_container.hpp" +#include "extractor/node_restriction_map.hpp" #include "extractor/suffix_table.hpp" #include "extractor/turn_lane_types.hpp" #include "extractor/way_restriction_map.hpp" diff --git a/include/guidance/turn_analysis.hpp b/include/guidance/turn_analysis.hpp index 239b9365fbb..7a97af725e6 100644 --- a/include/guidance/turn_analysis.hpp +++ b/include/guidance/turn_analysis.hpp @@ -4,7 +4,7 @@ #include "extractor/compressed_edge_container.hpp" #include "extractor/intersection/intersection_view.hpp" #include "extractor/name_table.hpp" -#include "extractor/restriction_index.hpp" +#include "extractor/node_restriction_map.hpp" #include "extractor/suffix_table.hpp" #include "guidance/driveway_handler.hpp" diff --git a/include/guidance/turn_discovery.hpp b/include/guidance/turn_discovery.hpp index cff8103ff9a..52b72f8467c 100644 --- a/include/guidance/turn_discovery.hpp +++ b/include/guidance/turn_discovery.hpp @@ -1,7 +1,7 @@ #ifndef OSRM_GUIDANCE_TURN_DISCOVERY_HPP_ #define OSRM_GUIDANCE_TURN_DISCOVERY_HPP_ -#include "extractor/restriction_index.hpp" +#include "extractor/node_restriction_map.hpp" #include "guidance/intersection.hpp" #include "guidance/turn_lane_data.hpp" #include "util/typedefs.hpp" diff --git a/include/util/for_each_indexed.hpp b/include/util/for_each_indexed.hpp new file mode 100644 index 00000000000..bae2fdd8015 --- /dev/null +++ b/include/util/for_each_indexed.hpp @@ -0,0 +1,31 @@ +#ifndef FOR_EACH_INDEXED_HPP +#define FOR_EACH_INDEXED_HPP + +#include +#include +#include + +namespace osrm +{ +namespace util +{ + +template +void for_each_indexed(ForwardIterator first, ForwardIterator last, Function function) +{ + for (size_t i = 0; first != last; ++first, ++i) + { + function(i, *first); + } +} + +template +void for_each_pair(ContainerT &container, Function function) +{ + for_each_indexed(std::begin(container), std::end(container), function); +} + +} // namespace util +} // namespace osrm + +#endif /* FOR_EACH_INDEXED_HPP */ diff --git a/include/util/permutation.hpp b/include/util/permutation.hpp index 907efc4da02..664889cc06e 100644 --- a/include/util/permutation.hpp +++ b/include/util/permutation.hpp @@ -61,8 +61,8 @@ void inplacePermutation(RandomAccessIterator begin, template std::vector orderingToPermutation(const std::vector &ordering) { - std::vector permutation(ordering.size()); - for (auto index : util::irange(0, ordering.size())) + std::vector permutation(ordering.size()); + for (auto index : util::irange(0, ordering.size())) permutation[ordering[index]] = index; return permutation; diff --git a/include/util/typedefs.hpp b/include/util/typedefs.hpp index 7807b4bafe2..b3e5a8d461b 100644 --- a/include/util/typedefs.hpp +++ b/include/util/typedefs.hpp @@ -103,6 +103,7 @@ static const NodeID SPECIAL_NODEID = std::numeric_limits::max(); static const NodeID SPECIAL_SEGMENTID = std::numeric_limits::max() >> 1; static const NodeID SPECIAL_GEOMETRYID = std::numeric_limits::max() >> 1; static const EdgeID SPECIAL_EDGEID = std::numeric_limits::max(); +static const RestrictionID SPECIAL_RESTRICTIONID = std::numeric_limits::max(); static const NameID INVALID_NAMEID = std::numeric_limits::max(); static const NameID EMPTY_NAMEID = 0; static const unsigned INVALID_COMPONENTID = 0; diff --git a/src/extractor/edge_based_graph_factory.cpp b/src/extractor/edge_based_graph_factory.cpp index 1e013fa33af..ba9b1e85bf2 100644 --- a/src/extractor/edge_based_graph_factory.cpp +++ b/src/extractor/edge_based_graph_factory.cpp @@ -239,7 +239,7 @@ void EdgeBasedGraphFactory::Run( const std::string &cnbg_ebg_mapping_path, const std::string &conditional_penalties_filename, const std::string &maneuver_overrides_filename, - const RestrictionMap &node_restriction_map, + const RestrictionMap &unconditional_node_restriction_map, const ConditionalRestrictionMap &conditional_node_restriction_map, const WayRestrictionMap &way_restriction_map, const std::vector &unresolved_maneuver_overrides) @@ -268,7 +268,7 @@ void EdgeBasedGraphFactory::Run( turn_penalties_index_filename, conditional_penalties_filename, maneuver_overrides_filename, - node_restriction_map, + unconditional_node_restriction_map, conditional_node_restriction_map, way_restriction_map, unresolved_maneuver_overrides); @@ -374,17 +374,17 @@ EdgeBasedGraphFactory::GenerateEdgeExpandedNodes(const WayRestrictionMap &way_re // Add copies of the nodes { util::UnbufferedLog log; - const auto via_ways = way_restriction_map.DuplicatedNodeRepresentatives(); - util::Percent progress(log, via_ways.size()); + const auto via_edges = way_restriction_map.DuplicatedViaEdges(); + util::Percent progress(log, via_edges.size()); NodeID edge_based_node_id = NodeID(m_number_of_edge_based_nodes - way_restriction_map.NumberOfDuplicatedNodes()); std::size_t progress_counter = 0; // allocate enough space for the mapping - for (const auto way : via_ways) + for (const auto edge : via_edges) { - const auto node_u = way.from; - const auto node_v = way.to; + const auto node_u = edge.from; + const auto node_v = edge.to; // we know that the edge exists as non-reversed edge const auto eid = m_node_based_graph.FindEdge(node_u, node_v); @@ -437,8 +437,8 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( const std::string &turn_penalties_index_filename, const std::string &conditional_penalties_filename, const std::string &maneuver_overrides_filename, - const RestrictionMap &node_restriction_map, - const ConditionalRestrictionMap &conditional_restriction_map, + const RestrictionMap &unconditional_node_restriction_map, + const ConditionalRestrictionMap &conditional_node_restriction_map, const WayRestrictionMap &way_restriction_map, const std::vector &unresolved_maneuver_overrides) { @@ -452,7 +452,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( m_edge_based_node_container, m_coordinates, m_compressed_edge_container, - node_restriction_map, + unconditional_node_restriction_map, m_barrier_nodes, turn_lanes_data, name_table, @@ -544,10 +544,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( }); // Generate edges for either artificial nodes or the main graph - const auto generate_edge = [this, - &scripting_environment, - weight_multiplier, - &conditional_restriction_map]( + 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 @@ -563,24 +560,6 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( const auto &road_legs_on_the_right, const auto &road_legs_on_the_left, const auto &edge_geometries) { - const auto node_restricted = - isRestricted(node_along_road_entering, - intersection_node, - m_node_based_graph.GetTarget(node_based_edge_to), - conditional_restriction_map); - - boost::optional conditional = boost::none; - if (node_restricted.first) - { - auto const &conditions = node_restricted.second->condition; - // get conditions of the restriction limiting the node - conditional = {{edge_based_node_from, - edge_based_node_to, - {static_cast(-1), - m_coordinates[intersection_node], - conditions}}}; - } - 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); @@ -676,9 +655,8 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( lookup::TurnIndexBlock turn_index_block = {from_node, intersection_node, to_node}; // insert data into the designated buffer - return std::make_pair( - EdgeWithData{edge_based_edge, turn_index_block, weight_penalty, duration_penalty}, - conditional); + return EdgeWithData{ + edge_based_edge, turn_index_block, weight_penalty, duration_penalty}; }; // @@ -739,7 +717,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( const auto intersection_view = convertToIntersectionView(m_node_based_graph, m_edge_based_node_container, - node_restriction_map, + unconditional_node_restriction_map, m_barrier_nodes, edge_geometries, turn_lanes_data, @@ -747,16 +725,16 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( outgoing_edges, merged_edge_ids); - // check if we are turning off a via way - const auto turning_off_via_way = - way_restriction_map.IsViaWay(incoming_edge.node, intersection_node); + // check if this edge is part of a restriction via-way + const auto is_restriction_via_edge = + way_restriction_map.IsViaWayEdge(incoming_edge.node, intersection_node); for (const auto &outgoing_edge : outgoing_edges) { auto is_turn_allowed = intersection::isTurnAllowed(m_node_based_graph, m_edge_based_node_container, - node_restriction_map, + unconditional_node_restriction_map, m_barrier_nodes, edge_geometries, turn_lanes_data, @@ -850,7 +828,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( } // In case a way restriction starts at a given location, add a turn onto - // every artificial node eminating here. + // every artificial node emanating here. // // e - f // | @@ -861,14 +839,14 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( // ab via bc to cd // ab via be to ef // - // has two artifical nodes (be/bc) with restrictions starting at `ab`. + // has two artificial nodes (be/bc) with restrictions starting at `ab`. // Since every restriction group (abc | abe) refers to the same // artificial node, we simply have to find a single representative for // the turn. Here we check whether the turn in question is the start of // a via way restriction. If that should be the case, we switch the id // of the edge-based-node for the target to the ID of the duplicated // node associated with the turn. (e.g. ab via bc switches bc to bc_dup) - auto const target_id = way_restriction_map.RemapIfRestricted( + auto const target_id = way_restriction_map.RemapIfRestrictionStart( nbe_to_ebn_mapping[outgoing_edge.edge], incoming_edge.node, outgoing_edge.node, @@ -877,7 +855,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( /***************************/ - const auto edgetarget = + const auto outgoing_edge_target = m_node_based_graph.GetTarget(outgoing_edge.edge); // TODO: this loop is not optimized - once we have a few @@ -888,7 +866,8 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( for (auto &turn : override.turn_sequence) { if (turn.from == incoming_edge.node && - turn.via == intersection_node && turn.to == edgetarget) + turn.via == intersection_node && + turn.to == outgoing_edge_target) { const auto &ebn_from = nbe_to_ebn_mapping[incoming_edge.edge]; @@ -900,7 +879,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( } { // scope to forget edge_with_data after - const auto edge_with_data_and_condition = + const auto edge_with_data = generate_edge(nbe_to_ebn_mapping[incoming_edge.edge], target_id, incoming_edge.node, @@ -912,20 +891,30 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( road_legs_on_the_left, edge_geometries); - buffer->continuous_data.push_back( - edge_with_data_and_condition.first); - if (edge_with_data_and_condition.second) + buffer->continuous_data.push_back(edge_with_data); + + // get conditional restrictions that apply to this turn + const auto &restrictions = + conditional_node_restriction_map.Restrictions( + incoming_edge.node, + outgoing_edge.node, + outgoing_edge_target); + for (const auto &restriction : restrictions) { buffer->conditionals.push_back( - *edge_with_data_and_condition.second); + {nbe_to_ebn_mapping[incoming_edge.edge], + target_id, + {static_cast(-1), + m_coordinates[intersection_node], + restriction->condition}}); } } - // when turning off a a via-way turn restriction, we need to not only + // When on the edge of a via-way turn restriction, we need to not only // handle the normal edges for the way, but also add turns for every // duplicated node. This process is integrated here to avoid doing the // turn analysis multiple times. - if (turning_off_via_way) + if (is_restriction_via_edge) { const auto duplicated_nodes = way_restriction_map.DuplicatedNodeIDs( incoming_edge.node, intersection_node); @@ -943,71 +932,91 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( auto const node_at_end_of_turn = m_node_based_graph.GetTarget(outgoing_edge.edge); - const auto is_way_restricted = way_restriction_map.IsRestricted( + const auto is_restricted = way_restriction_map.IsRestricted( duplicated_node_id, node_at_end_of_turn); - if (is_way_restricted) + if (is_restricted) { - auto const restriction = way_restriction_map.GetRestriction( - duplicated_node_id, node_at_end_of_turn); - - if (restriction.condition.empty()) + auto const &restrictions = + way_restriction_map.GetRestrictions( + duplicated_node_id, node_at_end_of_turn); + + auto const has_unconditional = + std::any_of(restrictions.begin(), + restrictions.end(), + [](const auto &restriction) { + return restriction->IsUnconditional(); + }); + if (has_unconditional) continue; + // From this via way, the outgoing edge will either: + // a) take a conditional turn transferring to a via + // path of an overlapping restriction. + // b) take a conditional turn to exit the restriction. + // If a) is applicable here, we change the target to be + // the duplicate restriction node. + auto const via_target_id = + way_restriction_map.RemapIfRestrictionVia( + nbe_to_ebn_mapping[outgoing_edge.edge], + from_id, + m_node_based_graph.GetTarget(outgoing_edge.edge), + m_number_of_edge_based_nodes); + // add into delayed data - auto edge_with_data_and_condition = - generate_edge(from_id, - nbe_to_ebn_mapping[outgoing_edge.edge], - incoming_edge.node, - incoming_edge.edge, - outgoing_edge.node, - outgoing_edge.edge, - turn->angle, - road_legs_on_the_right, - road_legs_on_the_left, - edge_geometries); - - buffer->delayed_data.push_back( - edge_with_data_and_condition.first); - if (edge_with_data_and_condition.second) - { - buffer->conditionals.push_back( - *edge_with_data_and_condition.second); - } + auto edge_with_data = generate_edge(from_id, + via_target_id, + incoming_edge.node, + incoming_edge.edge, + outgoing_edge.node, + outgoing_edge.edge, + turn->angle, + road_legs_on_the_right, + road_legs_on_the_left, + edge_geometries); + + buffer->delayed_data.push_back(edge_with_data); // also add the conditions for the way - if (is_way_restricted && !restriction.condition.empty()) + for (const auto &restriction : restrictions) { - // add a new conditional for the edge we just created + // add a new conditional for the edge we just + // created buffer->conditionals.push_back( {from_id, - nbe_to_ebn_mapping[outgoing_edge.edge], + via_target_id, {static_cast(-1), m_coordinates[intersection_node], - restriction.condition}}); + restriction->condition}}); } } else { - auto edge_with_data_and_condition = - generate_edge(from_id, - nbe_to_ebn_mapping[outgoing_edge.edge], - incoming_edge.node, - incoming_edge.edge, - outgoing_edge.node, - outgoing_edge.edge, - turn->angle, - road_legs_on_the_right, - road_legs_on_the_left, - edge_geometries); - - buffer->delayed_data.push_back( - edge_with_data_and_condition.first); - if (edge_with_data_and_condition.second) - { - buffer->conditionals.push_back( - *edge_with_data_and_condition.second); - } + // From this via way, the outgoing edge will either: + // a) continue along the current via path + // b) transfer to a via path of an overlapping restriction. + // c) exit the restriction + // If a) or b) are applicable here, we change the target to + // be the duplicate restriction node. + auto const via_target_id = + way_restriction_map.RemapIfRestrictionVia( + nbe_to_ebn_mapping[outgoing_edge.edge], + from_id, + m_node_based_graph.GetTarget(outgoing_edge.edge), + m_number_of_edge_based_nodes); + + auto edge_with_data = generate_edge(from_id, + via_target_id, + incoming_edge.node, + incoming_edge.edge, + outgoing_edge.node, + outgoing_edge.edge, + turn->angle, + road_legs_on_the_right, + road_legs_on_the_left, + edge_geometries); + + buffer->delayed_data.push_back(edge_with_data); } } } diff --git a/src/extractor/extraction_containers.cpp b/src/extractor/extraction_containers.cpp index b1893f6191c..ca967cc5c54 100644 --- a/src/extractor/extraction_containers.cpp +++ b/src/extractor/extraction_containers.cpp @@ -10,17 +10,12 @@ #include "util/exception.hpp" #include "util/exception_utils.hpp" -#include "util/fingerprint.hpp" +#include "util/for_each_indexed.hpp" #include "util/log.hpp" #include "util/timing_util.hpp" -#include "storage/io.hpp" - #include -#include -#include #include -#include #include @@ -99,6 +94,64 @@ inline NodeID mapExternalToInternalNodeID(Iter first, Iter last, const OSMNodeID return (it == last || value < *it) ? SPECIAL_NODEID : static_cast(std::distance(first, it)); } + +/** + * Here's what these properties represent on the node-based-graph + * way "ABCD" way "AB" + * ----------------------------------------------------------------- + * ⬇ A first_segment_source_id + * ⬇ | + * ⬇︎ B first_segment_target_id A first_segment_source_id + * ⬇︎ | ⬇ | last_segment_source_id + * ⬇︎ | ⬇ | + * ⬇︎ | B first_segment_target_id + * ⬇︎ C last_segment_source_id last_segment_target_id + * ⬇︎ | + * ⬇︎ D last_segment_target_id + * + * Finds the point where two ways connect at the end, and returns the 3 + * node-based nodes that describe the turn (the node just before, the + * node at the turn, and the next node after the turn) + **/ +std::tuple find_turn_nodes(const oe::NodesOfWay &from, + const oe::NodesOfWay &via, + const OSMNodeID &intersection_node) +{ + // connection node needed to choose orientation if from and via are the same way. E.g. u-turns + if (intersection_node == SPECIAL_OSM_NODEID || + intersection_node == from.first_segment_source_id()) + { + if (from.first_segment_source_id() == via.first_segment_source_id()) + { + return std::make_tuple(from.first_segment_target_id(), + via.first_segment_source_id(), + via.first_segment_target_id()); + } + if (from.first_segment_source_id() == via.last_segment_target_id()) + { + return std::make_tuple(from.first_segment_target_id(), + via.last_segment_target_id(), + via.last_segment_source_id()); + } + } + if (intersection_node == SPECIAL_OSM_NODEID || + intersection_node == from.last_segment_target_id()) + { + if (from.last_segment_target_id() == via.first_segment_source_id()) + { + return std::make_tuple(from.last_segment_source_id(), + via.first_segment_source_id(), + via.first_segment_target_id()); + } + if (from.last_segment_target_id() == via.last_segment_target_id()) + { + return std::make_tuple(from.last_segment_source_id(), + via.last_segment_target_id(), + via.last_segment_source_id()); + } + } + return std::make_tuple(SPECIAL_OSM_NODEID, SPECIAL_OSM_NODEID, SPECIAL_OSM_NODEID); +} } // namespace namespace osrm @@ -116,6 +169,8 @@ ExtractionContainers::ExtractionContainers() name_offsets.push_back(0); // Insert the total length sentinel (corresponds to the next name string offset) name_offsets.push_back(0); + // Sentinel for offset into used_nodes + way_node_id_offsets.push_back(0); } /** @@ -134,6 +189,9 @@ void ExtractionContainers::PrepareData(ScriptingEnvironment &scripting_environme { storage::tar::FileWriter writer(osrm_path, storage::tar::FileWriter::GenerateFingerprint); + const auto restriction_ways = IdentifyRestrictionWays(); + const auto maneuver_override_ways = IdentifyManeuverOverrideWays(); + PrepareNodes(); WriteNodes(writer); PrepareEdges(scripting_environment); @@ -142,20 +200,8 @@ void ExtractionContainers::PrepareData(ScriptingEnvironment &scripting_environme WriteEdges(writer); WriteMetadata(writer); - /* Sort these so that searching is a bit faster later on */ - { - util::UnbufferedLog log; - log << "Sorting used ways ... "; - TIMER_START(sort_ways); - tbb::parallel_sort(way_start_end_id_list.begin(), - way_start_end_id_list.end(), - FirstAndLastSegmentOfWayCompare()); - TIMER_STOP(sort_ways); - log << "ok, after " << TIMER_SEC(sort_ways) << "s"; - } - - PrepareManeuverOverrides(); - PrepareRestrictions(); + PrepareManeuverOverrides(maneuver_override_ways); + PrepareRestrictions(restriction_ways); WriteCharData(name_file_name); } @@ -665,47 +711,52 @@ void ExtractionContainers::WriteNodes(storage::tar::FileWriter &writer) const util::Log() << "Processed " << max_internal_node_id << " nodes"; } -void ExtractionContainers::PrepareManeuverOverrides() +ExtractionContainers::ReferencedWays ExtractionContainers::IdentifyManeuverOverrideWays() { - std::unordered_map referenced_ways; + ReferencedWays maneuver_override_ways; // prepare for extracting source/destination nodes for all maneuvers - { - util::UnbufferedLog log; - log << "Collecting start/end information on " << external_maneuver_overrides_list.size() - << " maneuver overrides..."; - TIMER_START(prepare_maneuver_overrides); - - const auto mark_ids = [&](auto const &external_maneuver_override) { - FirstAndLastSegmentOfWay dummy_segment{ - MAX_OSM_WAYID, MAX_OSM_NODEID, MAX_OSM_NODEID, MAX_OSM_NODEID, MAX_OSM_NODEID}; - std::for_each(external_maneuver_override.via_ways.begin(), - external_maneuver_override.via_ways.end(), - [&referenced_ways, dummy_segment](const auto &element) { - referenced_ways[element] = dummy_segment; - }); - }; + util::UnbufferedLog log; + log << "Collecting way information on " << external_maneuver_overrides_list.size() + << " maneuver overrides..."; + TIMER_START(identify_maneuver_override_ways); + + const auto mark_ids = [&](auto const &external_maneuver_override) { + NodesOfWay dummy_segment{MAX_OSM_WAYID, {MAX_OSM_NODEID, MAX_OSM_NODEID}}; + std::for_each(external_maneuver_override.via_ways.begin(), + external_maneuver_override.via_ways.end(), + [&maneuver_override_ways, dummy_segment](const auto &element) { + maneuver_override_ways[element] = dummy_segment; + }); + }; - // First, make an empty hashtable keyed by the ways referenced - // by the maneuver overrides - std::for_each(external_maneuver_overrides_list.begin(), - external_maneuver_overrides_list.end(), - mark_ids); + // First, make an empty hashtable keyed by the ways referenced + // by the maneuver overrides + std::for_each( + external_maneuver_overrides_list.begin(), external_maneuver_overrides_list.end(), mark_ids); - const auto set_ids = [&](auto const &start_end) { - auto itr = referenced_ways.find(start_end.way_id); - if (itr != referenced_ways.end()) - itr->second = start_end; - }; + const auto set_ids = [&](size_t way_list_idx, auto const &way_id) { + auto itr = maneuver_override_ways.find(way_id); + if (itr != maneuver_override_ways.end()) + { + auto node_start_itr = used_node_id_list.begin() + way_node_id_offsets[way_list_idx]; + auto node_end_itr = used_node_id_list.begin() + way_node_id_offsets[way_list_idx + 1]; + itr->second = NodesOfWay(way_id, std::vector(node_start_itr, node_end_itr)); + } + }; - // Then, populate the values in that hashtable for only the ways - // referenced - std::for_each(way_start_end_id_list.cbegin(), way_start_end_id_list.cend(), set_ids); + // Then, populate the values in that hashtable for only the ways + // referenced + util::for_each_indexed(ways_list.cbegin(), ways_list.cend(), set_ids); - TIMER_STOP(prepare_maneuver_overrides); - log << "ok, after " << TIMER_SEC(prepare_maneuver_overrides) << "s"; - } + TIMER_STOP(identify_maneuver_override_ways); + log << "ok, after " << TIMER_SEC(identify_maneuver_override_ways) << "s"; + + return maneuver_override_ways; +} +void ExtractionContainers::PrepareManeuverOverrides(const ReferencedWays &maneuver_override_ways) +{ auto const osm_node_to_internal_nbn = [&](auto const osm_node) { auto internal = mapExternalToInternalNodeID( used_node_id_list.begin(), used_node_id_list.end(), osm_node); @@ -716,82 +767,34 @@ void ExtractionContainers::PrepareManeuverOverrides() return internal; }; - // Given - // a -- b - ????????? - c -- d as via segment - // and either - // d -- e - ????????? - f -- g or - // h -- i - ????????? - j -- a - // return - // (d,e) or (j,a) as entry-segment - - /** - * Here's what these properties represent on the node-based-graph - * way "ABCD" way "AB" - * ----------------------------------------------------------------- - * ⬇ A first_segment_source_id - * ⬇ | - * ⬇︎ B first_segment_target_id A first_segment_source_id - * ⬇︎ | ⬇ | last_segment_source_id - * ⬇︎ | ⬇ | - * ⬇︎ | B first_segment_target_id - * ⬇︎ C last_segment_source_id last_segment_target_id - * ⬇︎ | - * ⬇︎ D last_segment_target_id - * - * Finds the point where two ways connect at the end, and returns the 3 - * node-based nodes that describe the turn (the node just before, the - * node at the turn, and the next node after the turn) - **/ - auto const find_turn_from_way_tofrom_nodes = [&](auto const &from_segment, - auto const &to_segment) { - if (from_segment.first_segment_source_id == to_segment.first_segment_source_id) - { - return NodeBasedTurn{osm_node_to_internal_nbn(from_segment.first_segment_target_id), - osm_node_to_internal_nbn(from_segment.first_segment_source_id), - osm_node_to_internal_nbn(to_segment.first_segment_target_id)}; - } - else if (from_segment.first_segment_source_id == to_segment.last_segment_target_id) - { - return NodeBasedTurn{osm_node_to_internal_nbn(from_segment.first_segment_target_id), - osm_node_to_internal_nbn(from_segment.first_segment_source_id), - osm_node_to_internal_nbn(to_segment.last_segment_source_id)}; - } - else if (from_segment.last_segment_target_id == to_segment.first_segment_source_id) - { - return NodeBasedTurn{osm_node_to_internal_nbn(from_segment.last_segment_source_id), - osm_node_to_internal_nbn(from_segment.last_segment_target_id), - osm_node_to_internal_nbn(to_segment.first_segment_target_id)}; - } - else if (from_segment.last_segment_target_id == to_segment.last_segment_target_id) - { - return NodeBasedTurn{osm_node_to_internal_nbn(from_segment.last_segment_source_id), - osm_node_to_internal_nbn(from_segment.last_segment_target_id), - osm_node_to_internal_nbn(to_segment.last_segment_source_id)}; - } - util::Log(logDEBUG) << "Maneuver override ways " << from_segment.way_id << " and " - << to_segment.way_id << " are not connected"; - return NodeBasedTurn{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID}; - }; - auto const get_turn_from_way_pair = [&](const OSMWayID &from_id, const OSMWayID &to_id) { - auto const from_segment_itr = referenced_ways.find(from_id); + auto const from_segment_itr = maneuver_override_ways.find(from_id); if (from_segment_itr->second.way_id != from_id) { util::Log(logDEBUG) << "Override references invalid way: " << from_id; return NodeBasedTurn{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID}; } - auto const to_segment_itr = referenced_ways.find(to_id); + auto const to_segment_itr = maneuver_override_ways.find(to_id); if (to_segment_itr->second.way_id != to_id) { util::Log(logDEBUG) << "Override references invalid way: " << to_id; return NodeBasedTurn{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID}; } - auto result = - find_turn_from_way_tofrom_nodes(from_segment_itr->second, to_segment_itr->second); - - return result; + OSMNodeID from, via, to; + std::tie(from, via, to) = + find_turn_nodes(from_segment_itr->second, to_segment_itr->second, SPECIAL_OSM_NODEID); + if (via == SPECIAL_OSM_NODEID) + { + // unconnected + util::Log(logDEBUG) << "Maneuver override ways " << from_segment_itr->second.way_id + << " and " << to_segment_itr->second.way_id << " are not connected"; + return NodeBasedTurn{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID}; + } + return NodeBasedTurn{osm_node_to_internal_nbn(from), + osm_node_to_internal_nbn(via), + osm_node_to_internal_nbn(to)}; }; const auto strings_to_turn_type_and_direction = [](const std::string &turn_string, @@ -909,7 +912,7 @@ void ExtractionContainers::PrepareManeuverOverrides() // Transforming the overrides into the dedicated internal types { util::UnbufferedLog log; - log << "Collecting start/end information on " << external_maneuver_overrides_list.size() + log << "Collecting node information on " << external_maneuver_overrides_list.size() << " maneuver overrides..."; TIMER_START(transform); std::for_each(external_maneuver_overrides_list.begin(), @@ -920,54 +923,64 @@ void ExtractionContainers::PrepareManeuverOverrides() } } -void ExtractionContainers::PrepareRestrictions() +ExtractionContainers::ReferencedWays ExtractionContainers::IdentifyRestrictionWays() { + // Contains the nodes of each way that is part of an restriction + ReferencedWays restriction_ways; - // contain the start/end nodes of each way that is part of an restriction - std::unordered_map referenced_ways; - - // prepare for extracting source/destination nodes for all restrictions - { - util::UnbufferedLog log; - log << "Collecting start/end information on " << restrictions_list.size() - << " restrictions..."; - TIMER_START(prepare_restrictions); - - const auto mark_ids = [&](auto const &turn_restriction) { - FirstAndLastSegmentOfWay dummy_segment{ - MAX_OSM_WAYID, MAX_OSM_NODEID, MAX_OSM_NODEID, MAX_OSM_NODEID, MAX_OSM_NODEID}; - if (turn_restriction.Type() == RestrictionType::WAY_RESTRICTION) - { - const auto &way = turn_restriction.AsWayRestriction(); - referenced_ways[way.from] = dummy_segment; - referenced_ways[way.to] = dummy_segment; - referenced_ways[way.via] = dummy_segment; - } - else + // Prepare for extracting nodes for all restrictions + util::UnbufferedLog log; + log << "Collecting way information on " << restrictions_list.size() << " restrictions..."; + TIMER_START(identify_restriction_ways); + + // Enter invalid IDs into the map to indicate that we want to find out about + // nodes of these ways. + const auto mark_ids = [&](auto const &turn_restriction) { + NodesOfWay dummy_segment{MAX_OSM_WAYID, {MAX_OSM_NODEID, MAX_OSM_NODEID}}; + if (turn_restriction.Type() == RestrictionType::WAY_RESTRICTION) + { + const auto &way = turn_restriction.AsWayRestriction(); + restriction_ways[way.from] = dummy_segment; + restriction_ways[way.to] = dummy_segment; + for (const auto &v : way.via) { - BOOST_ASSERT(turn_restriction.Type() == RestrictionType::NODE_RESTRICTION); - const auto &node = turn_restriction.AsNodeRestriction(); - referenced_ways[node.from] = dummy_segment; - referenced_ways[node.to] = dummy_segment; + restriction_ways[v] = dummy_segment; } - }; + } + else + { + BOOST_ASSERT(turn_restriction.Type() == RestrictionType::NODE_RESTRICTION); + const auto &node = turn_restriction.AsNodeRestriction(); + restriction_ways[node.from] = dummy_segment; + restriction_ways[node.to] = dummy_segment; + } + }; - std::for_each(restrictions_list.begin(), restrictions_list.end(), mark_ids); + std::for_each(restrictions_list.begin(), restrictions_list.end(), mark_ids); - // enter invalid IDs into the above maps to indicate that we want to find out about - // start/end - // nodes of these ways - // update the values for all edges already sporting SPECIAL_NODEID - const auto set_ids = [&](auto const &start_end) { - auto itr = referenced_ways.find(start_end.way_id); - if (itr != referenced_ways.end()) - itr->second = start_end; - }; + // Update the values for all ways already sporting SPECIAL_NODEID + const auto set_ids = [&](const size_t way_list_idx, auto const &way_id) { + auto itr = restriction_ways.find(way_id); + if (itr != restriction_ways.end()) + { + 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]; + itr->second = + NodesOfWay(way_id, std::vector(node_start_offset, node_end_offset)); + } + }; - std::for_each(way_start_end_id_list.cbegin(), way_start_end_id_list.cend(), set_ids); - TIMER_STOP(prepare_restrictions); - log << "ok, after " << TIMER_SEC(prepare_restrictions) << "s"; - } + util::for_each_indexed(ways_list.cbegin(), ways_list.cend(), set_ids); + TIMER_STOP(identify_restriction_ways); + log << "ok, after " << TIMER_SEC(identify_restriction_ways) << "s"; + + return restriction_ways; +} + +void ExtractionContainers::PrepareRestrictions(const ReferencedWays &restriction_ways) +{ auto const to_internal = [&](auto const osm_node) { auto internal = mapExternalToInternalNodeID( @@ -979,167 +992,242 @@ void ExtractionContainers::PrepareRestrictions() return internal; }; - // Given - // a -- b - ????????? - c -- d as via segment - // and either - // d -- e - ????????? - f -- g or - // h -- i - ????????? - j -- a - // (d,e) or (j,a) as entry-segment - auto const find_node_restriction = - [&](auto const &segment, auto const &via_segment, auto const via_node) { - // In case of way-restrictions, via-node will be set to MAX_OSM_NODEID to signal - // that - // the node is not present. - // connected at the front of the segment - // Turn restrictions are described as a restriction between the two segments closest - // to - // the shared via-node on the from and to ways. Graph compression will later - // renumber - // the from and to internal node IDs as nodes are plucked out of the node-based - // graph. - if (via_node == MAX_OSM_NODEID || segment.first_segment_source_id == via_node) + // Way restrictions are comprised of: + // 1. The segment in the from way that intersects with the via path + // 2. All segments that make up the via path + // 3. The segment in the to way that intersects with the via path. + // + // from: [a, b, c, d, e] + // via: [[f, g, h, i, j], [k, l], [m, n, o]] + // to: [p, q, r, s] + // + // First establish the orientation of the from/via intersection by finding which end + // nodes both ways share. From this we can select the from segment. + // + // intersect | from segment | next_connection + // a=f | b,a | f + // a=j | b,a | j + // e=f | e,d | f + // e=j | e,d | j + // + // Use the next connection to inform the orientation of the first via + // way and the intersection between first and second via ways. + // + // next_connection | intersect | via result | next_next_connection + // f | j=k | [f,g,h,i,j] | k + // f | j=l | [f,g,h,i,j] | l + // j | f=k | [j,i,h,g,f] | k + // j | f=l | [j,i,h,g,f] | l + // + // This is continued for the remaining via ways, appending to the via result + // + // The final via/to intersection also uses the next_connection information in a similar fashion. + // + // next_connection | intersect | to_segment + // m | o=p | p,q + // m | o=s | s,r + // o | m=p | p,q + // o | m=s | s,r + // + // The final result is a list of nodes that represent a valid from->via->to path through the + // ways. + // + // E.g. if intersection nodes are a=j, f=l, k=o, m=s + // the result will be {e [d,c,b,a,i,h,g,f,k,n,m] r} + auto const find_way_restriction = [&](const NodesOfWay &from_way, + const std::vector &via_ways, + const NodesOfWay &to_way) { + BOOST_ASSERT(!via_ways.empty()); + + WayRestriction restriction; + + // Find the orientation of the connected ways starting with the from-via intersection. + OSMNodeID from, via; + std::tie(from, via, std::ignore) = + find_turn_nodes(from_way, via_ways.front(), SPECIAL_OSM_NODEID); + if (via == SPECIAL_OSM_NODEID) + { + util::Log(logDEBUG) << "Restriction has unconnected from and via ways: " + << from_way.way_id << ", " << via_ways.front().way_id; + return WayRestriction{SPECIAL_NODEID, {}, SPECIAL_NODEID}; + } + restriction.from = to_internal(from); + restriction.via.push_back(to_internal(via)); + + // Use the connection node from the previous intersection to inform our conversion of + // via ways into internal nodes. + OSMNodeID next_connection = via; + for (const auto &via_way : via_ways) + { + if (next_connection == via_way.first_segment_source_id()) { - if (segment.first_segment_source_id == via_segment.first_segment_source_id) - { - return NodeRestriction{to_internal(segment.first_segment_target_id), - to_internal(segment.first_segment_source_id), - to_internal(via_segment.first_segment_target_id)}; - } - else if (segment.first_segment_source_id == via_segment.last_segment_target_id) - { - return NodeRestriction{to_internal(segment.first_segment_target_id), - to_internal(segment.first_segment_source_id), - to_internal(via_segment.last_segment_source_id)}; - } + std::transform(std::next(via_way.node_ids.begin()), + via_way.node_ids.end(), + std::back_inserter(restriction.via), + to_internal); + next_connection = via_way.last_segment_target_id(); } + else if (next_connection == via_way.last_segment_target_id()) + { + std::transform(std::next(via_way.node_ids.rbegin()), + via_way.node_ids.rend(), + std::back_inserter(restriction.via), + to_internal); + next_connection = via_way.first_segment_source_id(); + } + else + { + util::Log(logDEBUG) << "Restriction has unconnected via way: " << via_way.way_id + << " to node " << next_connection; + return WayRestriction{SPECIAL_NODEID, {}, SPECIAL_NODEID}; + } + } - // connected at the end of the segment - if (via_node == MAX_OSM_NODEID || segment.last_segment_target_id == via_node) + // Add the final to node after the via-to intersection. + if (next_connection == to_way.first_segment_source_id()) + { + restriction.to = to_internal(to_way.first_segment_target_id()); + } + else if (next_connection == to_way.last_segment_target_id()) + { + restriction.to = to_internal(to_way.last_segment_source_id()); + } + else + { + util::Log(logDEBUG) << "Restriction has unconnected via and to ways: " + << via_ways.back().way_id << ", " << to_way.way_id; + return WayRestriction{SPECIAL_NODEID, {}, SPECIAL_NODEID}; + } + return restriction; + }; + + // Check if we were able to resolve all the involved OSM elements before translating to an + // internal restriction + auto const get_way_restriction_from_OSM_ids = + [&](auto const from_id, auto const to_id, const std::vector &via_ids) { + auto const from_way_itr = restriction_ways.find(from_id); + if (from_way_itr->second.way_id != from_id) { - if (segment.last_segment_target_id == via_segment.first_segment_source_id) - { - return NodeRestriction{to_internal(segment.last_segment_source_id), - to_internal(segment.last_segment_target_id), - to_internal(via_segment.first_segment_target_id)}; - } - else if (segment.last_segment_target_id == via_segment.last_segment_target_id) + util::Log(logDEBUG) << "Restriction references invalid from way: " << from_id; + return WayRestriction{SPECIAL_NODEID, {}, SPECIAL_NODEID}; + } + + std::vector via_ways; + for (const auto &via_id : via_ids) + { + auto const via_segment_itr = restriction_ways.find(via_id); + if (via_segment_itr->second.way_id != via_id) { - return NodeRestriction{to_internal(segment.last_segment_source_id), - to_internal(segment.last_segment_target_id), - to_internal(via_segment.last_segment_source_id)}; + util::Log(logDEBUG) << "Restriction references invalid via way: " << via_id; + return WayRestriction{SPECIAL_NODEID, {}, SPECIAL_NODEID}; } + via_ways.push_back(via_segment_itr->second); } - // unconnected - util::Log(logDEBUG) << "Restriction references unconnected way: " << segment.way_id; - return NodeRestriction{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID}; + auto const to_way_itr = restriction_ways.find(to_id); + if (to_way_itr->second.way_id != to_id) + { + util::Log(logDEBUG) << "Restriction references invalid to way: " << to_id; + return WayRestriction{SPECIAL_NODEID, {}, SPECIAL_NODEID}; + } + + return find_way_restriction(from_way_itr->second, via_ways, to_way_itr->second); + }; + + // Node restrictions are described as a restriction between the two segments closest + // to the shared via-node on the from and to ways. + // from: [a, b, c, d, e] + // to: [f, g, h, i, j] + // + // The via node establishes the orientation of the from/to intersection when choosing the + // segments. + // via | node restriction + // a=f | b,a,g + // a=j | b,a,i + // e=f | d,e,g + // e=j | d,e,i + auto const find_node_restriction = + [&](auto const &from_segment, auto const &to_segment, auto const via_node) { + OSMNodeID from, via, to; + std::tie(from, via, to) = find_turn_nodes(from_segment, to_segment, via_node); + if (via == SPECIAL_OSM_NODEID) + { + // unconnected + util::Log(logDEBUG) + << "Restriction references unconnected way: " << from_segment.way_id; + return NodeRestriction{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID}; + } + return NodeRestriction{to_internal(from), to_internal(via), to_internal(to)}; }; - // translate the turn from one segment onto another into a node restriction (the ways can - // only - // be connected at a single location) + // Check if we were able to resolve all the involved OSM elements before translating to an + // internal restriction auto const get_node_restriction_from_OSM_ids = [&](auto const from_id, auto const to_id, const OSMNodeID via_node) { - auto const from_segment_itr = referenced_ways.find(from_id); + auto const from_segment_itr = restriction_ways.find(from_id); + if (from_segment_itr->second.way_id != from_id) { util::Log(logDEBUG) << "Restriction references invalid way: " << from_id; return NodeRestriction{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID}; } - auto const to_segment_itr = referenced_ways.find(to_id); + auto const to_segment_itr = restriction_ways.find(to_id); if (to_segment_itr->second.way_id != to_id) { util::Log(logDEBUG) << "Restriction references invalid way: " << to_id; return NodeRestriction{SPECIAL_NODEID, SPECIAL_NODEID, SPECIAL_NODEID}; } + return find_node_restriction(from_segment_itr->second, to_segment_itr->second, via_node); }; // Transform an OSMRestriction (based on WayIDs) into an OSRM restriction (base on NodeIDs). // Returns true on successful transformation, false in case of invalid references. - // Based on the auto type deduction, this transfor handles both conditional and - // unconditional - // turn restrictions. const auto transform = [&](const auto &external_type, auto &internal_type) { if (external_type.Type() == RestrictionType::WAY_RESTRICTION) { auto const &external = external_type.AsWayRestriction(); - // check if we were able to resolve all the involved ways - auto const from_restriction = - get_node_restriction_from_OSM_ids(external.from, external.via, MAX_OSM_NODEID); - auto const to_restriction = - get_node_restriction_from_OSM_ids(external.via, external.to, MAX_OSM_NODEID); - - // failed to translate either of the involved nodes? - if (!from_restriction.Valid() || !to_restriction.Valid()) + auto const restriction = + get_way_restriction_from_OSM_ids(external.from, external.to, external.via); + + if (!restriction.Valid()) return false; - // point located at both via and segment is alway on `second`, to FSSF is the order - // we - // need - WayRestriction way_restriction{from_restriction, to_restriction}; - internal_type.node_or_way = std::move(way_restriction); + internal_type.node_or_way = restriction; return true; } else { BOOST_ASSERT(external_type.Type() == RestrictionType::NODE_RESTRICTION); auto const &external = external_type.AsNodeRestriction(); - auto const via_node = to_internal(external.via); - // check if we were able to resolve all the involved ways auto restriction = get_node_restriction_from_OSM_ids(external.from, external.to, external.via); if (!restriction.Valid()) - { return false; - } - - if (restriction.via != via_node) - { - util::Log(logDEBUG) << "Restriction references invalid way: " << external.via; - return false; - } - internal_type.node_or_way = std::move(restriction); + internal_type.node_or_way = restriction; return true; } }; - // wrapper function to handle distinction between conditional and unconditional turn - // restrictions - const auto transform_into_internal_types = - [&](const InputConditionalTurnRestriction &external_restriction) { - // unconditional restriction - if (external_restriction.condition.empty() && - external_restriction.Type() == RestrictionType::NODE_RESTRICTION) - { - TurnRestriction restriction; - restriction.is_only = external_restriction.is_only; - if (transform(external_restriction, restriction)) - unconditional_turn_restrictions.push_back(std::move(restriction)); - } - // conditional turn restriction - else - { - ConditionalTurnRestriction restriction; - restriction.is_only = external_restriction.is_only; - restriction.condition = std::move(external_restriction.condition); - if (transform(external_restriction, restriction)) - { - conditional_turn_restrictions.push_back(std::move(restriction)); - } - } - }; + const auto transform_into_internal_types = [&](InputTurnRestriction &external_restriction) { + TurnRestriction restriction; + if (transform(external_restriction, restriction)) + { + restriction.is_only = external_restriction.is_only; + restriction.condition = std::move(external_restriction.condition); + turn_restrictions.push_back(std::move(restriction)); + } + }; // Transforming the restrictions into the dedicated internal types { util::UnbufferedLog log; - log << "Collecting start/end information on " << restrictions_list.size() - << " restrictions..."; + log << "Collecting node information on " << restrictions_list.size() << " restrictions..."; TIMER_START(transform); std::for_each( restrictions_list.begin(), restrictions_list.end(), transform_into_internal_types); diff --git a/src/extractor/extractor.cpp b/src/extractor/extractor.cpp index 064fbcccb93..5c5d96a7100 100644 --- a/src/extractor/extractor.cpp +++ b/src/extractor/extractor.cpp @@ -12,8 +12,9 @@ #include "extractor/maneuver_override_relation_parser.hpp" #include "extractor/name_table.hpp" #include "extractor/node_based_graph_factory.hpp" +#include "extractor/node_restriction_map.hpp" #include "extractor/restriction_filter.hpp" -#include "extractor/restriction_index.hpp" +#include "extractor/restriction_graph.hpp" #include "extractor/restriction_parser.hpp" #include "extractor/scripting_environment.hpp" #include "extractor/tarjan_scc.hpp" @@ -215,12 +216,8 @@ int Extractor::run(ScriptingEnvironment &scripting_environment) LaneDescriptionMap turn_lane_map; std::vector turn_restrictions; - std::vector conditional_turn_restrictions; std::vector unresolved_maneuver_overrides; - std::tie(turn_lane_map, - turn_restrictions, - conditional_turn_restrictions, - unresolved_maneuver_overrides) = + std::tie(turn_lane_map, turn_restrictions, unresolved_maneuver_overrides) = ParseOSMData(scripting_environment, number_of_threads); // Transform the node-based graph that OSM is based on into an edge-based graph @@ -242,7 +239,6 @@ int Extractor::run(ScriptingEnvironment &scripting_environment) NodeBasedGraphFactory node_based_graph_factory(config.GetPath(".osrm"), scripting_environment, turn_restrictions, - conditional_turn_restrictions, unresolved_maneuver_overrides); NameTable name_table; @@ -283,8 +279,8 @@ int Extractor::run(ScriptingEnvironment &scripting_environment) edge_based_nodes_container = EdgeBasedNodeDataContainer({}, std::move(node_based_graph_factory.GetAnnotationData())); - conditional_turn_restrictions = - removeInvalidRestrictions(std::move(conditional_turn_restrictions), node_based_graph); + turn_restrictions = removeInvalidRestrictions(std::move(turn_restrictions), node_based_graph); + auto restriction_graph = constructRestrictionGraph(turn_restrictions); const auto number_of_node_based_nodes = node_based_graph.GetNumberOfNodes(); @@ -294,8 +290,7 @@ int Extractor::run(ScriptingEnvironment &scripting_environment) node_based_graph_factory.GetCompressedEdges(), barrier_nodes, traffic_signals, - turn_restrictions, - conditional_turn_restrictions, + restriction_graph, segregated_edges, name_table, unresolved_maneuver_overrides, @@ -314,8 +309,7 @@ int Extractor::run(ScriptingEnvironment &scripting_environment) coordinates, node_based_graph_factory.GetCompressedEdges(), barrier_nodes, - turn_restrictions, - conditional_turn_restrictions, + restriction_graph, name_table, std::move(turn_lane_map), scripting_environment); @@ -374,12 +368,10 @@ int Extractor::run(ScriptingEnvironment &scripting_environment) return 0; } -std::tuple, - std::vector, - std::vector> -Extractor::ParseOSMData(ScriptingEnvironment &scripting_environment, - const unsigned number_of_threads) +std:: + tuple, std::vector> + Extractor::ParseOSMData(ScriptingEnvironment &scripting_environment, + const unsigned number_of_threads) { TIMER_START(extracting); @@ -455,7 +447,7 @@ Extractor::ParseOSMData(ScriptingEnvironment &scripting_environment, std::vector> resulting_nodes; std::vector> resulting_ways; std::vector> resulting_relations; - std::vector resulting_restrictions; + std::vector resulting_restrictions; std::vector resulting_maneuver_overrides; }; @@ -638,8 +630,7 @@ Extractor::ParseOSMData(ScriptingEnvironment &scripting_environment, util::Log() << "extraction finished after " << TIMER_SEC(extracting) << "s"; return std::make_tuple(std::move(turn_lane_map), - std::move(extraction_containers.unconditional_turn_restrictions), - std::move(extraction_containers.conditional_turn_restrictions), + std::move(extraction_containers.turn_restrictions), std::move(extraction_containers.internal_maneuver_overrides)); } @@ -712,8 +703,7 @@ EdgeID Extractor::BuildEdgeExpandedGraph( const CompressedEdgeContainer &compressed_edge_container, const std::unordered_set &barrier_nodes, const std::unordered_set &traffic_signals, - const std::vector &turn_restrictions, - const std::vector &conditional_turn_restrictions, + const RestrictionGraph &restriction_graph, const std::unordered_set &segregated_edges, const NameTable &name_table, const std::vector &maneuver_overrides, @@ -740,21 +730,10 @@ EdgeID Extractor::BuildEdgeExpandedGraph( turn_lane_map); const auto create_edge_based_edges = [&]() { - // scoped to relase intermediate datastructures right after the call - std::vector node_restrictions; - for (auto const &t : turn_restrictions) - if (t.Type() == RestrictionType::NODE_RESTRICTION) - node_restrictions.push_back(t); - - std::vector conditional_node_restrictions; - for (auto const &t : conditional_turn_restrictions) - if (t.Type() == RestrictionType::NODE_RESTRICTION) - conditional_node_restrictions.push_back(t); - - RestrictionMap via_node_restriction_map(node_restrictions, IndexNodeByFromAndVia()); - WayRestrictionMap via_way_restriction_map(conditional_turn_restrictions); - ConditionalRestrictionMap conditional_node_restriction_map(conditional_node_restrictions, - IndexNodeByFromAndVia()); + // scoped to release intermediate data structures right after the call + RestrictionMap unconditional_node_restriction_map(restriction_graph); + ConditionalRestrictionMap conditional_node_restriction_map(restriction_graph); + WayRestrictionMap via_way_restriction_map(restriction_graph); edge_based_graph_factory.Run(scripting_environment, config.GetPath(".osrm.turn_weight_penalties").string(), config.GetPath(".osrm.turn_duration_penalties").string(), @@ -762,7 +741,7 @@ EdgeID Extractor::BuildEdgeExpandedGraph( config.GetPath(".osrm.cnbg_to_ebg").string(), config.GetPath(".osrm.restrictions").string(), config.GetPath(".osrm.maneuver_overrides").string(), - via_node_restriction_map, + unconditional_node_restriction_map, conditional_node_restriction_map, via_way_restriction_map, maneuver_overrides); @@ -833,8 +812,7 @@ void Extractor::ProcessGuidanceTurns( const std::vector &node_coordinates, const CompressedEdgeContainer &compressed_edge_container, const std::unordered_set &barrier_nodes, - const std::vector &turn_restrictions, - const std::vector &conditional_turn_restrictions, + const RestrictionGraph &restriction_graph, const NameTable &name_table, LaneDescriptionMap lane_description_map, ScriptingEnvironment &scripting_environment) @@ -853,20 +831,15 @@ void Extractor::ProcessGuidanceTurns( SuffixTable street_name_suffix_table(scripting_environment); const auto &turn_lanes_data = transformTurnLaneMapIntoArrays(lane_description_map); - std::vector node_restrictions; - for (auto const &t : turn_restrictions) - if (t.Type() == RestrictionType::NODE_RESTRICTION) - node_restrictions.push_back(t); - - RestrictionMap node_restriction_map(node_restrictions, IndexNodeByFromAndVia()); - WayRestrictionMap way_restriction_map(conditional_turn_restrictions); + RestrictionMap unconditional_node_restriction_map(restriction_graph); + WayRestrictionMap way_restriction_map(restriction_graph); osrm::guidance::annotateTurns(node_based_graph, edge_based_node_container, node_coordinates, compressed_edge_container, barrier_nodes, - node_restriction_map, + unconditional_node_restriction_map, way_restriction_map, name_table, street_name_suffix_table, diff --git a/src/extractor/extractor_callbacks.cpp b/src/extractor/extractor_callbacks.cpp index caaddbcb78d..de7b282193c 100644 --- a/src/extractor/extractor_callbacks.cpp +++ b/src/extractor/extractor_callbacks.cpp @@ -11,7 +11,6 @@ #include "util/guidance/turn_lanes.hpp" #include "util/log.hpp" -#include #include #include @@ -19,8 +18,6 @@ #include "osrm/coordinate.hpp" -#include -#include #include #include #include @@ -69,10 +66,9 @@ void ExtractorCallbacks::ProcessNode(const osmium::Node &input_node, } } -void ExtractorCallbacks::ProcessRestriction(const InputConditionalTurnRestriction &restriction) +void ExtractorCallbacks::ProcessRestriction(const InputTurnRestriction &restriction) { external_memory.restrictions_list.push_back(restriction); - // util::Log() << restriction.toString(); } void ExtractorCallbacks::ProcessManeuverOverride(const InputManeuverOverride &override) @@ -477,12 +473,9 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti return OSMNodeID{static_cast(ref.ref())}; }); - external_memory.way_start_end_id_list.push_back( - {OSMWayID{static_cast(input_way.id())}, - OSMNodeID{static_cast(nodes[0].ref())}, - OSMNodeID{static_cast(nodes[1].ref())}, - OSMNodeID{static_cast(nodes[nodes.size() - 2].ref())}, - OSMNodeID{static_cast(nodes.back().ref())}}); + auto way_id = OSMWayID{static_cast(input_way.id())}; + external_memory.ways_list.push_back(way_id); + external_memory.way_node_id_offsets.push_back(external_memory.used_node_id_list.size()); } } // namespace extractor diff --git a/src/extractor/graph_compressor.cpp b/src/extractor/graph_compressor.cpp index 3735713435c..30ef5d593e9 100644 --- a/src/extractor/graph_compressor.cpp +++ b/src/extractor/graph_compressor.cpp @@ -20,46 +20,40 @@ namespace osrm namespace extractor { -void GraphCompressor::Compress( - const std::unordered_set &barrier_nodes, - const std::unordered_set &traffic_signals, - ScriptingEnvironment &scripting_environment, - std::vector &turn_restrictions, - std::vector &conditional_turn_restrictions, - std::vector &maneuver_overrides, - util::NodeBasedDynamicGraph &graph, - const std::vector &node_data_container, - CompressedEdgeContainer &geometry_compressor) +void GraphCompressor::Compress(const std::unordered_set &barrier_nodes, + const std::unordered_set &traffic_signals, + ScriptingEnvironment &scripting_environment, + std::vector &turn_restrictions, + std::vector &maneuver_overrides, + util::NodeBasedDynamicGraph &graph, + const std::vector &node_data_container, + CompressedEdgeContainer &geometry_compressor) { const unsigned original_number_of_nodes = graph.GetNumberOfNodes(); const unsigned original_number_of_edges = graph.GetNumberOfEdges(); - RestrictionCompressor restriction_compressor( - turn_restrictions, conditional_turn_restrictions, maneuver_overrides); + RestrictionCompressor restriction_compressor(turn_restrictions, maneuver_overrides); - // we do not compress turn restrictions on degree two nodes. These nodes are usually used to - // indicated `directed` barriers + // Some degree two nodes are not compressed if they act as entry/exit points into a + // restriction path. std::unordered_set restriction_via_nodes; const auto remember_via_nodes = [&](const auto &restriction) { if (restriction.Type() == RestrictionType::NODE_RESTRICTION) { - const auto &node = restriction.AsNodeRestriction(); - restriction_via_nodes.insert(node.via); + restriction_via_nodes.insert(restriction.AsNodeRestriction().via); } else { BOOST_ASSERT(restriction.Type() == RestrictionType::WAY_RESTRICTION); - const auto &way = restriction.AsWayRestriction(); - restriction_via_nodes.insert(way.in_restriction.via); - restriction_via_nodes.insert(way.out_restriction.via); + const auto &way_restriction = restriction.AsWayRestriction(); + // We do not compress the first and last via nodes so that we know how to enter/exit + // a restriction path and apply the restrictions correctly. + restriction_via_nodes.insert(way_restriction.via.front()); + restriction_via_nodes.insert(way_restriction.via.back()); } }; std::for_each(turn_restrictions.begin(), turn_restrictions.end(), remember_via_nodes); - std::for_each(conditional_turn_restrictions.begin(), - conditional_turn_restrictions.end(), - remember_via_nodes); - { const auto weight_multiplier = scripting_environment.GetProfileProperties().GetWeightMultiplier(); @@ -82,8 +76,8 @@ void GraphCompressor::Compress( continue; } - // check if v is a via node for a turn restriction, i.e. a 'directed' barrier node - if (restriction_via_nodes.count(node_v)) + // check if v is an entry/exit via node for a turn restriction + if (restriction_via_nodes.count(node_v) > 0) { continue; } @@ -181,7 +175,7 @@ void GraphCompressor::Compress( * 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 + * turn-lanes. Without this, we would have to treat any turn-lane beginning/ending * just * like a barrier. */ diff --git a/src/extractor/intersection/intersection_analysis.cpp b/src/extractor/intersection/intersection_analysis.cpp index dcf3b558d8e..e96a814fb14 100644 --- a/src/extractor/intersection/intersection_analysis.cpp +++ b/src/extractor/intersection/intersection_analysis.cpp @@ -428,24 +428,10 @@ double findEdgeLength(const IntersectionEdgeGeometries &geometries, const EdgeID template bool isTurnRestricted(const RestrictionsRange &restrictions, const NodeID to) { - // Check turn restrictions to find a node that is the only allowed target when coming from a - // node to an intersection - // d - // | - // a - b - c and `only_straight_on ab | bc would return `c` for `a,b` - const auto is_only = std::find_if(restrictions.first, - restrictions.second, - [](const auto &pair) { return pair.second->is_only; }); - if (is_only != restrictions.second) - return is_only->second->AsNodeRestriction().to != to; - - // Check if explicitly forbidden - const auto no_turn = - std::find_if(restrictions.first, restrictions.second, [&to](const auto &restriction) { - return restriction.second->AsNodeRestriction().to == to; - }); - - return no_turn != restrictions.second; + // Check if any of the restrictions would prevent a turn to 'to' + return std::any_of(restrictions.begin(), restrictions.end(), [&to](const auto &restriction) { + return restriction->IsTurnRestricted(to); + }); } bool isTurnAllowed(const util::NodeBasedDynamicGraph &graph, diff --git a/src/extractor/node_based_graph_factory.cpp b/src/extractor/node_based_graph_factory.cpp index e40436820c1..51473797d2f 100644 --- a/src/extractor/node_based_graph_factory.cpp +++ b/src/extractor/node_based_graph_factory.cpp @@ -19,14 +19,10 @@ NodeBasedGraphFactory::NodeBasedGraphFactory( const boost::filesystem::path &input_file, ScriptingEnvironment &scripting_environment, std::vector &turn_restrictions, - std::vector &conditional_turn_restrictions, std::vector &maneuver_overrides) { LoadDataFromFile(input_file); - Compress(scripting_environment, - turn_restrictions, - conditional_turn_restrictions, - maneuver_overrides); + Compress(scripting_environment, turn_restrictions, maneuver_overrides); CompressGeometry(); CompressAnnotationData(); } @@ -82,18 +78,15 @@ void NodeBasedGraphFactory::LoadDataFromFile(const boost::filesystem::path &inpu }()); } -void NodeBasedGraphFactory::Compress( - ScriptingEnvironment &scripting_environment, - std::vector &turn_restrictions, - std::vector &conditional_turn_restrictions, - std::vector &maneuver_overrides) +void NodeBasedGraphFactory::Compress(ScriptingEnvironment &scripting_environment, + std::vector &turn_restrictions, + std::vector &maneuver_overrides) { GraphCompressor graph_compressor; graph_compressor.Compress(barriers, traffic_signals, scripting_environment, turn_restrictions, - conditional_turn_restrictions, maneuver_overrides, compressed_output_graph, annotation_data, diff --git a/src/extractor/restriction_compressor.cpp b/src/extractor/restriction_compressor.cpp index 506fd1ed4c9..21fdd7f8667 100644 --- a/src/extractor/restriction_compressor.cpp +++ b/src/extractor/restriction_compressor.cpp @@ -3,7 +3,6 @@ #include #include -#include namespace osrm { @@ -12,41 +11,35 @@ namespace extractor RestrictionCompressor::RestrictionCompressor( std::vector &restrictions, - std::vector &conditional_turn_restrictions, std::vector &maneuver_overrides) { - // add a node restriction ptr to the starts/ends maps, needs to be a reference! + // add a turn restriction ptr to the starts/ends maps, needs to be a reference! auto index = [&](auto &element) { - starts.insert(std::make_pair(element.from, &element)); - ends.insert(std::make_pair(element.to, &element)); - }; - // !needs to be reference, so we can get the correct address - const auto index_starts_and_ends = [&](auto &restriction) { - if (restriction.Type() == RestrictionType::WAY_RESTRICTION) + starts.insert({element.From(), &element}); + ends.insert({element.To(), &element}); + if (element.Type() == RestrictionType::WAY_RESTRICTION) { - auto &way_restriction = restriction.AsWayRestriction(); - index(way_restriction.in_restriction); - index(way_restriction.out_restriction); - } - else - { - BOOST_ASSERT(restriction.Type() == RestrictionType::NODE_RESTRICTION); - auto &node_restriction = restriction.AsNodeRestriction(); - index(node_restriction); + const auto &way_via = element.AsWayRestriction().via; + BOOST_ASSERT(way_via.size() >= 2); + // No need to track the first and last via nodes as they will not be compressed. + for (const auto &via_node : + boost::make_iterator_range(way_via.begin() + 1, way_via.end() - 1)) + { + vias.insert({via_node, &element}); + } } }; + // !needs to be reference, so we can get the correct address + const auto index_starts_ends_vias = [&](auto &restriction) { index(restriction); }; - // add all restrictions as their respective startend pointers - std::for_each(restrictions.begin(), restrictions.end(), index_starts_and_ends); - std::for_each(conditional_turn_restrictions.begin(), - conditional_turn_restrictions.end(), - index_starts_and_ends); + // add all restrictions as their respective start/via/end pointers + std::for_each(restrictions.begin(), restrictions.end(), index_starts_ends_vias); auto index_maneuver = [&](auto &maneuver) { for (auto &turn : maneuver.turn_sequence) { - maneuver_starts.insert(std::make_pair(turn.from, &turn)); - maneuver_ends.insert(std::make_pair(turn.to, &turn)); + maneuver_starts.insert({turn.from, &turn}); + maneuver_ends.insert({turn.to, &turn}); } }; // !needs to be reference, so we can get the correct address @@ -58,26 +51,48 @@ RestrictionCompressor::RestrictionCompressor( void RestrictionCompressor::Compress(const NodeID from, const NodeID via, const NodeID to) { // handle turn restrictions - // extract all startptrs and move them from via to from. + // extract all start ptrs and move them from via to from. auto all_starts_range = starts.equal_range(via); - std::vector start_ptrs; + std::vector start_ptrs; std::transform(all_starts_range.first, all_starts_range.second, std::back_inserter(start_ptrs), [](const auto pair) { return pair.second; }); const auto update_start = [&](auto ptr) { - // ____ | from - p.from | via - p.via | to - p.to | ____ - BOOST_ASSERT(ptr->from == via); - if (ptr->via == to) + if (ptr->Type() == RestrictionType::NODE_RESTRICTION) { - ptr->from = from; + + // ____ | from - p.from | via - p.via | to - p.to | ____ + auto &node_ptr = ptr->AsNodeRestriction(); + BOOST_ASSERT(node_ptr.from == via); + if (node_ptr.via == to) + { + node_ptr.from = from; + } + // ____ | to - p.from | via - p.via | from - p.to | ____ + else + { + BOOST_ASSERT(node_ptr.via == from); + node_ptr.from = to; + } } - // ____ | to - p.from | via - p.via | from - p.to | ____ else { - BOOST_ASSERT(ptr->via == from); - ptr->from = to; + BOOST_ASSERT(ptr->Type() == RestrictionType::WAY_RESTRICTION); + auto &way_ptr = ptr->AsWayRestriction(); + // ____ | from - p.from | via - p.via[0] | to - p[1..],p.to | ____ + BOOST_ASSERT(way_ptr.from == via); + if (way_ptr.via.front() == to) + { + way_ptr.from = from; + } + // ____ | to - p.from | via - p.via[0] | from - p[1,..],p.to | ____ + else + { + BOOST_ASSERT(way_ptr.via.front() == from); + way_ptr.from = to; + } } }; @@ -86,29 +101,52 @@ void RestrictionCompressor::Compress(const NodeID from, const NodeID via, const // update the ptrs in our mapping starts.erase(via); - const auto reinsert_start = [&](auto ptr) { starts.insert(std::make_pair(ptr->from, ptr)); }; + const auto reinsert_start = [&](auto ptr) { starts.insert({ptr->From(), ptr}); }; std::for_each(start_ptrs.begin(), start_ptrs.end(), reinsert_start); // extract all end ptrs and move them from via to to auto all_ends_range = ends.equal_range(via); - std::vector end_ptrs; + std::vector end_ptrs; std::transform(all_ends_range.first, all_ends_range.second, std::back_inserter(end_ptrs), [](const auto pair) { return pair.second; }); const auto update_end = [&](auto ptr) { - BOOST_ASSERT(ptr->to == via); - // p.from | ____ - p.via | from - p.to | via - ____ | to - if (ptr->via == from) + if (ptr->Type() == RestrictionType::NODE_RESTRICTION) { - ptr->to = to; + auto &node_ptr = ptr->AsNodeRestriction(); + + BOOST_ASSERT(node_ptr.to == via); + // p.from | ____ - p.via | from - p.to | via - ____ | to + if (node_ptr.via == from) + { + node_ptr.to = to; + } + // p.from | ____ - p.via | to - p.to | via - ____ | from + else + { + BOOST_ASSERT(node_ptr.via == to); + node_ptr.to = from; + } } - // p.from | ____ - p.via | to - p.to | via - ____ | from else { - BOOST_ASSERT(ptr->via == to); - ptr->to = from; + BOOST_ASSERT(ptr->Type() == RestrictionType::WAY_RESTRICTION); + auto &way_ptr = ptr->AsWayRestriction(); + + BOOST_ASSERT(way_ptr.to == via); + // p.from,p.via[..,n-1] | ____ - p.via[n] | from - p.to | via - ____ | to + if (way_ptr.via.back() == from) + { + way_ptr.to = to; + } + // p.from,p.via[..,n-1] | ____ - p.via[n] | to - p.to | via - ____ | from + else + { + BOOST_ASSERT(way_ptr.via.back() == to); + way_ptr.to = from; + } } }; std::for_each(end_ptrs.begin(), end_ptrs.end(), update_end); @@ -116,9 +154,24 @@ void RestrictionCompressor::Compress(const NodeID from, const NodeID via, const // update end ptrs in mapping ends.erase(via); - const auto reinsert_end = [&](auto ptr) { ends.insert(std::make_pair(ptr->to, ptr)); }; + const auto reinsert_end = [&](auto ptr) { ends.insert({ptr->To(), ptr}); }; std::for_each(end_ptrs.begin(), end_ptrs.end(), reinsert_end); + // remove compressed node from all via paths + auto all_vias_range = vias.equal_range(via); + + const auto update_via = [&](auto restriction_pair) { + BOOST_ASSERT(restriction_pair.second->Type() == RestrictionType::WAY_RESTRICTION); + auto &way_ptr = restriction_pair.second->AsWayRestriction(); + BOOST_ASSERT(std::find(way_ptr.via.begin(), way_ptr.via.end(), via) != way_ptr.via.end()); + way_ptr.via.erase(std::remove(way_ptr.via.begin(), way_ptr.via.end(), via), + way_ptr.via.end()); + }; + std::for_each(all_vias_range.first, all_vias_range.second, update_via); + + // update via ptrs in mapping + vias.erase(via); + /**********************************************************************************************/ // handle maneuver overrides from nodes @@ -149,9 +202,7 @@ void RestrictionCompressor::Compress(const NodeID from, const NodeID via, const // update the ptrs in our mapping maneuver_starts.erase(via); - const auto reinsert_start_mnv = [&](auto ptr) { - maneuver_starts.insert(std::make_pair(ptr->from, ptr)); - }; + const auto reinsert_start_mnv = [&](auto ptr) { maneuver_starts.insert({ptr->from, ptr}); }; std::for_each(mnv_start_ptrs.begin(), mnv_start_ptrs.end(), reinsert_start_mnv); /**********************************************************************************************/ @@ -183,9 +234,7 @@ void RestrictionCompressor::Compress(const NodeID from, const NodeID via, const // update end ptrs in mapping maneuver_ends.erase(via); - const auto reinsert_end_mnvs = [&](auto ptr) { - maneuver_ends.insert(std::make_pair(ptr->to, ptr)); - }; + const auto reinsert_end_mnvs = [&](auto ptr) { maneuver_ends.insert({ptr->to, ptr}); }; std::for_each(mnv_end_ptrs.begin(), mnv_end_ptrs.end(), reinsert_end_mnvs); } diff --git a/src/extractor/restriction_filter.cpp b/src/extractor/restriction_filter.cpp index ff14ece13fb..cc5484e0696 100644 --- a/src/extractor/restriction_filter.cpp +++ b/src/extractor/restriction_filter.cpp @@ -1,5 +1,6 @@ #include "extractor/restriction_filter.hpp" #include "util/node_based_graph.hpp" +#include "util/timing_util.hpp" #include "util/typedefs.hpp" #include @@ -10,60 +11,48 @@ namespace osrm namespace extractor { -std::vector -removeInvalidRestrictions(std::vector restrictions, +std::vector +removeInvalidRestrictions(std::vector restrictions, const util::NodeBasedDynamicGraph &node_based_graph) { - // definition of what we presume to be a valid via-node restriction - const auto is_valid_node = [&node_based_graph](const auto &node_restriction) { - // a valid restriction needs to be connected to both its from and to locations - bool found_from = false, found_to = false; - for (auto eid : node_based_graph.GetAdjacentEdgeRange(node_restriction.via)) + util::UnbufferedLog log; + log << "Removing invalid restrictions..."; + TIMER_START(remove_invalid_restrictions); + + const auto is_valid_edge = [&node_based_graph](const auto from, const auto to) { + const auto eid = node_based_graph.FindEdge(from, to); + if (eid == SPECIAL_EDGEID) { - const auto target = node_based_graph.GetTarget(eid); - if (target == node_restriction.from) - found_from = true; - if (target == node_restriction.to) - found_to = true; + util::Log(logDEBUG) << "Restriction has invalid edge: " << from << ", " << to; + return false; } - if (!found_from || !found_to) + const auto &edge_data = node_based_graph.GetEdgeData(eid); + if (edge_data.reversed) + { + util::Log(logDEBUG) << "Restriction has non-traversable edge: " << from << ", " << to; return false; - + } return true; }; - // definition of what we presume to be a valid via-way restriction - const auto is_valid_way = [&node_based_graph, is_valid_node](const auto &way_restriction) { - const auto eid = node_based_graph.FindEdge(way_restriction.in_restriction.via, - way_restriction.out_restriction.via); - - // ability filter, we currently cannot handle restrictions that do not match up in geometry: - // restrictions cannot be interrupted by traffic signals or other similar entities that - // cause node penalties - if ((way_restriction.in_restriction.via != way_restriction.out_restriction.from) || - (way_restriction.out_restriction.via != way_restriction.in_restriction.to)) - return false; - - // the edge needs to exit (we cannot handle intermediate stuff, so far) - if (eid == SPECIAL_EDGEID) - return false; - - const auto &data = node_based_graph.GetEdgeData(eid); - - // edge needs to be traversable for a valid restrction - if (data.reversed) - return false; + const auto is_valid_node = [is_valid_edge](const auto &node_restriction) { + return is_valid_edge(node_restriction.from, node_restriction.via) && + is_valid_edge(node_restriction.via, node_restriction.to); + }; - // is the in restriction referencing the correct nodes - if (!is_valid_node(way_restriction.in_restriction)) + const auto is_valid_way = [is_valid_edge](const auto &way_restriction) { + if (!is_valid_edge(way_restriction.from, way_restriction.via.front())) return false; - // is the out restriction referencing the correct nodes - if (!is_valid_node(way_restriction.out_restriction)) + const auto invalid_it = std::adjacent_find( + way_restriction.via.begin(), + way_restriction.via.end(), + [&](auto via_from, auto via_to) { return !is_valid_edge(via_from, via_to); }); + if (invalid_it != way_restriction.via.end()) return false; - return true; + return is_valid_edge(way_restriction.via.back(), way_restriction.to); }; const auto is_invalid = [is_valid_way, is_valid_node](const auto &restriction) { @@ -80,8 +69,13 @@ removeInvalidRestrictions(std::vector restrictions, const auto end_valid_restrictions = std::remove_if(restrictions.begin(), restrictions.end(), is_invalid); + const auto num_removed = std::distance(end_valid_restrictions, restrictions.end()); restrictions.erase(end_valid_restrictions, restrictions.end()); + TIMER_STOP(remove_invalid_restrictions); + log << "removed " << num_removed << " invalid restrictions, after " + << TIMER_SEC(remove_invalid_restrictions) << "s"; + return restrictions; } diff --git a/src/extractor/restriction_graph.cpp b/src/extractor/restriction_graph.cpp new file mode 100644 index 00000000000..a5c2a9a0345 --- /dev/null +++ b/src/extractor/restriction_graph.cpp @@ -0,0 +1,288 @@ +#include "extractor/restriction_graph.hpp" +#include "util/node_based_graph.hpp" +#include "util/timing_util.hpp" +#include + +#include +#include + +namespace osrm +{ +namespace extractor +{ + +namespace restriction_graph_details +{ + +void insertEdge(RestrictionGraph &rg, const RestrictionID id, const RestrictionEdge &edge) +{ + const auto range = rg.GetEdges(id); + auto &node = rg.nodes[id]; + if (node.edges_begin_idx + range.size() != rg.edges.size()) + { + // Most nodes will only have one edge, so this copy will be infrequent + node.edges_begin_idx = rg.edges.size(); + std::copy(range.begin(), range.end(), std::back_inserter(rg.edges)); + } + rg.edges.push_back(edge); + node.num_edges += 1; +} + +void insertRestriction(RestrictionGraph &rg, + const RestrictionID id, + const TurnRestriction *restriction) +{ + const auto range = rg.GetRestrictions(id); + auto &node = rg.nodes[id]; + if (node.restrictions_begin_idx + range.size() != rg.restrictions.size()) + { + // Most nodes will only have zero or one restriction, so this copy will be infrequent + node.restrictions_begin_idx = rg.restrictions.size(); + std::copy(range.begin(), range.end(), std::back_inserter(rg.restrictions)); + } + rg.restrictions.push_back(restriction); + node.num_restrictions += 1; +} + +RestrictionID getOrInsertStartNode(RestrictionGraph &rg, NodeID from, NodeID to) +{ + auto start_edge = std::make_pair(from, to); + auto start_node_idx_itr = rg.start_edge_to_node.find(start_edge); + if (start_node_idx_itr == rg.start_edge_to_node.end()) + { + // First time we have seen a restriction start from this edge. + auto start_node_idx = rg.nodes.size(); + rg.nodes.push_back(RestrictionNode{rg.restrictions.size(), 0, rg.edges.size(), 0}); + start_node_idx_itr = rg.start_edge_to_node.insert({start_edge, start_node_idx}).first; + } + return start_node_idx_itr->second; +} + +RestrictionID insertViaNode(RestrictionGraph &rg, NodeID from, NodeID to) +{ + auto new_via_node_idx = rg.nodes.size(); + rg.nodes.push_back(RestrictionNode{rg.restrictions.size(), 0, rg.edges.size(), 0}); + rg.via_edge_to_node.insert({{from, to}, new_via_node_idx}); + return new_via_node_idx; +} + +// pathBuilder builds the prefix tree structure that is used to first insert turn restrictions +// into the graph. +struct pathBuilder +{ + RestrictionGraph &rg; + RestrictionID cur_node; + + pathBuilder(RestrictionGraph &rg_) : rg(rg_) { cur_node = SPECIAL_RESTRICTIONID; } + + static std::string name() { return "path builder"; }; + + void start(NodeID from, NodeID to) { cur_node = getOrInsertStartNode(rg, from, to); } + + void next(NodeID from, NodeID to) + { + const auto &edge_range = rg.GetEdges(cur_node); + auto edge_to_itr = std::find_if(edge_range.begin(), + edge_range.end(), + [&](const auto &edge) { return edge.node_based_to == to; }); + if (edge_to_itr != edge_range.end()) + { + cur_node = edge_to_itr->target; + return; + } + + // This is a new restriction path we have not seen before. + // Add a new via node and edge to the tree. + auto new_via_node_idx = insertViaNode(rg, from, to); + insertEdge(rg, cur_node, RestrictionEdge{to, new_via_node_idx, false}); + + cur_node = new_via_node_idx; + } + + void end(const TurnRestriction &restriction) { insertRestriction(rg, cur_node, &restriction); } +}; + +// transferBuilder adds the transfer edges between overlapping restriction paths. It does this +// by tracking paths in the graph that can be equal to a suffix of a restriction path, and +// attempting to connect the them with a new edge. +struct transferBuilder +{ + RestrictionGraph &rg; + std::vector suffix_nodes; + RestrictionID cur_node; + + transferBuilder(RestrictionGraph &rg_) : rg(rg_) { cur_node = SPECIAL_RESTRICTIONID; } + + static std::string name() { return "transfer builder"; }; + + void start(NodeID from, NodeID to) { cur_node = getOrInsertStartNode(rg, from, to); } + + void next_suffixes(NodeID from, NodeID to) + { + // Update the suffix paths to include those that also have this edge + auto new_suffix_it = suffix_nodes.begin(); + for (const auto &suffix_node : suffix_nodes) + { + const auto &edges = rg.GetEdges(suffix_node); + const auto edge_it = std::find_if(edges.begin(), edges.end(), [&to](const auto &edge) { + return edge.node_based_to == to && !edge.is_transfer; + }); + if (edge_it != edges.end()) + { + *(new_suffix_it++) = edge_it->target; + } + } + suffix_nodes.erase(new_suffix_it, suffix_nodes.end()); + + // Add a start node if it is a trivial suffix + auto start_way_it = rg.start_edge_to_node.find({from, to}); + if (start_way_it != rg.start_edge_to_node.end()) + { + suffix_nodes.push_back({start_way_it->second}); + } + } + + // For each edge on path, if there is an edge in the suffix path not on the current + // path, add as a transfer. + void add_suffix_transfer(const RestrictionID &suffix_node) + { + for (const auto &suffix_edge : rg.GetEdges(suffix_node)) + { + if (suffix_edge.is_transfer) + continue; + + // Check there are no unconditional restrictions at the current node that would prevent + // the transfer + const auto &restrictions = rg.GetRestrictions(cur_node); + const auto is_restricted = + std::any_of(restrictions.begin(), restrictions.end(), [&](const auto &restriction) { + return restriction->IsTurnRestricted(suffix_edge.node_based_to) && + restriction->IsUnconditional(); + }); + if (is_restricted) + continue; + + const auto &edges = rg.GetEdges(cur_node); + // Check that the suffix edge is not a next edge along the current path. + const auto can_transfer = std::none_of(edges.begin(), edges.end(), [&](auto &edge) { + return edge.node_based_to == suffix_edge.node_based_to; + }); + if (can_transfer) + { + insertEdge(rg, + cur_node, + RestrictionEdge{suffix_edge.node_based_to, suffix_edge.target, true}); + } + } + }; + + void next(NodeID from, NodeID to) + { + const auto &edges = rg.GetEdges(cur_node); + auto next_edge_itr = std::find_if( + edges.begin(), edges.end(), [&](const auto edge) { return edge.node_based_to == to; }); + + // We know this edge exists + cur_node = next_edge_itr->target; + + this->next_suffixes(from, to); + + std::for_each(suffix_nodes.begin(), suffix_nodes.end(), [&](const auto &suffix_node) { + this->add_suffix_transfer(suffix_node); + }); + + for (const auto &suffix_node : suffix_nodes) + { + // Also add any restrictions from suffix paths to the current node in the + // restriction graph. + for (const auto &restriction : rg.GetRestrictions(suffix_node)) + { + insertRestriction(rg, cur_node, restriction); + } + } + } + + void end(const TurnRestriction & /*unused*/) {} +}; + +template +void buildGraph(RestrictionGraph &rg, const std::vector &restrictions) +{ + const auto run_builder = [&](const auto &restriction) { + builder_type builder(rg); + + builder.start(restriction.From(), restriction.FirstVia()); + if (restriction.Type() == RestrictionType::WAY_RESTRICTION) + { + const auto &way_restriction = restriction.AsWayRestriction(); + util::for_each_pair(way_restriction.via.begin(), + way_restriction.via.end(), + [&](NodeID from, NodeID to) { builder.next(from, to); }); + } + builder.end(restriction); + }; + + std::for_each(restrictions.begin(), restrictions.end(), run_builder); +} +} // namespace restriction_graph_details + +RestrictionGraph constructRestrictionGraph(const std::vector &turn_restrictions) +{ + util::UnbufferedLog log; + log << "Constructing restriction graph on " << turn_restrictions.size() << " restrictions..."; + TIMER_START(construct_restriction_graph); + + namespace rgd = restriction_graph_details; + RestrictionGraph rg; + rgd::buildGraph(rg, turn_restrictions); + rgd::buildGraph(rg, turn_restrictions); + + // Reorder nodes so that via nodes are at the front. This makes it easier to represent the + // bijection between restriction graph via nodes and edge-based graph duplicate nodes. + std::vector is_via_node(rg.nodes.size(), false); + for (const auto &entry : rg.via_edge_to_node) + { + is_via_node[entry.second] = true; + } + std::vector ordering(rg.nodes.size()); + std::iota(ordering.begin(), ordering.end(), 0); + std::partition(ordering.begin(), ordering.end(), [&](const auto i) { return is_via_node[i]; }); + // Start renumbering + const auto permutation = util::orderingToPermutation(ordering); + util::inplacePermutation(rg.nodes.begin(), rg.nodes.end(), permutation); + std::for_each(rg.edges.begin(), rg.edges.end(), [&](auto &edge) { + edge.target = permutation[edge.target]; + }); + rg.num_via_nodes = std::count(is_via_node.begin(), is_via_node.end(), true); + for (auto &entry : rg.via_edge_to_node) + { + entry.second = permutation[entry.second]; + } + for (auto &entry : rg.start_edge_to_node) + { + entry.second = permutation[entry.second]; + } + + TIMER_STOP(construct_restriction_graph); + log << "ok, after " << TIMER_SEC(construct_restriction_graph) << "s"; + + return rg; +} + +RestrictionGraph::RestrictionRange RestrictionGraph::GetRestrictions(RestrictionID id) const +{ + const auto &node = nodes[id]; + return boost::make_iterator_range(restrictions.begin() + node.restrictions_begin_idx, + restrictions.begin() + node.restrictions_begin_idx + + node.num_restrictions); +} + +RestrictionGraph::EdgeRange RestrictionGraph::GetEdges(RestrictionID id) const +{ + const auto &node = nodes[id]; + return boost::make_iterator_range(edges.begin() + node.edges_begin_idx, + edges.begin() + node.edges_begin_idx + node.num_edges); +} + +} // namespace extractor +} // namespace osrm diff --git a/src/extractor/restriction_parser.cpp b/src/extractor/restriction_parser.cpp index 2065aa50370..28ff24a57db 100644 --- a/src/extractor/restriction_parser.cpp +++ b/src/extractor/restriction_parser.cpp @@ -4,7 +4,6 @@ #include "util/conditional_restrictions.hpp" #include "util/log.hpp" -#include #include #include #include @@ -15,7 +14,6 @@ #include #include -#include namespace osrm { @@ -54,7 +52,7 @@ RestrictionParser::RestrictionParser(bool use_turn_restrictions_, * in the corresponding profile. We use it for both namespacing restrictions, as in * restriction:motorcar as well as whitelisting if its in except:motorcar. */ -boost::optional +boost::optional RestrictionParser::TryParse(const osmium::Relation &relation) const { // return if turn restrictions should be ignored @@ -122,14 +120,13 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const } } - // we pretend every restriction is a conditional restriction. If we do not find any restriction, - // we can trim away the vector after parsing - InputConditionalTurnRestriction restriction_container; + InputTurnRestriction restriction_container; restriction_container.is_only = is_only_restriction; constexpr auto INVALID_OSM_ID = std::numeric_limits::max(); auto from = INVALID_OSM_ID; - auto via = INVALID_OSM_ID; + auto via_node = INVALID_OSM_ID; + std::vector via_ways; auto to = INVALID_OSM_ID; bool is_node_restriction = true; @@ -152,7 +149,7 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const continue; } BOOST_ASSERT(0 == strcmp("via", role)); - via = static_cast(member.ref()); + via_node = static_cast(member.ref()); is_node_restriction = true; // set via node id break; @@ -170,7 +167,7 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const } else if (0 == strcmp("via", role)) { - via = static_cast(member.ref()); + via_ways.push_back({static_cast(member.ref())}); is_node_restriction = false; } break; @@ -211,17 +208,18 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const } } - if (from != INVALID_OSM_ID && via != INVALID_OSM_ID && to != INVALID_OSM_ID) + if (from != INVALID_OSM_ID && (via_node != INVALID_OSM_ID || !via_ways.empty()) && + to != INVALID_OSM_ID) { if (is_node_restriction) { // template struct requires bracket for ID initialisation :( - restriction_container.node_or_way = InputNodeRestriction{{from}, {via}, {to}}; + restriction_container.node_or_way = InputNodeRestriction{{from}, {via_node}, {to}}; } else { // template struct requires bracket for ID initialisation :( - restriction_container.node_or_way = InputWayRestriction{{from}, {via}, {to}}; + restriction_container.node_or_way = InputWayRestriction{{from}, via_ways, {to}}; } return restriction_container; } diff --git a/src/extractor/scripting_environment_lua.cpp b/src/extractor/scripting_environment_lua.cpp index ca0072561ce..c8e55fd55f9 100644 --- a/src/extractor/scripting_environment_lua.cpp +++ b/src/extractor/scripting_environment_lua.cpp @@ -870,7 +870,7 @@ void Sol2ScriptingEnvironment::ProcessElements( const ExtractionRelationContainer &relations, std::vector> &resulting_nodes, std::vector> &resulting_ways, - std::vector &resulting_restrictions, + std::vector &resulting_restrictions, std::vector &resulting_maneuver_overrides) { ExtractionNode result_node; diff --git a/src/extractor/way_restriction_map.cpp b/src/extractor/way_restriction_map.cpp index 5ccb9292cfd..bc1c90e09e4 100644 --- a/src/extractor/way_restriction_map.cpp +++ b/src/extractor/way_restriction_map.cpp @@ -1,8 +1,5 @@ #include "extractor/way_restriction_map.hpp" -#include "util/for_each_pair.hpp" -#include -#include #include #include @@ -11,247 +8,117 @@ namespace osrm namespace extractor { -namespace +WayRestrictionMap::WayRestrictionMap(const RestrictionGraph &restriction_graph) + : restriction_graph(restriction_graph) { -struct FindViaWay -{ - bool operator()(const std::tuple value, - const TurnRestriction &restriction) const - { - const auto &way = restriction.AsWayRestriction(); - return value < std::tie(way.in_restriction.via, way.out_restriction.via); - } - bool operator()(const TurnRestriction &restriction, - const std::tuple value) const - { - const auto &way = restriction.AsWayRestriction(); - return std::tie(way.in_restriction.via, way.out_restriction.via) < value; - } -}; - -template auto asDuplicatedNode(const restriction_type &restriction) -{ - auto &way = restriction.AsWayRestriction(); - // group restrictions by the via-way. On same via-ways group by from - return std::tie(way.in_restriction.via, way.out_restriction.via, way.in_restriction.from); -} - -template struct CompareByDuplicatedNode -{ - bool operator()(const ConditionalTurnRestriction &lhs, const ConditionalTurnRestriction &rhs) - { - if (asDuplicatedNode(lhs) < asDuplicatedNode(rhs)) - { - return true; - } - else if (asDuplicatedNode(rhs) < asDuplicatedNode(lhs)) - { - return false; - } - else - { - const auto lhs_to = lhs.AsWayRestriction().out_restriction.to; - const auto rhs_to = rhs.AsWayRestriction().out_restriction.to; - - const bool has_conditions_lhs = !lhs.condition.empty(); - const bool has_conditions_rhs = !rhs.condition.empty(); - - return std::tie(lhs_to, lhs.is_only, has_conditions_lhs) < - std::tie(rhs_to, rhs.is_only, has_conditions_rhs); - } - } -}; - -template -std::vector -extractRestrictions(const std::vector &turn_restrictions) -{ - std::vector result; - for (const auto &turn_restriction : turn_restrictions) - { - if (turn_restriction.Type() == RestrictionType::WAY_RESTRICTION) - { - const auto &way = turn_restriction.AsWayRestriction(); - - // so far we can only handle restrictions that are not interrupted - if (way.in_restriction.via == way.out_restriction.from && - way.in_restriction.to == way.out_restriction.via) - result.push_back(turn_restriction); - } - } - std::sort(result.begin(), result.end(), CompareByDuplicatedNode()); - auto new_end = std::unique(result.begin(), result.end()); - result.erase(new_end, result.end()); - return result; -} - -template struct ByInFromAndVia -{ - std::pair operator()(const restriction_type &restriction) - { - const auto &way = restriction.AsWayRestriction(); - return std::make_pair(way.in_restriction.from, way.in_restriction.via); - } -}; - -} // namespace - -// get all way restrictions -WayRestrictionMap::WayRestrictionMap( - const std::vector &turn_restrictions_) - : restriction_data(extractRestrictions(turn_restrictions_)), - restriction_starts(restriction_data, ByInFromAndVia()) -{ - std::size_t offset = 1; - // the first group starts at 0 - if (!restriction_data.empty()) - duplicated_node_groups.push_back(0); - - auto const add_offset_on_new_groups = [&](auto const &lhs, auto const &rhs) { - BOOST_ASSERT(rhs == restriction_data[offset]); - BOOST_ASSERT(lhs.Type() == RestrictionType::WAY_RESTRICTION); - BOOST_ASSERT(rhs.Type() == RestrictionType::WAY_RESTRICTION); - // add a new lower bound for rhs - if (asDuplicatedNode(lhs) != asDuplicatedNode(rhs)) - duplicated_node_groups.push_back(offset); - ++offset; - }; - util::for_each_pair(restriction_data.begin(), restriction_data.end(), add_offset_on_new_groups); - duplicated_node_groups.push_back(restriction_data.size()); } std::size_t WayRestrictionMap::NumberOfDuplicatedNodes() const { - return duplicated_node_groups.size() - 1; + return restriction_graph.num_via_nodes; } -bool WayRestrictionMap::IsViaWay(const NodeID from, const NodeID to) const +bool WayRestrictionMap::IsViaWayEdge(const NodeID from, const NodeID to) const { - // safe-guards - if (restriction_data.empty()) - return false; - - const auto itr = std::lower_bound( - restriction_data.begin(), restriction_data.end(), std::make_tuple(from, to), FindViaWay()); - - // no fitting restriction - if (itr == restriction_data.end()) - return false; - - const auto &way = itr->AsWayRestriction(); - - return way.out_restriction.from == from && way.out_restriction.via == to; -} - -DuplicatedNodeID WayRestrictionMap::AsDuplicatedNodeID(const RestrictionID restriction_id) const -{ - const auto upper_bound_restriction = std::upper_bound( - duplicated_node_groups.begin(), duplicated_node_groups.end(), restriction_id); - const auto distance_to_upper_bound = - std::distance(duplicated_node_groups.begin(), upper_bound_restriction); - return distance_to_upper_bound - 1; + return restriction_graph.via_edge_to_node.count({from, to}) > 0; } std::vector WayRestrictionMap::DuplicatedNodeIDs(const NodeID from, const NodeID to) const { - const auto duplicated_node_range_itr = std::equal_range( - restriction_data.begin(), restriction_data.end(), std::make_tuple(from, to), FindViaWay()); - - const auto as_restriction_id = [this](const auto itr) { - return std::distance(restriction_data.begin(), itr); - }; - - auto first = AsDuplicatedNodeID(as_restriction_id(duplicated_node_range_itr.first)); - auto end = AsDuplicatedNodeID(as_restriction_id(duplicated_node_range_itr.second)); - - std::vector result(end - first); - std::iota(result.begin(), result.end(), first); - + const auto restriction_ways = restriction_graph.via_edge_to_node.equal_range({from, to}); + std::vector result; + std::transform(restriction_ways.first, + restriction_ways.second, + std::back_inserter(result), + [](const auto &range_val) { return DuplicatedNodeID(range_val.second); }); return result; } bool WayRestrictionMap::IsRestricted(DuplicatedNodeID duplicated_node, const NodeID to) const { - // loop over all restrictions associated with the node. Mark as restricted based on - // is_only/restricted targets - for (RestrictionID restriction_index = duplicated_node_groups[duplicated_node]; - restriction_index != duplicated_node_groups[duplicated_node + 1]; - ++restriction_index) - { - BOOST_ASSERT(restriction_index < restriction_data.size()); - const auto &restriction = restriction_data[restriction_index]; - const auto &way = restriction.AsWayRestriction(); + // Checks if a turn to 'to' is restricted + BOOST_ASSERT(duplicated_node < restriction_graph.num_via_nodes); + const auto &restrictions = restriction_graph.GetRestrictions(duplicated_node); + return std::any_of(restrictions.begin(), restrictions.end(), [&to](const auto &restriction) { + return restriction->IsTurnRestricted(to); + }); +} - if (restriction.is_only) - return way.out_restriction.to != to; - else if (to == way.out_restriction.to) - return true; +std::vector +WayRestrictionMap::GetRestrictions(DuplicatedNodeID duplicated_node, const NodeID to) const +{ + std::vector result; + // Fetch all restrictions that will restrict a turn to 'to'. + BOOST_ASSERT(duplicated_node < restriction_graph.num_via_nodes); + const auto &restrictions = restriction_graph.GetRestrictions(duplicated_node); + std::copy_if(restrictions.begin(), + restrictions.end(), + std::back_inserter(result), + [&to](const auto &restriction) { return restriction->IsTurnRestricted(to); }); + if (result.empty()) + { + throw( + "Asking for the restriction of an unrestricted turn. Check with `IsRestricted` before " + "calling GetRestriction"); } - return false; + return result; } -ConditionalTurnRestriction const & -WayRestrictionMap::GetRestriction(DuplicatedNodeID duplicated_node, const NodeID to) const +std::vector WayRestrictionMap::DuplicatedViaEdges() const { - // loop over all restrictions associated with the node. Mark as restricted based on - // is_only/restricted targets - for (RestrictionID restriction_index = duplicated_node_groups[duplicated_node]; - restriction_index != duplicated_node_groups[duplicated_node + 1]; - ++restriction_index) - { - BOOST_ASSERT(restriction_index < restriction_data.size()); - const auto &restriction = restriction_data[restriction_index]; - const auto &way = restriction.AsWayRestriction(); + std::vector result; + result.resize(NumberOfDuplicatedNodes()); - if (restriction.is_only && (way.out_restriction.to != to)) - { - return restriction; - } - else if (!restriction.is_only && (to == way.out_restriction.to)) - { - return restriction; - } + // We use the node id from the restriction graph to enumerate all + // duplicate nodes, and map them to their node based edge representation. + // This means an node based edge (from,to) can have many duplicate nodes. + for (auto entry : restriction_graph.via_edge_to_node) + { + result[entry.second] = {entry.first.first, entry.first.second}; } - throw("Asking for the restriction of an unrestricted turn. Check with `IsRestricted` before " - "calling GetRestriction"); + return result; } -std::vector WayRestrictionMap::DuplicatedNodeRepresentatives() const +NodeID WayRestrictionMap::RemapIfRestrictionStart(const NodeID edge_based_node, + const NodeID node_based_from, + const NodeID node_based_via, + const NodeID node_based_to, + const NodeID number_of_edge_based_nodes) const { - std::vector result; - result.reserve(NumberOfDuplicatedNodes()); - std::transform(duplicated_node_groups.begin(), - duplicated_node_groups.end() - 1, - std::back_inserter(result), - [&](auto const representative_id) -> ViaWay { - auto &way = restriction_data[representative_id].AsWayRestriction(); - return {way.in_restriction.via, way.out_restriction.via}; - }); - return result; + + auto restriction_it = + restriction_graph.start_edge_to_node.find({node_based_from, node_based_via}); + if (restriction_it != restriction_graph.start_edge_to_node.end()) + { + for (const auto &edge : restriction_graph.GetEdges(restriction_it->second)) + { + if (edge.node_based_to == node_based_to) + { + return number_of_edge_based_nodes - NumberOfDuplicatedNodes() + edge.target; + } + } + } + return edge_based_node; } -NodeID WayRestrictionMap::RemapIfRestricted(const NodeID edge_based_node, - const NodeID node_based_from, - const NodeID node_based_via, - const NodeID node_based_to, - const NodeID number_of_edge_based_nodes) const +NodeID WayRestrictionMap::RemapIfRestrictionVia(const NodeID edge_based_target_node, + const NodeID edge_based_via_node, + const NodeID node_based_to, + const NodeID number_of_edge_based_nodes) const { - auto range = restriction_starts.Restrictions(node_based_from, node_based_via); - - // returns true if the ID saved in an iterator belongs to a turn restriction that references - // node_based_to as destination of the `in_restriction` - const auto restriction_targets_to = [node_based_to](const auto &pair) { - return pair.second->AsWayRestriction().in_restriction.to == node_based_to; - }; - const auto itr = std::find_if(range.first, range.second, restriction_targets_to); - // in case we found a matching restriction, we can remap the edge_based_node - if (itr != range.second) - return number_of_edge_based_nodes - NumberOfDuplicatedNodes() + - AsDuplicatedNodeID(itr->second - &restriction_data[0]); - else - return edge_based_node; + auto duplicated_node_id = + edge_based_via_node + NumberOfDuplicatedNodes() - number_of_edge_based_nodes; + BOOST_ASSERT(duplicated_node_id < restriction_graph.num_via_nodes); + for (const auto &edge : restriction_graph.GetEdges(duplicated_node_id)) + { + if (edge.node_based_to == node_based_to) + { + return number_of_edge_based_nodes - NumberOfDuplicatedNodes() + edge.target; + } + } + return edge_based_target_node; } } // namespace extractor diff --git a/src/guidance/guidance_processing.cpp b/src/guidance/guidance_processing.cpp index 7dec2aff3ca..0f3155046ea 100644 --- a/src/guidance/guidance_processing.cpp +++ b/src/guidance/guidance_processing.cpp @@ -201,9 +201,9 @@ void annotateTurns(const util::NodeBasedDynamicGraph &node_based_graph, // be fine. bearing_class_by_node_based_node[intersection_node] = bearing_class_id; - // check if we are turning off a via way - const auto turning_off_via_way = - way_restriction_map.IsViaWay(incoming_edge.node, intersection_node); + // check if we on a restriction via edge + const auto is_restriction_via_edge = + way_restriction_map.IsViaWayEdge(incoming_edge.node, intersection_node); for (const auto &outgoing_edge : outgoing_edges) { @@ -239,11 +239,11 @@ void annotateTurns(const util::NodeBasedDynamicGraph &node_based_graph, guidance::TurnBearing(intersection[0].perceived_bearing), guidance::TurnBearing(turn->perceived_bearing)}); - // when turning off a a via-way turn restriction, we need to not only + // When on the edge of a via-way turn restriction, we need to not only // handle the normal edges for the way, but also add turns for every // duplicated node. This process is integrated here to avoid doing the // turn analysis multiple times. - if (turning_off_via_way) + if (is_restriction_via_edge) { const auto duplicated_nodes = way_restriction_map.DuplicatedNodeIDs( incoming_edge.node, intersection_node); @@ -261,10 +261,18 @@ void annotateTurns(const util::NodeBasedDynamicGraph &node_based_graph, if (is_way_restricted) { - auto const restriction = way_restriction_map.GetRestriction( - duplicated_node_id, node_at_end_of_turn); - - if (restriction.condition.empty()) + auto const restrictions = + way_restriction_map.GetRestrictions( + duplicated_node_id, node_at_end_of_turn); + + auto has_unconditional = + std::any_of(restrictions.begin(), + restrictions.end(), + [](const auto &restriction) { + return restriction->IsUnconditional(); + }); + + if (has_unconditional) continue; buffer->delayed_turn_data.push_back(guidance::TurnData{ diff --git a/unit_tests/extractor/graph_compressor.cpp b/unit_tests/extractor/graph_compressor.cpp index 470463df71c..e49932afb0b 100644 --- a/unit_tests/extractor/graph_compressor.cpp +++ b/unit_tests/extractor/graph_compressor.cpp @@ -68,7 +68,6 @@ BOOST_AUTO_TEST_CASE(long_road_test) std::unordered_set barrier_nodes; std::unordered_set traffic_lights; std::vector restrictions; - std::vector conditional_restrictions; std::vector annotations(1); CompressedEdgeContainer container; test::MockScriptingEnvironment scripting_environment; @@ -92,7 +91,6 @@ BOOST_AUTO_TEST_CASE(long_road_test) traffic_lights, scripting_environment, restrictions, - conditional_restrictions, maneuver_overrides, graph, annotations, @@ -116,7 +114,6 @@ BOOST_AUTO_TEST_CASE(loop_test) std::unordered_set barrier_nodes; std::unordered_set traffic_lights; std::vector restrictions; - std::vector conditional_restrictions; CompressedEdgeContainer container; std::vector annotations(1); test::MockScriptingEnvironment scripting_environment; @@ -154,7 +151,6 @@ BOOST_AUTO_TEST_CASE(loop_test) traffic_lights, scripting_environment, restrictions, - conditional_restrictions, maneuver_overrides, graph, annotations, @@ -182,7 +178,6 @@ BOOST_AUTO_TEST_CASE(t_intersection) std::unordered_set traffic_lights; std::vector annotations(1); std::vector restrictions; - std::vector conditional_restrictions; CompressedEdgeContainer container; test::MockScriptingEnvironment scripting_environment; std::vector maneuver_overrides; @@ -205,7 +200,6 @@ BOOST_AUTO_TEST_CASE(t_intersection) traffic_lights, scripting_environment, restrictions, - conditional_restrictions, maneuver_overrides, graph, annotations, @@ -227,7 +221,6 @@ BOOST_AUTO_TEST_CASE(street_name_changes) std::unordered_set traffic_lights; std::vector annotations(2); std::vector restrictions; - std::vector conditional_restrictions; CompressedEdgeContainer container; test::MockScriptingEnvironment scripting_environment; std::vector maneuver_overrides; @@ -246,7 +239,6 @@ BOOST_AUTO_TEST_CASE(street_name_changes) traffic_lights, scripting_environment, restrictions, - conditional_restrictions, maneuver_overrides, graph, annotations, @@ -267,7 +259,6 @@ BOOST_AUTO_TEST_CASE(direction_changes) std::unordered_set traffic_lights; std::vector annotations(1); std::vector restrictions; - std::vector conditional_restrictions; CompressedEdgeContainer container; test::MockScriptingEnvironment scripting_environment; std::vector maneuver_overrides; @@ -282,7 +273,6 @@ BOOST_AUTO_TEST_CASE(direction_changes) traffic_lights, scripting_environment, restrictions, - conditional_restrictions, maneuver_overrides, graph, annotations, diff --git a/unit_tests/extractor/intersection_analysis_tests.cpp b/unit_tests/extractor/intersection_analysis_tests.cpp index c6a1ec228f1..b7ec2e707de 100644 --- a/unit_tests/extractor/intersection_analysis_tests.cpp +++ b/unit_tests/extractor/intersection_analysis_tests.cpp @@ -24,7 +24,6 @@ BOOST_AUTO_TEST_CASE(simple_intersection_connectivity) {EMPTY_NAMEID, 0, INAVLID_CLASS_DATA, TRAVEL_MODE_DRIVING, false}, {EMPTY_NAMEID, 1, INAVLID_CLASS_DATA, TRAVEL_MODE_DRIVING, false}}; std::vector restrictions{TurnRestriction{NodeRestriction{0, 2, 1}, false}}; - std::vector conditional_restrictions; CompressedEdgeContainer container; test::MockScriptingEnvironment scripting_environment; std::vector maneuver_overrides; @@ -90,7 +89,6 @@ BOOST_AUTO_TEST_CASE(simple_intersection_connectivity) traffic_lights, scripting_environment, restrictions, - conditional_restrictions, maneuver_overrides, graph, annotations, @@ -101,7 +99,8 @@ BOOST_AUTO_TEST_CASE(simple_intersection_connectivity) EdgeBasedNodeDataContainer node_data_container( std::vector(graph.GetNumberOfEdges()), annotations); - RestrictionMap restriction_map(restrictions, IndexNodeByFromAndVia()); + RestrictionGraph restriction_graph = constructRestrictionGraph(restrictions); + RestrictionMap restriction_map(restriction_graph); const auto connectivity_matrix = [&](NodeID node) { std::vector result; @@ -156,7 +155,6 @@ BOOST_AUTO_TEST_CASE(roundabout_intersection_connectivity) std::unordered_set traffic_lights; std::vector annotations; std::vector restrictions; - std::vector conditional_restrictions; CompressedEdgeContainer container; test::MockScriptingEnvironment scripting_environment; std::vector maneuver_overrides; @@ -214,7 +212,6 @@ BOOST_AUTO_TEST_CASE(roundabout_intersection_connectivity) traffic_lights, scripting_environment, restrictions, - conditional_restrictions, maneuver_overrides, graph, annotations, @@ -225,7 +222,9 @@ BOOST_AUTO_TEST_CASE(roundabout_intersection_connectivity) EdgeBasedNodeDataContainer node_data_container( std::vector(graph.GetNumberOfEdges()), annotations); - RestrictionMap restriction_map(restrictions, IndexNodeByFromAndVia()); + + RestrictionGraph restriction_graph = constructRestrictionGraph(restrictions); + RestrictionMap restriction_map(restriction_graph); const auto connectivity_matrix = [&](NodeID node) { std::vector result; @@ -263,7 +262,6 @@ BOOST_AUTO_TEST_CASE(skip_degree_two_nodes) std::unordered_set traffic_lights{2}; std::vector annotations(1); std::vector restrictions; - std::vector conditional_restrictions; CompressedEdgeContainer container; test::MockScriptingEnvironment scripting_environment; std::vector maneuver_overrides; @@ -306,7 +304,6 @@ BOOST_AUTO_TEST_CASE(skip_degree_two_nodes) traffic_lights, scripting_environment, restrictions, - conditional_restrictions, maneuver_overrides, graph, annotations, diff --git a/unit_tests/extractor/restriction_graph.cpp b/unit_tests/extractor/restriction_graph.cpp new file mode 100644 index 00000000000..66c6d5bf2f5 --- /dev/null +++ b/unit_tests/extractor/restriction_graph.cpp @@ -0,0 +1,597 @@ +#include "extractor/restriction_graph.hpp" +#include "extractor/maneuver_override.hpp" +#include "extractor/restriction.hpp" +#include "util/typedefs.hpp" + +#include +#include + +#include + +BOOST_AUTO_TEST_SUITE(restriction_graph) + +using namespace osrm; +using namespace osrm::extractor; + +TurnRestriction makeWayRestriction(NodeID from, std::vector via, NodeID to, bool is_only) +{ + WayRestriction wr{from, via, to}; + return TurnRestriction(wr, is_only); +} + +TurnRestriction makeNodeRestriction(NodeID from, NodeID via, NodeID to, bool is_only) +{ + NodeRestriction nr{from, via, to}; + return TurnRestriction(nr, is_only); +} + +struct instruction +{ + NodeID to; + bool is_only; +}; + +std::ostream &operator<<(std::ostream &os, const instruction &in) +{ + os << std::boolalpha << in.to << ':' << in.is_only; + return os; +} + +bool operator==(const instruction &lhs, const instruction &rhs) noexcept +{ + return lhs.to == rhs.to && lhs.is_only == rhs.is_only; +} + +bool operator!=(const instruction &lhs, const instruction &rhs) noexcept { return !(lhs == rhs); } + +void checkInstructions(RestrictionGraph::RestrictionRange restrictions, + std::vector expected_instructions) +{ + std::vector actual_instructions; + std::transform(restrictions.begin(), + restrictions.end(), + std::back_inserter(actual_instructions), + [](const auto &restriction) { + return instruction{restriction->To(), bool(restriction->is_only)}; + }); + std::sort(actual_instructions.begin(), + actual_instructions.end(), + [](const auto &lhs, const auto &rhs) { + return (lhs.to < rhs.to) || (lhs.to == rhs.to && lhs.is_only); + }); + std::sort(expected_instructions.begin(), + expected_instructions.end(), + [](const auto &lhs, const auto &rhs) { + return (lhs.to < rhs.to) || (lhs.to == rhs.to && lhs.is_only); + }); + + BOOST_REQUIRE_EQUAL_COLLECTIONS(actual_instructions.begin(), + actual_instructions.end(), + expected_instructions.begin(), + expected_instructions.end()); +} + +void checkEdges(RestrictionGraph::EdgeRange edges, std::vector expected_edges) +{ + std::vector actual_edges; + std::transform(edges.begin(), + edges.end(), + std::back_inserter(actual_edges), + [&](const auto &edge) { return edge.node_based_to; }); + std::sort(actual_edges.begin(), actual_edges.end(), std::less()); + std::sort(expected_edges.begin(), expected_edges.end(), std::less()); + + BOOST_REQUIRE_EQUAL_COLLECTIONS( + actual_edges.begin(), actual_edges.end(), expected_edges.begin(), expected_edges.end()); +} + +std::map nextEdges(RestrictionGraph::EdgeRange edges) +{ + std::map res; + std::transform( + edges.begin(), edges.end(), std::inserter(res, res.end()), [&](const auto &edge) { + return std::make_pair(edge.node_based_to, edge.target); + }); + return res; +} + +std::map checkNode(const RestrictionGraph &graph, + const RestrictionID node_id, + std::vector expected_instructions, + std::vector expected_edges) +{ + checkInstructions(graph.GetRestrictions(node_id), expected_instructions); + checkEdges(graph.GetEdges(node_id), expected_edges); + return nextEdges(graph.GetEdges(node_id)); +} + +std::map +validateStartRestrictionNode(RestrictionGraph &graph, + NodeID from, + NodeID to, + std::vector restriction_instructions, + std::vector restriction_edges) +{ + BOOST_REQUIRE_EQUAL(graph.start_edge_to_node.count({from, to}), 1); + const auto node_id = graph.start_edge_to_node[{from, to}]; + BOOST_REQUIRE_GE(node_id, graph.num_via_nodes); + BOOST_REQUIRE_LT(node_id, graph.nodes.size()); + + return checkNode(graph, node_id, restriction_instructions, restriction_edges); +} + +std::map +validateViaRestrictionNode(RestrictionGraph &graph, + size_t via_node_idx, + NodeID from, + NodeID to, + std::vector restriction_instructions, + std::vector restriction_edges) +{ + BOOST_REQUIRE_GE(graph.via_edge_to_node.count({from, to}), 1); + auto node_match_it = graph.via_edge_to_node.equal_range({from, to}); + + BOOST_REQUIRE_MESSAGE( + std::any_of(node_match_it.first, + node_match_it.second, + [&](const auto node_match) { return node_match.second == via_node_idx; }), + "Could not find expected via_node_idx " << via_node_idx << " for graph edge " << from << "," + << to); + + BOOST_REQUIRE_LT(via_node_idx, graph.num_via_nodes); + return checkNode(graph, via_node_idx, restriction_instructions, restriction_edges); +} + +BOOST_AUTO_TEST_CASE(empty_restrictions) +{ + // + // Input + // + // Output + // + std::vector empty; + + auto graph = constructRestrictionGraph(empty); + + BOOST_CHECK(graph.nodes.empty()); + BOOST_CHECK_EQUAL(graph.num_via_nodes, 0); + BOOST_CHECK(graph.edges.empty()); + BOOST_CHECK(graph.start_edge_to_node.empty()); + BOOST_CHECK(graph.via_edge_to_node.empty()); +} + +BOOST_AUTO_TEST_CASE(node_restriction) +{ + // + // Input + // 0 -> 1: only_2 + // + // Output + // s(0,1,[only_2]) + // + std::vector input{ + makeNodeRestriction(0, 1, 2, true), + }; + + std::cout << "Construct graph" << std::endl; + auto graph = constructRestrictionGraph(input); + std::cout << "Constructed graph" << std::endl; + + BOOST_CHECK_EQUAL(graph.nodes.size(), 1); + BOOST_CHECK_EQUAL(graph.num_via_nodes, 0); + BOOST_CHECK_EQUAL(graph.start_edge_to_node.size(), 1); + BOOST_CHECK_EQUAL(graph.via_edge_to_node.size(), 0); + BOOST_CHECK_EQUAL(graph.edges.size(), 0); + + validateStartRestrictionNode(graph, 0, 1, {{2, true}}, {}); +} + +BOOST_AUTO_TEST_CASE(way_restriction) +{ + // + // Input + // 0 -> 1 -> 2 -> 3: only_4 + // + // Output + // s(0,1) -> v(1,2) -> v(2,3,[only_4]) + // + std::vector input{ + makeWayRestriction(0, + { + 1, + 2, + 3, + }, + 4, + true), + }; + + auto graph = constructRestrictionGraph(input); + + BOOST_CHECK_EQUAL(graph.nodes.size(), 3); + BOOST_CHECK_EQUAL(graph.num_via_nodes, 2); + BOOST_CHECK_EQUAL(graph.start_edge_to_node.size(), 1); + BOOST_CHECK_EQUAL(graph.via_edge_to_node.size(), 2); + BOOST_CHECK_EQUAL(graph.edges.size(), 2); + + auto start_edges = validateStartRestrictionNode(graph, 0, 1, {}, {2}); + auto via_edges = validateViaRestrictionNode(graph, start_edges[2], 1, 2, {}, {3}); + validateViaRestrictionNode(graph, via_edges[3], 2, 3, {{4, true}}, {}); +} + +BOOST_AUTO_TEST_CASE(disconnected_restrictions) +{ + // + // Input + // 0 -> 1 -> 2 -> 3: only_4 + // 5 -> 6 -> 7 -> 8: only_9 + // + // Output + // s(0,1) -> v(1,2) -> v(2,3,[only_4]) + // s(5,6) -> v(6,7) -> v(7,8,[only_9]) + // + std::vector input{ + makeWayRestriction(0, + { + 1, + 2, + 3, + }, + 4, + true), + makeWayRestriction(5, + { + 6, + 7, + 8, + }, + 9, + true), + }; + + auto graph = constructRestrictionGraph(input); + + BOOST_CHECK_EQUAL(graph.nodes.size(), 6); + BOOST_CHECK_EQUAL(graph.num_via_nodes, 4); + BOOST_CHECK_EQUAL(graph.edges.size(), 4); + BOOST_CHECK_EQUAL(graph.start_edge_to_node.size(), 2); + BOOST_CHECK_EQUAL(graph.via_edge_to_node.size(), 4); + + auto start_edges_1 = validateStartRestrictionNode(graph, 0, 1, {}, {2}); + auto start_edges_2 = validateStartRestrictionNode(graph, 5, 6, {}, {7}); + + auto via_edges_1 = validateViaRestrictionNode(graph, start_edges_1[2], 1, 2, {}, {3}); + validateViaRestrictionNode(graph, via_edges_1[3], 2, 3, {{4, true}}, {}); + auto via_edges_2 = validateViaRestrictionNode(graph, start_edges_2[7], 6, 7, {}, {8}); + validateViaRestrictionNode(graph, via_edges_2[8], 7, 8, {{9, true}}, {}); +} + +BOOST_AUTO_TEST_CASE(same_prefix_restrictions) +{ + // + // Input + // 0 -> 1 -> 2 -> 3: only_4 + // 0 -> 1 -> 2 -> 5: only_6 + // + // Output + // s(0,1) -> v(1,2) -> v(2,3,[only_4]) + // \_>v(2,5,[only_6]) + // + std::vector input{ + makeWayRestriction(0, + { + 1, + 2, + 3, + }, + 4, + true), + makeWayRestriction(0, + { + 1, + 2, + 5, + }, + 6, + true), + }; + + auto graph = constructRestrictionGraph(input); + + BOOST_CHECK_EQUAL(graph.nodes.size(), 4); + BOOST_CHECK_EQUAL(graph.num_via_nodes, 3); + BOOST_CHECK_EQUAL(graph.edges.size(), 3); + BOOST_CHECK_EQUAL(graph.start_edge_to_node.size(), 1); + BOOST_CHECK_EQUAL(graph.via_edge_to_node.size(), 3); + + auto start_edges = validateStartRestrictionNode(graph, 0, 1, {}, {2}); + + auto via_edges = validateViaRestrictionNode(graph, start_edges[2], 1, 2, {}, {3, 5}); + validateViaRestrictionNode(graph, via_edges[3], 2, 3, {{4, true}}, {}); + validateViaRestrictionNode(graph, via_edges[5], 2, 5, {{6, true}}, {}); +} + +BOOST_AUTO_TEST_CASE(duplicate_edges) +{ + // + // Input + // 0 -> 1 -> 2 -> 3: only_4 + // 5 -> 1 -> 2 -> 3: only_6 + // + // Output + // s(0,1) -> v(1,2) -> v(2,3,[only_4]) + // s(5,1) -> v(1,2) -> v(2,3,[only_6]) + // + std::vector input{ + makeWayRestriction(0, + { + 1, + 2, + 3, + }, + 4, + true), + makeWayRestriction(5, + { + 1, + 2, + 3, + }, + 6, + true), + }; + + auto graph = constructRestrictionGraph(input); + + BOOST_CHECK_EQUAL(graph.nodes.size(), 6); + BOOST_CHECK_EQUAL(graph.num_via_nodes, 4); + BOOST_CHECK_EQUAL(graph.edges.size(), 4); + BOOST_CHECK_EQUAL(graph.start_edge_to_node.size(), 2); + BOOST_CHECK_EQUAL(graph.via_edge_to_node.size(), 4); + BOOST_CHECK_EQUAL(graph.via_edge_to_node.count({1, 2}), 2); + BOOST_CHECK_EQUAL(graph.via_edge_to_node.count({2, 3}), 2); + + auto start_edges_1 = validateStartRestrictionNode(graph, 0, 1, {}, {2}); + auto start_edges_2 = validateStartRestrictionNode(graph, 5, 1, {}, {2}); + + auto via_edges_1 = validateViaRestrictionNode(graph, start_edges_1[2], 1, 2, {}, {3}); + validateViaRestrictionNode(graph, via_edges_1[3], 2, 3, {{4, true}}, {}); + + auto via_edges_2 = validateViaRestrictionNode(graph, start_edges_2[2], 1, 2, {}, {3}); + validateViaRestrictionNode(graph, via_edges_2[3], 2, 3, {{6, true}}, {}); +} + +BOOST_AUTO_TEST_CASE(nested_restriction) +{ + // + // Input + // 0 -> 1 -> 2 -> 3: [only_4] + // 1 -> 2: [only_4] + // + // Output + // s(0,1) -> v(1,2,[only_4]) -> v(2,3,[only_4]) + // s(1,2,[only_4]) + // + std::vector input{ + makeWayRestriction(0, + { + 1, + 2, + 3, + }, + 4, + true), + makeNodeRestriction(1, 2, 4, true), + }; + + auto graph = constructRestrictionGraph(input); + + BOOST_CHECK_EQUAL(graph.nodes.size(), 4); + BOOST_CHECK_EQUAL(graph.num_via_nodes, 2); + BOOST_CHECK_EQUAL(graph.edges.size(), 2); + BOOST_CHECK_EQUAL(graph.start_edge_to_node.size(), 2); + BOOST_CHECK_EQUAL(graph.via_edge_to_node.size(), 2); + + auto start_edges_1 = validateStartRestrictionNode(graph, 0, 1, {}, {2}); + validateStartRestrictionNode(graph, 1, 2, {{4, true}}, {}); + + auto via_edges_1 = validateViaRestrictionNode(graph, start_edges_1[2], 1, 2, {{4, true}}, {3}); + validateViaRestrictionNode(graph, via_edges_1[3], 2, 3, {{4, true}}, {}); +} + +BOOST_AUTO_TEST_CASE(partially_nested_restriction) +{ + // + // Input + // 0 -> 1 -> 2 -> 3: only_4 + // 2 -> 3 -> 4: only_5 + // + // Output + // s(0,1) -> v(1,2) -> v(2,3,[only_4]) + // | + // t + // | + // s(2,3) -> v(3,4,[only_5]) + // + std::vector input{ + makeWayRestriction(0, + { + 1, + 2, + 3, + }, + 4, + true), + makeWayRestriction(2, {3, 4}, 5, true), + }; + + auto graph = constructRestrictionGraph(input); + + BOOST_CHECK_EQUAL(graph.nodes.size(), 5); + BOOST_CHECK_EQUAL(graph.num_via_nodes, 3); + BOOST_CHECK_EQUAL(graph.edges.size(), 4); + BOOST_CHECK_EQUAL(graph.start_edge_to_node.size(), 2); + BOOST_CHECK_EQUAL(graph.via_edge_to_node.size(), 3); + + auto start_edges_1 = validateStartRestrictionNode(graph, 0, 1, {}, {2}); + auto start_edges_2 = validateStartRestrictionNode(graph, 2, 3, {}, {4}); + + auto via_edges_1 = validateViaRestrictionNode(graph, start_edges_1[2], 1, 2, {}, {3}); + validateViaRestrictionNode(graph, via_edges_1[3], 2, 3, {{4, true}}, {4}); + validateViaRestrictionNode(graph, start_edges_2[4], 3, 4, {{5, true}}, {}); +} + +BOOST_AUTO_TEST_CASE(conflicting_nested_restriction) +{ + // + // Input + // 0 -> 1 -> 2 -> 3: no_4 + // 2 -> 3 -> 4: only_5 + // + // Output + // s(0,1) -> v(1,2) -> v(2,3,[no_4]) + // s(2,3) -> v(3,4,[only_5]) + // + std::vector input{ + makeWayRestriction(0, + { + 1, + 2, + 3, + }, + 4, + false), + makeWayRestriction(2, {3, 4}, 5, true), + }; + + auto graph = constructRestrictionGraph(input); + + BOOST_CHECK_EQUAL(graph.nodes.size(), 5); + BOOST_CHECK_EQUAL(graph.num_via_nodes, 3); + BOOST_CHECK_EQUAL(graph.edges.size(), 3); + BOOST_CHECK_EQUAL(graph.start_edge_to_node.size(), 2); + BOOST_CHECK_EQUAL(graph.via_edge_to_node.size(), 3); + + auto start_edges_1 = validateStartRestrictionNode(graph, 0, 1, {}, {2}); + auto start_edges_2 = validateStartRestrictionNode(graph, 2, 3, {}, {4}); + + auto via_edges_1 = validateViaRestrictionNode(graph, start_edges_1[2], 1, 2, {}, {3}); + validateViaRestrictionNode(graph, via_edges_1[3], 2, 3, {{4, false}}, {}); + validateViaRestrictionNode(graph, start_edges_2[4], 3, 4, {{5, true}}, {}); +} + +BOOST_AUTO_TEST_CASE(restriction_edge_matches_start) +{ + // + // Input + // 0 -> 1 -> 2 -> 3: only_4 + // 3 -> 4 -> 5: only_6 + // + // Output + // s(0,1) -> v(1,2) -> v(2,3,[only_4]) + // s(3,4) -> v(4,5,[only_6]) + // + std::vector input{ + makeWayRestriction(0, + { + 1, + 2, + 3, + }, + 4, + true), + makeWayRestriction(3, {4, 5}, 6, true), + }; + + auto graph = constructRestrictionGraph(input); + + BOOST_CHECK_EQUAL(graph.nodes.size(), 5); + BOOST_CHECK_EQUAL(graph.num_via_nodes, 3); + BOOST_CHECK_EQUAL(graph.edges.size(), 3); + BOOST_CHECK_EQUAL(graph.start_edge_to_node.size(), 2); + BOOST_CHECK_EQUAL(graph.via_edge_to_node.size(), 3); + + auto start_edges_1 = validateStartRestrictionNode(graph, 0, 1, {}, {2}); + auto start_edges_2 = validateStartRestrictionNode(graph, 3, 4, {}, {5}); + + auto via_edges_1 = validateViaRestrictionNode(graph, start_edges_1[2], 1, 2, {}, {3}); + validateViaRestrictionNode(graph, via_edges_1[3], 2, 3, {{4, true}}, {}); + validateViaRestrictionNode(graph, start_edges_2[5], 4, 5, {{6, true}}, {}); +} + +BOOST_AUTO_TEST_CASE(self_nested_restriction) +{ + // + // Input + // 0 -> 1 -> 0 -> 1 -> 2: only_3 + // + // Output + // s(0,1) -> v(1,0) -> v(0,1) -> v(1,2,[only_3]) + // \___t___/ + // + std::vector input{ + makeWayRestriction(0, + { + 1, + 0, + 1, + 2, + }, + 3, + true), + }; + + auto graph = constructRestrictionGraph(input); + + BOOST_CHECK_EQUAL(graph.nodes.size(), 4); + BOOST_CHECK_EQUAL(graph.num_via_nodes, 3); + BOOST_CHECK_EQUAL(graph.edges.size(), 4); + BOOST_CHECK_EQUAL(graph.start_edge_to_node.size(), 1); + BOOST_CHECK_EQUAL(graph.via_edge_to_node.size(), 3); + + auto start_edges_1 = validateStartRestrictionNode(graph, 0, 1, {}, {0}); + + auto via_edges_1 = validateViaRestrictionNode(graph, start_edges_1[0], 1, 0, {}, {1}); + auto via_edges_2 = validateViaRestrictionNode(graph, via_edges_1[1], 0, 1, {}, {0, 2}); + validateViaRestrictionNode(graph, via_edges_2[2], 1, 2, {{3, true}}, {}); +} + +BOOST_AUTO_TEST_CASE(single_node_restriction) +{ + // + // Input + // 0 -> 0 -> 0 -> 0 -> 0: only_0 + // + // Output + // s(0,0) -> v(0,0) -> v(0,0) -> v(0,0,[only_0]) + // \t/ + // + std::vector input{ + makeWayRestriction(0, + { + 0, + 0, + 0, + 0, + }, + 0, + true), + }; + + auto graph = constructRestrictionGraph(input); + + BOOST_CHECK_EQUAL(graph.nodes.size(), 4); + BOOST_CHECK_EQUAL(graph.num_via_nodes, 3); + BOOST_CHECK_EQUAL(graph.edges.size(), 4); + BOOST_CHECK_EQUAL(graph.start_edge_to_node.size(), 1); + BOOST_CHECK_EQUAL(graph.via_edge_to_node.size(), 3); + BOOST_CHECK_EQUAL(graph.via_edge_to_node.count({0, 0}), 3); + + auto start_edges_1 = validateStartRestrictionNode(graph, 0, 0, {}, {0}); + + auto via_edges_1 = validateViaRestrictionNode(graph, start_edges_1[0], 0, 0, {}, {0}); + auto via_edges_2 = validateViaRestrictionNode(graph, via_edges_1[0], 0, 0, {}, {0}); + validateViaRestrictionNode(graph, via_edges_2[0], 0, 0, {{0, true}}, {0}); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/unit_tests/mocks/mock_scripting_environment.hpp b/unit_tests/mocks/mock_scripting_environment.hpp index 8531176ad2f..a34a03cc064 100644 --- a/unit_tests/mocks/mock_scripting_environment.hpp +++ b/unit_tests/mocks/mock_scripting_environment.hpp @@ -42,7 +42,7 @@ class MockScriptingEnvironment : public extractor::ScriptingEnvironment const extractor::ExtractionRelationContainer &, std::vector> &, std::vector> &, - std::vector &, + std::vector &, std::vector &) override final { }