From d96960f9cc59433a5108784939f84d7d4a087c55 Mon Sep 17 00:00:00 2001 From: Michael Bell Date: Sun, 17 Jan 2021 19:11:16 +0000 Subject: [PATCH] Support snapping to multiple ways at an input location This PR improves routing results by adding support for snapping to multiple ways at input locations. This means all edges at the snapped location can act as source/target candidates for routing search, ensuring we always find the best route, and not the one dependent on the edge selected. --- CHANGELOG.md | 1 + features/bicycle/alley.feature | 6 +- features/car/destination.feature | 31 +- features/car/restrictions.feature | 4 +- .../guidance/merge-segregated-roads.feature | 15 +- features/testbot/bearing.feature | 12 +- features/testbot/distance_matrix.feature | 17 +- features/testbot/duration_matrix.feature | 10 +- features/testbot/snap_intersection.feature | 629 ++++++++++++ .../testbot/traffic_turn_penalties.feature | 4 +- features/testbot/weight.feature | 2 +- include/engine/api/base_api.hpp | 120 ++- include/engine/api/base_parameters.hpp | 4 +- include/engine/api/match_api.hpp | 6 +- include/engine/api/nearest_api.hpp | 4 +- include/engine/api/route_api.hpp | 40 +- include/engine/api/table_api.hpp | 89 +- include/engine/api/trip_api.hpp | 26 +- .../contiguous_internalmem_datafacade.hpp | 116 +-- include/engine/datafacade/datafacade_base.hpp | 61 +- include/engine/geospatial_query.hpp | 547 ++++------- include/engine/hint.hpp | 34 +- include/engine/internal_route_result.hpp | 23 +- include/engine/phantom_node.hpp | 40 +- include/engine/plugins/plugin_base.hpp | 297 +++--- include/engine/plugins/trip.hpp | 2 +- include/engine/routing_algorithms.hpp | 54 +- .../routing_algorithms/alternative_path.hpp | 4 +- .../direct_shortest_path.hpp | 2 +- .../routing_algorithms/many_to_many.hpp | 2 +- .../routing_algorithms/routing_base.hpp | 181 ++-- .../routing_algorithms/routing_base_ch.hpp | 46 +- .../routing_algorithms/routing_base_mld.hpp | 196 ++-- .../routing_algorithms/shortest_path.hpp | 9 +- .../routing_algorithms/shortest_path_impl.hpp | 924 ++++++++++++------ .../server/api/base_parameters_grammar.hpp | 22 +- include/util/static_rtree.hpp | 32 +- src/engine/api/json_factory.cpp | 4 +- src/engine/hint.cpp | 85 +- src/engine/plugins/match.cpp | 29 +- src/engine/plugins/nearest.cpp | 3 - src/engine/plugins/table.cpp | 16 +- src/engine/plugins/tile.cpp | 1 - src/engine/plugins/trip.cpp | 47 +- src/engine/plugins/viaroute.cpp | 55 +- .../alternative_path_ch.cpp | 76 +- .../alternative_path_mld.cpp | 41 +- .../direct_shortest_path.cpp | 24 +- .../routing_algorithms/many_to_many_ch.cpp | 24 +- .../routing_algorithms/many_to_many_mld.cpp | 193 ++-- .../routing_algorithms/routing_base.cpp | 102 +- .../routing_algorithms/routing_base_ch.cpp | 22 +- .../routing_algorithms/shortest_path.cpp | 4 +- unit_tests/engine/base64.cpp | 16 +- .../engine/collapse_internal_route_result.cpp | 44 +- unit_tests/engine/offline_facade.cpp | 126 +-- unit_tests/mocks/mock_datafacade.hpp | 94 +- unit_tests/server/parameters_parser.cpp | 59 +- unit_tests/util/static_rtree.cpp | 63 +- 59 files changed, 2798 insertions(+), 1942 deletions(-) create mode 100644 features/testbot/snap_intersection.feature diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ad8b5eef10..da90522355b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,7 @@ - FIXED: Completed support for no_entry and no_exit turn restrictions. [#5988](https://github.com/Project-OSRM/osrm-backend/pull/5988) - ADDED: Add support for non-round-trips with a single fixed endpoint. [#6050](https://github.com/Project-OSRM/osrm-backend/pull/6050) - FIXED: Improvements to maneuver override processing [#6125](https://github.com/Project-OSRM/osrm-backend/pull/6125) + - ADDED: Support snapping to multiple ways at an input location. [#5953](https://github.com/Project-OSRM/osrm-backend/pull/5953) # 5.26.0 - Changes from 5.25.0 diff --git a/features/bicycle/alley.feature b/features/bicycle/alley.feature index 74a87b98da6..db7b624a7ea 100644 --- a/features/bicycle/alley.feature +++ b/features/bicycle/alley.feature @@ -28,7 +28,7 @@ Feature: Bicycle - Route around alleys When I route I should get | from | to | a:nodes | weight | # | - | a | f | 1:2:3:6 | 200.4 | Avoids d,e,f | - | a | e | 1:2:5 | 176.4 | Take the alley b,e if neccessary | - | d | f | 4:1:2:3:6 | 252.6 | Avoids the alley d,e,f | + | a | f | 1:2:3:6 | 196.2 | Avoids d,e,f | + | a | e | 1:2:5 | 172.2 | Take the alley b,e if neccessary | + | d | f | 4:1:2:3:6 | 248.4 | Avoids the alley d,e,f | diff --git a/features/car/destination.feature b/features/car/destination.feature index 43e12386cbc..d5d3c128712 100644 --- a/features/car/destination.feature +++ b/features/car/destination.feature @@ -7,10 +7,10 @@ Feature: Car - Destination only, no passing through Scenario: Car - Destination only street Given the node map """ - a e - b c d + a e + b1 c 2d - x y + x y """ And the ways @@ -23,21 +23,21 @@ Feature: Car - Destination only, no passing through When I route I should get | from | to | route | | a | b | ab,ab | - | a | c | ab,bcd | - | a | d | ab,bcd,bcd | + | a | c | ab,bcd,bcd | + | a | 2 | ab,bcd,bcd | | a | e | axye,axye | | e | d | de,de | - | e | c | de,bcd | - | e | b | de,bcd,bcd | + | e | c | de,bcd,bcd | + | e | 1 | de,bcd,bcd | | e | a | axye,axye | Scenario: Car - Destination only street Given the node map """ - a e - b c d + a e + b1 c 2d - x y + x y """ And the ways @@ -51,12 +51,12 @@ Feature: Car - Destination only, no passing through When I route I should get | from | to | route | | a | b | ab,ab | - | a | c | ab,bc | - | a | d | ab,cd | + | a | c | ab,bc,bc | + | a | 2 | ab,bc,cd | | a | e | axye,axye | | e | d | de,de | - | e | c | de,cd | - | e | b | de,bc | + | e | c | de,cd,cd | + | e | 1 | de,cd,bc | | e | a | axye,axye | Scenario: Car - Routing inside a destination only area @@ -117,6 +117,7 @@ Feature: Car - Destination only, no passing through + \ + | d | + 1 | \___e """ @@ -129,7 +130,7 @@ Feature: Car - Destination only, no passing through When I route I should get | from | to | route | | e | a | acbe,acbe | - | d | a | de,acbe,acbe | + | 1 | a | de,acbe,acbe | | c | d | cd,cd | Scenario: Car - Routing through a parking lot tagged access=destination,service diff --git a/features/car/restrictions.feature b/features/car/restrictions.feature index b9c878b0025..61416b96be1 100644 --- a/features/car/restrictions.feature +++ b/features/car/restrictions.feature @@ -411,7 +411,7 @@ Feature: Car - Turn restrictions y i j f b x a e g h - c d + c1 d """ And the ways @@ -438,7 +438,7 @@ Feature: Car - Turn restrictions When I route I should get | from | to | route | | e | f | ae,xa,bx,fb,fb | - | c | f | dc,da,ae,ge,hg,hg,ge,ae,xa,bx,fb,fb | + | 1 | f | dc,da,ae,ge,hg,hg,ge,ae,xa,bx,fb,fb | | d | f | da,ae,ge,hg,hg,ge,ae,xa,bx,fb,fb | @except diff --git a/features/guidance/merge-segregated-roads.feature b/features/guidance/merge-segregated-roads.feature index c802d4ade4a..bb85673f7e8 100644 --- a/features/guidance/merge-segregated-roads.feature +++ b/features/guidance/merge-segregated-roads.feature @@ -332,10 +332,11 @@ Feature: Merge Segregated Roads | .b. c h + 1 | + | 4 | | - | | - 1 2 - | | + 2 | + | 3 d g 'e' | @@ -354,13 +355,13 @@ Feature: Merge Segregated Roads | hb | road | yes | When I route I should get - | waypoints | turns | route | intersections | + | waypoints | turns | route | intersections | | a,f | depart,arrive | road,road | true:180,false:0 true:180,false:0 true:180;true:0 | - | c,f | depart,arrive | bridge,road | true:180,false:0 true:180;true:0 | | 1,f | depart,arrive | bridge,road | true:180,false:0 true:180;true:0 | + | 2,f | depart,arrive | bridge,road | true:180,false:0 true:180;true:0 | | f,a | depart,arrive | road,road | true:0,true:0 false:180,true:0 false:180;true:180 | - | g,a | depart,arrive | bridge,road | true:0,true:0 false:180;true:180 | - | 2,a | depart,arrive | bridge,road | true:0,true:0 false:180;true:180 | + | 3,a | depart,arrive | bridge,road | true:0,true:0 false:180;true:180 | + | 4,a | depart,arrive | bridge,road | true:0,true:0 false:180;true:180 | @negative Scenario: Traffic Circle diff --git a/features/testbot/bearing.feature b/features/testbot/bearing.feature index d74f4bafd8e..0ae483ca4f2 100644 --- a/features/testbot/bearing.feature +++ b/features/testbot/bearing.feature @@ -67,10 +67,10 @@ Feature: Compass bearing Scenario: Bearing in a roundabout Given the node map """ - k d c j - e b - f a - l g h i + k d 1c j + e b + f a + l g2 h i """ And the ways @@ -94,8 +94,8 @@ Feature: Compass bearing When I route I should get | from | to | route | bearing | - | c | b | cd,de,ef,fg,gh,ha,ab,ab | 0->270,270->225,225->180,180->135,135->90,90->45,45->0,0->0 | - | g | f | gh,ha,ab,bc,cd,de,ef,ef | 0->90,90->45,45->0,0->315,315->270,270->225,225->180,180->0 | + | 1 | b | cd,de,ef,fg,gh,ha,ab,ab | 0->270,270->225,225->180,180->135,135->90,90->45,45->0,0->0 | + | 2 | f | gh,ha,ab,bc,cd,de,ef,ef | 0->90,90->45,45->0,0->315,315->270,270->225,225->180,180->0 | Scenario: Bearing should stay constant when zig-zagging Given the node map diff --git a/features/testbot/distance_matrix.feature b/features/testbot/distance_matrix.feature index 92f4b8a6a2b..5d1ea444ac4 100644 --- a/features/testbot/distance_matrix.feature +++ b/features/testbot/distance_matrix.feature @@ -589,12 +589,12 @@ Feature: Basic Distance Matrix When I request a travel distance matrix I should get | | a | b | c | d | e | f | - | a | 0 | 100 | 300 | 650 | 1934.5 | 1534.6 | - | b | 760.6 | 0 | 200 | 550.1 | 1834.6 | 1434.6 | - | c | 560.6 | 660.5 | 0 | 350 | 1634.6 | 1234.6 | - | d | 1484.6 | 1584.5| 1784.5 | 0 | 1284.5 | 884.6 | - | e | 200 | 300 | 500 | 710.6 | 0 | 1595.2 | - | f | 600 | 699.9 | 899.9 | 1110.5 | 399.9 | 0 | + | a | 0 | 100 | 300 | 650 | 660.5 | 1534.6 | + | b | 760.6 | 0 | 200 | 550.1 | 560.6 | 1434.6 | + | c | 560.6 | 660.5 | 0 | 350 | 360.5 | 1234.6 | + | d | 1484.6 | 1584.5| 1645.1 | 0 | 1284.5 | 884.6 | + | e | 200 | 300 | 360.5 | 710.6 | 0 | 1595.2 | + | f | 600 | 699.9 | 760.5 | 884.6 | 399.9 | 0 | Scenario: Testbot - Filling in noroutes with estimates (defaults to input coordinate location) @@ -720,11 +720,10 @@ Feature: Basic Distance Matrix | 1 | 2 | abcdef,fghij,fghij | 1000.1m | | 1 | 3 | abcdef,fghij,fghij | 1400.1m | | 2 | 3 | fghij,fghij | 400m | - + When I request a travel distance matrix I should get - | | 1 | 2 | 3 | + | | 1 | 2 | 3 | | 1 | 0 | 1000.1 | 1400.1 | | 2 | 1000.1 | 0 | 400 | | 3 | 1400.1 | 400 | 0 | - diff --git a/features/testbot/duration_matrix.feature b/features/testbot/duration_matrix.feature index 28ffaaed39f..4c6e1510d64 100644 --- a/features/testbot/duration_matrix.feature +++ b/features/testbot/duration_matrix.feature @@ -491,18 +491,18 @@ Feature: Basic Duration Matrix When I route I should get | from | to | route | distance | time | weight | - | a | c | ac,ac | 200m | 5s | 5 | + | a | c | ac,ac | 200m | 1s | 1 | When I request a travel time matrix I should get - | | a | b | c | d | - | a | 0 | 1 | 5 | 10 | + | | a | b | c | d | + | a | 0 | 1 | 1 | 6 | When I request a travel time matrix I should get | | a | | a | 0 | | b | 1 | - | c | 15 | - | d | 10 | + | c | 1 | + | d | 6 | Scenario: Testbot - OneToMany vs ManyToOne Given the node map diff --git a/features/testbot/snap_intersection.feature b/features/testbot/snap_intersection.feature new file mode 100644 index 00000000000..063986bab12 --- /dev/null +++ b/features/testbot/snap_intersection.feature @@ -0,0 +1,629 @@ +Feature: Snapping at intersections + + Background: + # Use turnbot so that we can validate when we are + # snapping to one of many potential candidate ways + Given the profile "turnbot" + + # https://github.com/Project-OSRM/osrm-backend/issues/4465 + Scenario: Snapping source to intersection with one-way roads + Given the node map + """ + a e c + \ | / + d + + 1 + """ + + And the ways + | nodes | oneway | + | da | yes | + | dc | yes | + | de | yes | + + + When I route I should get + | from | to | route | time | + | 1 | e | de,de | 20s | + | 1 | a | da,da | 28.3s | + | 1 | c | dc,dc | 28.3s | + + When I request a travel time matrix I should get + | | a | c | e | + | 1 | 28.3 | 28.3 | 20 | + + + Scenario: Snapping destination to intersection with one-way roads + Given the node map + """ + a e c + \ | / + d + + 1 + """ + + And the ways + | nodes | oneway | + | da | -1 | + | dc | -1 | + | de | -1 | + + + When I route I should get + | from | to | route | time | + | e | 1 | de,de | 20s | + | a | 1 | da,da | 28.3s | + | c | 1 | dc,dc | 28.3s | + + When I request a travel time matrix I should get + | | 1 | + | a | 28.3 | + | c | 28.3 | + | e | 20 | + + + Scenario: Snapping to intersection with bi-directional roads + Given the node map + """ + a e + | / + d---c + + 1 + """ + + And the ways + | nodes | + | ad | + | ed | + | dc | + + When I route I should get + | from | to | route | time | weight | + | 1 | c | dc,dc | 20s | 20 | + | 1 | a | ad,ad | 20s | 20 | + | 1 | e | ed,ed | 28.3s | 28.3 | + | c | 1 | dc,dc | 20s | 20 | + | a | 1 | ad,ad | 20s | 20 | + | e | 1 | ed,ed | 28.3s | 28.3 | + + When I request a travel time matrix I should get + | | a | c | e | + | 1 | 20 | 20 | 28.3 | + + When I request a travel time matrix I should get + | | 1 | + | a | 20 | + | c | 20 | + | e | 28.3 | + + + Scenario: Snapping at compressible node + Given the node map + """ + a---b---c + """ + + And the ways + | nodes | + | abc | + + When I route I should get + | from | to | route | time | weight | + | b | c | abc,abc | 20s | 20 | + | b | a | abc,abc | 20s | 20 | + | a | b | abc,abc | 20s | 20 | + | c | b | abc,abc | 20s | 20 | + + + Scenario: Snapping at compressible node with traffic lights + Given the node map + """ + a---b---c + """ + + And the ways + | nodes | + | abc | + + # Turnbot will use the turn penalty instead of traffic penalty. + # We do this to induce a penalty between two edges of the same + # segment. + And the nodes + | node | highway | + | b | traffic_signals | + + # Snaps to first edge in forward direction + When I route I should get + | from | to | route | time | weight | + | b | c | abc,abc | 40s | 40 | + | b | a | abc,abc | 20s | 20 | + | a | b | abc,abc | 20s | 20 | + | c | b | abc,abc | 40s | 40 | + + + Scenario: Snapping at compressible node traffic lights, one-way + Given the node map + """ + a-->b-->c + """ + + And the ways + | nodes | oneway | + | abc | yes | + + # Turnbot will use the turn penalty instead of traffic penalty. + # We do this to induce a penalty between two edges of the same + # segment. + And the nodes + | node | highway | + | b | traffic_signals | + + + # Snaps to first edge in forward direction + When I route I should get + | from | to | route | time | weight | + | b | c | abc,abc | 40s | 40 | + | a | b | abc,abc | 20s | 20 | + + When I route I should get + | from | to | code | + | b | a | NoRoute | + | c | b | NoRoute | + + + Scenario: Snapping at compressible node traffic lights, reverse one-way + Given the node map + """ + a<--b<--c + """ + + And the ways + | nodes | oneway | + | abc | -1 | + + # Turnbot will use the turn penalty instead of traffic penalty. + # We do this to induce a penalty between two edges of the same + # segment. + And the nodes + | node | highway | + | b | traffic_signals | + + + # Snaps to first edge in forward direction - as this is one-way, + # the forward direction has changed. + When I route I should get + | from | to | route | time | weight | + | b | a | abc,abc | 40s | 40 | + | c | b | abc,abc | 20s | 20 | + + When I route I should get + | from | to | code | + | b | c | NoRoute | + | a | b | NoRoute | + + + Scenario: Snapping at traffic lights, reverse disabled + Given the node map + """ + a-->b-->c + """ + + And the ways + | nodes | + | abc | + + And the contract extra arguments "--segment-speed-file {speeds_file}" + And the customize extra arguments "--segment-speed-file {speeds_file}" + And the speed file + """ + 2,1,0 + 3,2,0 + """ + + # Turnbot will use the turn penalty instead of traffic penalty. + # We do this to induce a penalty between two edges of the same + # segment. + And the nodes + | node | highway | + | b | traffic_signals | + + # Snaps to first edge in forward direction. + When I route I should get + | from | to | route | time | weight | + | b | c | abc,abc | 40s | 40 | + | a | b | abc,abc | 20s | 20 | + + When I route I should get + | from | to | code | + | b | a | NoRoute | + | c | b | NoRoute | + + + Scenario: Snapping at traffic lights, forward disabled + Given the node map + """ + a<--b<--c + """ + + And the ways + | nodes | + | abc | + + And the contract extra arguments "--segment-speed-file {speeds_file}" + And the customize extra arguments "--segment-speed-file {speeds_file}" + And the speed file + """ + 1,2,0 + 2,3,0 + """ + + # Turnbot will use the turn penalty instead of traffic penalty. + # We do this to induce a penalty between two edges of the same + # segment. + And the nodes + | node | highway | + | b | traffic_signals | + + # Forward direction is disabled, still snaps to first edge in forward direction + When I route I should get + | from | to | route | time | weight | + | b | a | abc,abc | 20s | 20 | + | c | b | abc,abc | 40s | 40 | + + When I route I should get + | from | to | code | + | b | c | NoRoute | + | a | b | NoRoute | + + + Scenario: Snap to target node with next section of segment blocked + Given the node map + """ + a-->b---c---d<--e + """ + + And the ways + | nodes | + | abc | + | cde | + + And the contract extra arguments "--segment-speed-file {speeds_file}" + And the customize extra arguments "--segment-speed-file {speeds_file}" + And the speed file + """ + 2,1,0 + 4,5,0 + """ + + When I route I should get + | from | to | route | time | weight | + | a | d | abc,cde,cde | 60s | 60 | + | e | b | cde,abc,abc | 60s | 60 | + + + When I route I should get + | from | to | code | + | a | e | NoRoute | + | e | a | NoRoute | + + + Scenario: Snapping to source node with previous section of segment blocked + Given the node map + """ + a<--b---c---d-->e + """ + + And the ways + | nodes | + | abc | + | cde | + + And the contract extra arguments "--segment-speed-file {speeds_file}" + And the customize extra arguments "--segment-speed-file {speeds_file}" + And the speed file + """ + 1,2,0 + 5,4,0 + """ + + When I route I should get + | from | to | code | + | a | e | NoRoute | + | b | e | NoRoute | + | e | a | NoRoute | + | d | a | NoRoute | + + + Scenario: Only snaps to one of many equidistant nearest locations + Given the node map + """ + b-------c + | | + | | + a 1 d + """ + + And the ways + | nodes | + | ab | + | bc | + | cd | + + When I route I should get + | from | to | route | time | weight | + | 1 | b | ab,ab | 30s | 30 | + | 1 | c | ab,bc,bc | 80s +-1 | 80 +-1 | + + + Scenario: Snaps to alternative big SCC candidate if nearest candidates are not strongly connected + Given the node map + """ + 1 + g---h---i + a-----b-----c + | + f-----e-----d + j---k---l + 2 + """ + + Given the extract extra arguments "--small-component-size=4" + + And the ways + | nodes | + | abc | + | cd | + | fed | + | ghi | + | jkl | + + # As forward direction is disabled... + When I route I should get + | from | to | route | time | weight | locations | + | 1 | 2 | abc,cd,fed,fed | 100s +-1 | 100 +-1 | b,c,d,e | + + + Scenario: Can use big or small SCC nearest candidates if at same location + Given the node map + """ + 1 + a-----b-----c + | | + g | + | + f-----e-----d + + """ + + Given the extract extra arguments "--small-component-size=4" + + And the ways + | nodes | oneway | + | ab | no | + | bc | no | + | cd | no | + | fed | no | + | bg | yes | # small SCC + + And the relations + | type | way:from | way:to | node:via | restriction | + | restriction | ab | bg | b | no_right_turn | + | restriction | bc | bg | b | no_left_turn | + + When I route I should get + | from | to | route | time | weight | locations | + | 1 | g | bg,bg | 20s | 20 | b,g | + | 1 | e | bc,cd,fed,fed | 120s +-1 | 120 +-1 | b,c,d,e | + + + Scenario: Using small SCC candidates when at same location as big SCC alternatives is not supported + Given the node map + """ + 1 + g---h---i + a-----b-----c + | | + | | + m | + f-----e-----d + j---k---l + 2 + + """ + + Given the extract extra arguments "--small-component-size=4" + + And the ways + | nodes | oneway | + | ab | no | + | bc | no | + | cd | no | + | fed | no | + | ghi | no | + | jkl | no | + | bm | yes | # small SCC + + And the relations + | type | way:from | way:to | node:via | restriction | + | restriction | ab | bm | b | no_right_turn | + | restriction | bc | bm | b | no_left_turn | + + When I route I should get + | from | to | route | time | weight | locations | + | 1 | 2 | bc,cd,fed,fed | 120s +-1 | 120 +-1 | b,c,d,e | + | 1 | m | bc,cd,fed,fed | 120s +-1 | 120 +-1 | b,c,d,e | + + + Scenario: Shortest via path with continuation, simple loop + Given the node map + """ + a---b + """ + + And the ways + | nodes | + | ab | + + When I route I should get + | waypoints | route | time | weight | + | a,b,a | ab,ab,ab,ab | 60s | 60 | + + + Scenario: Shortest via path with uturns, simple loop + Given the node map + """ + a---b + """ + + Given the query options + | continue_straight | false | + + And the ways + | nodes | + | ab | + + # Does not pay the cost of the turn + When I route I should get + | waypoints | route | time | weight | + | a,b,a | ab,ab,ab,ab | 40s | 40 | + + + Scenario: Shortest path with multiple endpoint snapping candidates + Given the node map + """ + b + + c + + a d f + + e + """ + + And the ways + | nodes | oneway | + | ab | no | + | ac | no | + | ad | no | + | ae | no | + | bf | no | + | cf | yes | + | df | yes | + | ef | no | + + + When I route I should get + | from | to | route | time | weight | + | a | f | ad,df,df | 40s | 40 | + | f | a | ef,ae,ae | 66.6s | 66.6 | + + When I request a travel time matrix I should get + | | a | f | + | a | 0 | 40 | + | f | 66.6 | 0 | + + + Scenario: Shortest via path with continuation, multiple waypoint snapping candidates + Given the node map + """ + b g + + c h + + a d f i + k + e j + """ + + And the ways + | nodes | oneway | + | ab | no | + | ac | no | + | ad | no | + | ae | no | + | bf | no | + | cf | yes | + | df | yes | + | ef | no | + | fg | no | + | fh | -1 | + | fi | -1 | + | fj | no | + | gk | no | + | hk | no | + | ik | no | + | kj | no | + + And the relations + | type | way:from | way:to | node:via | restriction | + | restriction | df | fg | f | only_left_turn | + | restriction | fi | bf | f | only_right_turn | + + # Longer routes can take different paths from sub-routes + When I route I should get + | waypoints | route | time | weight | + | a,f | ad,df,df | 40s | 40 | + | f,k | fj,kj,kj | 65.6s | 65.6 | + | a,f,k | ac,cf,cf,fj,kj,kj | 132.8s | 132.8 | + | k,f | ik,fi,fi | 54.3s | 54.3 | + | f,a | ef,ae,ae | 66.6s | 66.6 | + | k,f,a | kj,fj,fj,ef,ae,ae | 141.4s | 141.4 | + + When I request a travel time matrix I should get + | | a | f | k | + | a | 0 | 40 | 132.8 | + | f | 66.6 | 0 | 65.6 | + | k | 141.4 | 54.3 | 0 | + + + Scenario: Shortest via path with uturns, multiple waypoint snapping candidates + Given the node map + """ + b g + + c h + + a d f i + k + e j + """ + + Given the query options + | continue_straight | false | + + And the ways + | nodes | oneway | + | ab | no | + | ac | no | + | ad | no | + | ae | no | + | bf | no | + | cf | yes | + | df | yes | + | ef | no | + | fg | no | + | fh | -1 | + | fi | -1 | + | fj | no | + | gk | no | + | hk | no | + | ik | no | + | kj | no | + + And the relations + | type | way:from | way:to | node:via | restriction | + | restriction | df | fg | f | only_left_turn | + | restriction | fi | bf | f | only_right_turn | + + # Longer routes use same path as sub-routes + When I route I should get + | waypoints | route | time | weight | + | a,f | ad,df,df | 40s | 40 | + | f,k | fj,kj,kj | 65.6s | 65.6 | + | a,f,k | ad,df,df,fj,kj,kj | 105.6s | 105.6 | + | k,f | ik,fi,fi | 54.3s | 54.3 | + | f,a | ef,ae,ae | 66.6s | 66.6 | + | k,f,a | ik,fi,fi,ef,ae,ae | 120.9s | 120.9 | diff --git a/features/testbot/traffic_turn_penalties.feature b/features/testbot/traffic_turn_penalties.feature index 4ab9aa8b251..86bdeded125 100644 --- a/features/testbot/traffic_turn_penalties.feature +++ b/features/testbot/traffic_turn_penalties.feature @@ -47,7 +47,7 @@ Feature: Traffic - turn penalties applied to turn onto which a phantom node snap | 1 | e | ab,be,be | 36 km/h | 30s +-1 | | b | f | bc,cf,cf | 36 km/h | 40s +-1 | | 2 | f | bc,cf,cf | 36 km/h | 30s +-1 | - | c | g | cd,dg,dg | 144 km/h | 10s +-1 | + | c | g | cd,dg,dg | 72 km/h | 20s +-1 | | 3 | g | cd,dg,dg | 54 km/h | 20s +-1 | Scenario: Weighting based on turn penalty file with weights @@ -65,5 +65,5 @@ Feature: Traffic - turn penalties applied to turn onto which a phantom node snap | 1 | e | ab,be,be | 36 km/h | 30s +-1 | 6.8,20,0 | | b | f | bc,cf,cf | 36 km/h | 40s +-1 | 20,20,0 | | 2 | f | bc,cf,cf | 36 km/h | 30s +-1 | 10.1,20,0 | - | c | g | cd,dg,dg | 144 km/h | 10s +-1 | 120.8,20,0 | + | c | g | cd,dg,dg | 72 km/h | 20s +-1 | 120.8,20,0 | | 3 | g | cd,dg,dg | 54 km/h | 20s +-1 | 110.9,20,0 | diff --git a/features/testbot/weight.feature b/features/testbot/weight.feature index 5d8aadee37e..04e42b46bb3 100644 --- a/features/testbot/weight.feature +++ b/features/testbot/weight.feature @@ -281,7 +281,7 @@ Feature: Weight tests When I route I should get | waypoints | route | distance | weights | times | - | a,c | , | 40m +-.1 | 5.12,0 | 290s,0s | + | a,c | , | 40m +-.1 | 2.22,0 | 200s,0s | | a,e | ,, | 60m +-.1 | 5.12,1.11,0 | 290s,100s,0s | | e,a | ,, | 60m +-.1 | 2.21,2.22,0 | 10s,200s,0s | | e,d | ,, | 40m +-.1 | 4.01,1.11,0 | 190s,100s,0s | diff --git a/include/engine/api/base_api.hpp b/include/engine/api/base_api.hpp index 3f27dcf5178..cc568fb5784 100644 --- a/include/engine/api/base_api.hpp +++ b/include/engine/api/base_api.hpp @@ -9,9 +9,12 @@ #include "engine/hint.hpp" #include "util/coordinate_calculation.hpp" +#include #include +#include #include +#include #include #include @@ -22,6 +25,8 @@ namespace engine namespace api { +static const constexpr char *INTERSECTION_DELIMITER = " / "; + class BaseAPI { public: @@ -30,92 +35,129 @@ class BaseAPI { } - util::json::Array MakeWaypoints(const std::vector &segment_end_coordinates) const + util::json::Array + MakeWaypoints(const std::vector &waypoint_candidates) const { BOOST_ASSERT(parameters.coordinates.size() > 0); - BOOST_ASSERT(parameters.coordinates.size() == segment_end_coordinates.size() + 1); + BOOST_ASSERT(parameters.coordinates.size() == waypoint_candidates.size()); util::json::Array waypoints; waypoints.values.resize(parameters.coordinates.size()); - waypoints.values[0] = MakeWaypoint(segment_end_coordinates.front().source_phantom); - auto out_iter = std::next(waypoints.values.begin()); boost::range::transform( - segment_end_coordinates, out_iter, [this](const PhantomNodes &phantom_pair) { - return MakeWaypoint(phantom_pair.target_phantom); - }); + waypoint_candidates, + waypoints.values.begin(), + [this](const PhantomNodeCandidates &candidates) { return MakeWaypoint(candidates); }); return waypoints; } // FIXME: gcc 4.9 does not like MakeWaypoints to be protected // protected: - util::json::Object MakeWaypoint(const PhantomNode &phantom) const + util::json::Object MakeWaypoint(const PhantomNodeCandidates &candidates) const { + // TODO: check forward/reverse + const auto toName = [this](const auto &phantom) { + return facade.GetNameForID(facade.GetNameIndex(phantom.forward_segment_id.id)) + .to_string(); + }; + const auto noEmpty = [](const auto &name) { return !name.empty(); }; + + // At an intersection we may have multiple phantom node candidates. + // Combine them to represent the waypoint name. + std::string waypoint_name = boost::algorithm::join( + candidates | boost::adaptors::transformed(toName) | boost::adaptors::filtered(noEmpty), + INTERSECTION_DELIMITER); + + const auto &snapped_location = candidatesSnappedLocation(candidates); + const auto &input_location = candidatesInputLocation(candidates); if (parameters.generate_hints) { - // TODO: check forward/reverse + std::vector seg_hints(candidates.size()); + std::transform(candidates.begin(), + candidates.end(), + seg_hints.begin(), + [this](const auto &phantom) { + return SegmentHint{phantom, facade.GetCheckSum()}; + }); + return json::makeWaypoint( - phantom.location, - util::coordinate_calculation::greatCircleDistance(phantom.location, - phantom.input_location), - facade.GetNameForID(facade.GetNameIndex(phantom.forward_segment_id.id)).to_string(), - Hint{phantom, facade.GetCheckSum()}); + snapped_location, + util::coordinate_calculation::greatCircleDistance(snapped_location, input_location), + waypoint_name, + {std::move(seg_hints)}); } else { - // TODO: check forward/reverse return json::makeWaypoint( - phantom.location, - util::coordinate_calculation::greatCircleDistance(phantom.location, - phantom.input_location), - facade.GetNameForID(facade.GetNameIndex(phantom.forward_segment_id.id)) - .to_string()); + snapped_location, + util::coordinate_calculation::greatCircleDistance(snapped_location, input_location), + waypoint_name); } } flatbuffers::Offset>> MakeWaypoints(flatbuffers::FlatBufferBuilder *builder, - const std::vector &segment_end_coordinates) const + const std::vector &waypoint_candidates) const { BOOST_ASSERT(parameters.coordinates.size() > 0); - BOOST_ASSERT(parameters.coordinates.size() == segment_end_coordinates.size() + 1); + BOOST_ASSERT(parameters.coordinates.size() == waypoint_candidates.size()); std::vector> waypoints; waypoints.resize(parameters.coordinates.size()); - waypoints[0] = - MakeWaypoint(builder, segment_end_coordinates.front().source_phantom)->Finish(); - - std::transform(segment_end_coordinates.begin(), - segment_end_coordinates.end(), - std::next(waypoints.begin()), - [this, builder](const PhantomNodes &phantom_pair) { - return MakeWaypoint(builder, phantom_pair.target_phantom)->Finish(); + + std::transform(waypoint_candidates.begin(), + waypoint_candidates.end(), + waypoints.begin(), + [this, builder](const PhantomNodeCandidates &candidates) { + return MakeWaypoint(builder, candidates)->Finish(); }); return builder->CreateVector(waypoints); } // FIXME: gcc 4.9 does not like MakeWaypoints to be protected // protected: - std::unique_ptr MakeWaypoint(flatbuffers::FlatBufferBuilder *builder, - const PhantomNode &phantom) const + std::unique_ptr + MakeWaypoint(flatbuffers::FlatBufferBuilder *builder, + const PhantomNodeCandidates &candidates) const { + const auto &snapped_location = candidatesSnappedLocation(candidates); + const auto &input_location = candidatesInputLocation(candidates); auto location = - fbresult::Position(static_cast(util::toFloating(phantom.location.lon)), - static_cast(util::toFloating(phantom.location.lat))); - auto name_string = builder->CreateString( - facade.GetNameForID(facade.GetNameIndex(phantom.forward_segment_id.id)).to_string()); + fbresult::Position(static_cast(util::toFloating(snapped_location.lon)), + static_cast(util::toFloating(snapped_location.lat))); + + const auto toName = [this](const auto &phantom) { + return facade.GetNameForID(facade.GetNameIndex(phantom.forward_segment_id.id)) + .to_string(); + }; + const auto noEmpty = [](const auto &name) { return !name.empty(); }; + + // At an intersection we may have multiple phantom node candidates. + // Combine them to represent the waypoint name. + std::string waypoint_name = boost::algorithm::join( + candidates | boost::adaptors::transformed(toName) | boost::adaptors::filtered(noEmpty), + INTERSECTION_DELIMITER); + auto name_string = builder->CreateString(waypoint_name); flatbuffers::Offset hint_string; if (parameters.generate_hints) { - hint_string = builder->CreateString(Hint{phantom, facade.GetCheckSum()}.ToBase64()); + std::vector seg_hints(candidates.size()); + std::transform(candidates.begin(), + candidates.end(), + seg_hints.begin(), + [this](const auto &phantom) { + return SegmentHint{phantom, facade.GetCheckSum()}; + }); + Hint hint{std::move(seg_hints)}; + hint_string = builder->CreateString(hint.ToBase64()); } auto waypoint = std::make_unique(*builder); waypoint->add_location(&location); - waypoint->add_distance(util::coordinate_calculation::greatCircleDistance( - phantom.location, phantom.input_location)); + waypoint->add_distance( + util::coordinate_calculation::greatCircleDistance(snapped_location, input_location)); waypoint->add_name(name_string); if (parameters.generate_hints) { diff --git a/include/engine/api/base_parameters.hpp b/include/engine/api/base_parameters.hpp index e49f711579f..d5bb71a5323 100644 --- a/include/engine/api/base_parameters.hpp +++ b/include/engine/api/base_parameters.hpp @@ -51,14 +51,14 @@ namespace api * Holds member attributes: * - coordinates: for specifying location(s) to services * - hints: hint for the service to derive the position(s) in the road network more efficiently, - * optional per coordinate + * optional per coordinate. Multiple hints can be provided for a coordinate. * - radiuses: limits the search for segments in the road network to given radius(es) in meter, * optional per coordinate * - bearings: limits the search for segments in the road network to given bearing(s) in degree * towards true north in clockwise direction, optional per coordinate * - approaches: force the phantom node to start towards the node with the road country side. * - * \see OSRM, Coordinate, Hint, Bearing, RouteParame, RouteParameters, TableParameters, + * \see OSRM, Coordinate, Hint, Bearing, RouteParameters, TableParameters, * NearestParameters, TripParameters, MatchParameters and TileParameters */ struct BaseParameters diff --git a/include/engine/api/match_api.hpp b/include/engine/api/match_api.hpp index e95882f4656..d7899817d29 100644 --- a/include/engine/api/match_api.hpp +++ b/include/engine/api/match_api.hpp @@ -76,7 +76,7 @@ class MatchAPI final : public RouteAPI routes.values.reserve(number_of_routes); for (auto index : util::irange(0UL, sub_matchings.size())) { - auto route = MakeRoute(sub_routes[index].segment_end_coordinates, + auto route = MakeRoute(sub_routes[index].leg_endpoints, sub_routes[index].unpacked_path_segments, sub_routes[index].source_traversed_in_reverse, sub_routes[index].target_traversed_in_reverse); @@ -146,7 +146,7 @@ class MatchAPI final : public RouteAPI } const auto &phantom = sub_matchings[matching_index.sub_matching_index].nodes[matching_index.point_index]; - auto waypoint = BaseAPI::MakeWaypoint(&fb_result, phantom); + auto waypoint = BaseAPI::MakeWaypoint(&fb_result, {phantom}); waypoint->add_matchings_index(matching_index.sub_matching_index); waypoint->add_alternatives_count(sub_matchings[matching_index.sub_matching_index] .alternatives_count[matching_index.point_index]); @@ -200,7 +200,7 @@ class MatchAPI final : public RouteAPI } const auto &phantom = sub_matchings[matching_index.sub_matching_index].nodes[matching_index.point_index]; - auto waypoint = BaseAPI::MakeWaypoint(phantom); + auto waypoint = BaseAPI::MakeWaypoint({phantom}); waypoint.values["matchings_index"] = matching_index.sub_matching_index; waypoint.values["waypoint_index"] = matching_index.point_index; waypoint.values["alternatives_count"] = diff --git a/include/engine/api/nearest_api.hpp b/include/engine/api/nearest_api.hpp index 246cfe88ce2..43c42de293c 100644 --- a/include/engine/api/nearest_api.hpp +++ b/include/engine/api/nearest_api.hpp @@ -71,7 +71,7 @@ class NearestAPI final : public BaseAPI auto node_values = MakeNodes(phantom_node); fbresult::Uint64Pair nodes{node_values.first, node_values.second}; - auto waypoint = MakeWaypoint(&fb_result, phantom_node); + auto waypoint = MakeWaypoint(&fb_result, {phantom_node}); waypoint->add_nodes(&nodes); return waypoint->Finish(); }); @@ -100,7 +100,7 @@ class NearestAPI final : public BaseAPI waypoints.values.begin(), [this](const PhantomNodeWithDistance &phantom_with_distance) { auto &phantom_node = phantom_with_distance.phantom_node; - auto waypoint = MakeWaypoint(phantom_node); + auto waypoint = MakeWaypoint({phantom_node}); util::json::Array nodes; diff --git a/include/engine/api/route_api.hpp b/include/engine/api/route_api.hpp index 7b4dfc02f99..e72a4af8671 100644 --- a/include/engine/api/route_api.hpp +++ b/include/engine/api/route_api.hpp @@ -47,8 +47,8 @@ class RouteAPI : public BaseAPI void MakeResponse(const InternalManyRoutesResult &raw_routes, - const std::vector - &all_start_end_points, // all used coordinates, ignoring waypoints= parameter + const std::vector + &waypoint_candidates, // all used coordinates, ignoring waypoints= parameter osrm::engine::api::ResultT &response) const { BOOST_ASSERT(!raw_routes.routes.empty()); @@ -56,19 +56,19 @@ class RouteAPI : public BaseAPI if (response.is()) { auto &fb_result = response.get(); - MakeResponse(raw_routes, all_start_end_points, fb_result); + MakeResponse(raw_routes, waypoint_candidates, fb_result); } else { auto &json_result = response.get(); - MakeResponse(raw_routes, all_start_end_points, json_result); + MakeResponse(raw_routes, waypoint_candidates, json_result); } } void MakeResponse(const InternalManyRoutesResult &raw_routes, - const std::vector - &all_start_end_points, // all used coordinates, ignoring waypoints= parameter + const std::vector + &waypoint_candidates, // all used coordinates, ignoring waypoints= parameter flatbuffers::FlatBufferBuilder &fb_result) const { @@ -80,8 +80,8 @@ class RouteAPI : public BaseAPI } auto response = - MakeFBResponse(raw_routes, fb_result, [this, &all_start_end_points, &fb_result]() { - return BaseAPI::MakeWaypoints(&fb_result, all_start_end_points); + MakeFBResponse(raw_routes, fb_result, [this, &waypoint_candidates, &fb_result]() { + return BaseAPI::MakeWaypoints(&fb_result, waypoint_candidates); }); if (!data_timestamp.empty()) @@ -93,8 +93,8 @@ class RouteAPI : public BaseAPI void MakeResponse(const InternalManyRoutesResult &raw_routes, - const std::vector - &all_start_end_points, // all used coordinates, ignoring waypoints= parameter + const std::vector + &waypoint_candidates, // all used coordinates, ignoring waypoints= parameter util::json::Object &response) const { util::json::Array jsRoutes; @@ -104,7 +104,7 @@ class RouteAPI : public BaseAPI if (!route.is_valid()) continue; - jsRoutes.values.push_back(MakeRoute(route.segment_end_coordinates, + jsRoutes.values.push_back(MakeRoute(route.leg_endpoints, route.unpacked_path_segments, route.source_traversed_in_reverse, route.target_traversed_in_reverse)); @@ -112,7 +112,7 @@ class RouteAPI : public BaseAPI if (!parameters.skip_waypoints) { - response.values["waypoints"] = BaseAPI::MakeWaypoints(all_start_end_points); + response.values["waypoints"] = BaseAPI::MakeWaypoints(waypoint_candidates); } response.values["routes"] = std::move(jsRoutes); response.values["code"] = "Ok"; @@ -138,7 +138,7 @@ class RouteAPI : public BaseAPI continue; routes.push_back(MakeRoute(fb_result, - raw_route.segment_end_coordinates, + raw_route.leg_endpoints, raw_route.unpacked_path_segments, raw_route.source_traversed_in_reverse, raw_route.target_traversed_in_reverse)); @@ -328,12 +328,12 @@ class RouteAPI : public BaseAPI flatbuffers::Offset MakeRoute(flatbuffers::FlatBufferBuilder &fb_result, - const std::vector &segment_end_coordinates, + const std::vector &leg_endpoints, const std::vector> &unpacked_path_segments, const std::vector &source_traversed_in_reverse, const std::vector &target_traversed_in_reverse) const { - auto legs_info = MakeLegs(segment_end_coordinates, + auto legs_info = MakeLegs(leg_endpoints, unpacked_path_segments, source_traversed_in_reverse, target_traversed_in_reverse); @@ -705,12 +705,12 @@ class RouteAPI : public BaseAPI return fb_result.CreateVector(intersections); } - util::json::Object MakeRoute(const std::vector &segment_end_coordinates, + util::json::Object MakeRoute(const std::vector &leg_endpoints, const std::vector> &unpacked_path_segments, const std::vector &source_traversed_in_reverse, const std::vector &target_traversed_in_reverse) const { - auto legs_info = MakeLegs(segment_end_coordinates, + auto legs_info = MakeLegs(leg_endpoints, unpacked_path_segments, source_traversed_in_reverse, target_traversed_in_reverse); @@ -868,7 +868,7 @@ class RouteAPI : public BaseAPI const RouteParameters ¶meters; std::pair, std::vector> - MakeLegs(const std::vector &segment_end_coordinates, + MakeLegs(const std::vector &leg_endpoints, const std::vector> &unpacked_path_segments, const std::vector &source_traversed_in_reverse, const std::vector &target_traversed_in_reverse) const @@ -877,13 +877,13 @@ class RouteAPI : public BaseAPI std::make_pair(std::vector(), std::vector()); auto &legs = result.first; auto &leg_geometries = result.second; - auto number_of_legs = segment_end_coordinates.size(); + auto number_of_legs = leg_endpoints.size(); legs.reserve(number_of_legs); leg_geometries.reserve(number_of_legs); for (auto idx : util::irange(0UL, number_of_legs)) { - const auto &phantoms = segment_end_coordinates[idx]; + const auto &phantoms = leg_endpoints[idx]; const auto &path_data = unpacked_path_segments[idx]; const bool reversed_source = source_traversed_in_reverse[idx]; diff --git a/include/engine/api/table_api.hpp b/include/engine/api/table_api.hpp index 2c378e6e7d3..c7a9dba837d 100644 --- a/include/engine/api/table_api.hpp +++ b/include/engine/api/table_api.hpp @@ -48,25 +48,25 @@ class TableAPI final : public BaseAPI virtual void MakeResponse(const std::pair, std::vector> &tables, - const std::vector &phantoms, + const std::vector &candidates, const std::vector &fallback_speed_cells, osrm::engine::api::ResultT &response) const { if (response.is()) { auto &fb_result = response.get(); - MakeResponse(tables, phantoms, fallback_speed_cells, fb_result); + MakeResponse(tables, candidates, fallback_speed_cells, fb_result); } else { auto &json_result = response.get(); - MakeResponse(tables, phantoms, fallback_speed_cells, json_result); + MakeResponse(tables, candidates, fallback_speed_cells, json_result); } } virtual void MakeResponse(const std::pair, std::vector> &tables, - const std::vector &phantoms, + const std::vector &candidates, const std::vector &fallback_speed_cells, flatbuffers::FlatBufferBuilder &fb_result) const { @@ -86,15 +86,15 @@ class TableAPI final : public BaseAPI { if (!parameters.skip_waypoints) { - sources = MakeWaypoints(fb_result, phantoms); + sources = MakeWaypoints(fb_result, candidates); } - number_of_sources = phantoms.size(); + number_of_sources = candidates.size(); } else { if (!parameters.skip_waypoints) { - sources = MakeWaypoints(fb_result, phantoms, parameters.sources); + sources = MakeWaypoints(fb_result, candidates, parameters.sources); } } @@ -104,15 +104,15 @@ class TableAPI final : public BaseAPI { if (!parameters.skip_waypoints) { - destinations = MakeWaypoints(fb_result, phantoms); + destinations = MakeWaypoints(fb_result, candidates); } - number_of_destinations = phantoms.size(); + number_of_destinations = candidates.size(); } else { if (!parameters.skip_waypoints) { - destinations = MakeWaypoints(fb_result, phantoms, parameters.destinations); + destinations = MakeWaypoints(fb_result, candidates, parameters.destinations); } } @@ -168,7 +168,7 @@ class TableAPI final : public BaseAPI virtual void MakeResponse(const std::pair, std::vector> &tables, - const std::vector &phantoms, + const std::vector &candidates, const std::vector &fallback_speed_cells, util::json::Object &response) const { @@ -180,15 +180,15 @@ class TableAPI final : public BaseAPI { if (!parameters.skip_waypoints) { - response.values["sources"] = MakeWaypoints(phantoms); + response.values["sources"] = MakeWaypoints(candidates); } - number_of_sources = phantoms.size(); + number_of_sources = candidates.size(); } else { if (!parameters.skip_waypoints) { - response.values["sources"] = MakeWaypoints(phantoms, parameters.sources); + response.values["sources"] = MakeWaypoints(candidates, parameters.sources); } } @@ -196,15 +196,16 @@ class TableAPI final : public BaseAPI { if (!parameters.skip_waypoints) { - response.values["destinations"] = MakeWaypoints(phantoms); + response.values["destinations"] = MakeWaypoints(candidates); } - number_of_destinations = phantoms.size(); + number_of_destinations = candidates.size(); } else { if (!parameters.skip_waypoints) { - response.values["destinations"] = MakeWaypoints(phantoms, parameters.destinations); + response.values["destinations"] = + MakeWaypoints(candidates, parameters.destinations); } } @@ -236,32 +237,34 @@ class TableAPI final : public BaseAPI protected: virtual flatbuffers::Offset>> MakeWaypoints(flatbuffers::FlatBufferBuilder &builder, - const std::vector &phantoms) const + const std::vector &candidates) const { std::vector> waypoints; - waypoints.reserve(phantoms.size()); - BOOST_ASSERT(phantoms.size() == parameters.coordinates.size()); + waypoints.reserve(candidates.size()); + BOOST_ASSERT(candidates.size() == parameters.coordinates.size()); - boost::range::transform( - phantoms, std::back_inserter(waypoints), [this, &builder](const PhantomNode &phantom) { - return BaseAPI::MakeWaypoint(&builder, phantom)->Finish(); - }); + boost::range::transform(candidates, + std::back_inserter(waypoints), + [this, &builder](const PhantomNodeCandidates &candidates) { + return BaseAPI::MakeWaypoint(&builder, candidates)->Finish(); + }); return builder.CreateVector(waypoints); } virtual flatbuffers::Offset>> MakeWaypoints(flatbuffers::FlatBufferBuilder &builder, - const std::vector &phantoms, + const std::vector &candidates, const std::vector &indices) const { std::vector> waypoints; waypoints.reserve(indices.size()); - boost::range::transform(indices, - std::back_inserter(waypoints), - [this, &builder, phantoms](const std::size_t idx) { - BOOST_ASSERT(idx < phantoms.size()); - return BaseAPI::MakeWaypoint(&builder, phantoms[idx])->Finish(); - }); + boost::range::transform( + indices, + std::back_inserter(waypoints), + [this, &builder, &candidates](const std::size_t idx) { + BOOST_ASSERT(idx < candidates.size()); + return BaseAPI::MakeWaypoint(&builder, candidates[idx])->Finish(); + }); return builder.CreateVector(waypoints); } @@ -313,29 +316,31 @@ class TableAPI final : public BaseAPI return builder.CreateVector(fb_table); } - virtual util::json::Array MakeWaypoints(const std::vector &phantoms) const + virtual util::json::Array + MakeWaypoints(const std::vector &candidates) const { util::json::Array json_waypoints; - json_waypoints.values.reserve(phantoms.size()); - BOOST_ASSERT(phantoms.size() == parameters.coordinates.size()); + json_waypoints.values.reserve(candidates.size()); + BOOST_ASSERT(candidates.size() == parameters.coordinates.size()); - boost::range::transform( - phantoms, - std::back_inserter(json_waypoints.values), - [this](const PhantomNode &phantom) { return BaseAPI::MakeWaypoint(phantom); }); + boost::range::transform(candidates, + std::back_inserter(json_waypoints.values), + [this](const PhantomNodeCandidates &candidates) { + return BaseAPI::MakeWaypoint(candidates); + }); return json_waypoints; } - virtual util::json::Array MakeWaypoints(const std::vector &phantoms, + virtual util::json::Array MakeWaypoints(const std::vector &candidates, const std::vector &indices) const { util::json::Array json_waypoints; json_waypoints.values.reserve(indices.size()); boost::range::transform(indices, std::back_inserter(json_waypoints.values), - [this, phantoms](const std::size_t idx) { - BOOST_ASSERT(idx < phantoms.size()); - return BaseAPI::MakeWaypoint(phantoms[idx]); + [this, &candidates](const std::size_t idx) { + BOOST_ASSERT(idx < candidates.size()); + return BaseAPI::MakeWaypoint(candidates[idx]); }); return json_waypoints; } diff --git a/include/engine/api/trip_api.hpp b/include/engine/api/trip_api.hpp index 9b82a016ed8..fb8e5598bc2 100644 --- a/include/engine/api/trip_api.hpp +++ b/include/engine/api/trip_api.hpp @@ -26,7 +26,7 @@ class TripAPI final : public RouteAPI } void MakeResponse(const std::vector> &sub_trips, const std::vector &sub_routes, - const std::vector &phantoms, + const std::vector &candidates, osrm::engine::api::ResultT &response) const { BOOST_ASSERT(sub_trips.size() == sub_routes.size()); @@ -34,17 +34,17 @@ class TripAPI final : public RouteAPI if (response.is()) { auto &fb_result = response.get(); - MakeResponse(sub_trips, sub_routes, phantoms, fb_result); + MakeResponse(sub_trips, sub_routes, candidates, fb_result); } else { auto &json_result = response.get(); - MakeResponse(sub_trips, sub_routes, phantoms, json_result); + MakeResponse(sub_trips, sub_routes, candidates, json_result); } } void MakeResponse(const std::vector> &sub_trips, const std::vector &sub_routes, - const std::vector &phantoms, + const std::vector &candidates, flatbuffers::FlatBufferBuilder &fb_result) const { auto data_timestamp = facade.GetTimestamp(); @@ -55,8 +55,8 @@ class TripAPI final : public RouteAPI } auto response = - MakeFBResponse(sub_routes, fb_result, [this, &fb_result, &sub_trips, &phantoms]() { - return MakeWaypoints(fb_result, sub_trips, phantoms); + MakeFBResponse(sub_routes, fb_result, [this, &fb_result, &sub_trips, &candidates]() { + return MakeWaypoints(fb_result, sub_trips, candidates); }); if (!data_timestamp.empty()) @@ -67,7 +67,7 @@ class TripAPI final : public RouteAPI } void MakeResponse(const std::vector> &sub_trips, const std::vector &sub_routes, - const std::vector &phantoms, + const std::vector &candidates, util::json::Object &response) const { auto number_of_routes = sub_trips.size(); @@ -75,7 +75,7 @@ class TripAPI final : public RouteAPI routes.values.reserve(number_of_routes); for (auto index : util::irange(0UL, sub_trips.size())) { - auto route = MakeRoute(sub_routes[index].segment_end_coordinates, + auto route = MakeRoute(sub_routes[index].leg_endpoints, sub_routes[index].unpacked_path_segments, sub_routes[index].source_traversed_in_reverse, sub_routes[index].target_traversed_in_reverse); @@ -83,7 +83,7 @@ class TripAPI final : public RouteAPI } if (!parameters.skip_waypoints) { - response.values["waypoints"] = MakeWaypoints(sub_trips, phantoms); + response.values["waypoints"] = MakeWaypoints(sub_trips, candidates); } response.values["trips"] = std::move(routes); response.values["code"] = "Ok"; @@ -120,7 +120,7 @@ class TripAPI final : public RouteAPI flatbuffers::Offset>> MakeWaypoints(flatbuffers::FlatBufferBuilder &fb_result, const std::vector> &sub_trips, - const std::vector &phantoms) const + const std::vector &candidates) const { std::vector> waypoints; waypoints.reserve(parameters.coordinates.size()); @@ -132,7 +132,7 @@ class TripAPI final : public RouteAPI auto trip_index = input_idx_to_trip_idx[input_index]; BOOST_ASSERT(!trip_index.NotUsed()); - auto waypoint = BaseAPI::MakeWaypoint(&fb_result, phantoms[input_index]); + auto waypoint = BaseAPI::MakeWaypoint(&fb_result, candidates[input_index]); waypoint->add_waypoint_index(trip_index.point_index); waypoint->add_trips_index(trip_index.sub_trip_index); waypoints.push_back(waypoint->Finish()); @@ -142,7 +142,7 @@ class TripAPI final : public RouteAPI } util::json::Array MakeWaypoints(const std::vector> &sub_trips, - const std::vector &phantoms) const + const std::vector &candidates) const { util::json::Array waypoints; waypoints.values.reserve(parameters.coordinates.size()); @@ -154,7 +154,7 @@ class TripAPI final : public RouteAPI auto trip_index = input_idx_to_trip_idx[input_index]; BOOST_ASSERT(!trip_index.NotUsed()); - auto waypoint = BaseAPI::MakeWaypoint(phantoms[input_index]); + auto waypoint = BaseAPI::MakeWaypoint(candidates[input_index]); waypoint.values["trips_index"] = trip_index.sub_trip_index; waypoint.values["waypoint_index"] = trip_index.point_index; waypoints.values.push_back(std::move(waypoint)); diff --git a/include/engine/datafacade/contiguous_internalmem_datafacade.hpp b/include/engine/datafacade/contiguous_internalmem_datafacade.hpp index d426ed84fa7..0548e056950 100644 --- a/include/engine/datafacade/contiguous_internalmem_datafacade.hpp +++ b/include/engine/datafacade/contiguous_internalmem_datafacade.hpp @@ -323,127 +323,41 @@ class ContiguousInternalMemoryDataFacadeBase : public BaseDataFacade std::vector NearestPhantomNodesInRange(const util::Coordinate input_coordinate, - const float max_distance, + const double max_distance, + const boost::optional bearing, const Approach approach, const bool use_all_edges) const override final { BOOST_ASSERT(m_geospatial_query.get()); - return m_geospatial_query->NearestPhantomNodesInRange( - input_coordinate, max_distance, approach, use_all_edges); - } - - std::vector - NearestPhantomNodesInRange(const util::Coordinate input_coordinate, - const float max_distance, - const int bearing, - const int bearing_range, - const Approach approach, - const bool use_all_edges) const override final - { - BOOST_ASSERT(m_geospatial_query.get()); - - return m_geospatial_query->NearestPhantomNodesInRange( - input_coordinate, max_distance, bearing, bearing_range, approach, use_all_edges); - } - - std::vector - NearestPhantomNodes(const util::Coordinate input_coordinate, - const unsigned max_results, - const Approach approach) const override final - { - BOOST_ASSERT(m_geospatial_query.get()); - - return m_geospatial_query->NearestPhantomNodes(input_coordinate, max_results, approach); - } - - std::vector - NearestPhantomNodes(const util::Coordinate input_coordinate, - const unsigned max_results, - const double max_distance, - const Approach approach) const override final - { - BOOST_ASSERT(m_geospatial_query.get()); - return m_geospatial_query->NearestPhantomNodes( - input_coordinate, max_results, max_distance, approach); + input_coordinate, approach, boost::none, max_distance, bearing, use_all_edges); } std::vector NearestPhantomNodes(const util::Coordinate input_coordinate, - const unsigned max_results, - const int bearing, - const int bearing_range, + const size_t max_results, + const boost::optional max_distance, + const boost::optional bearing, const Approach approach) const override final { BOOST_ASSERT(m_geospatial_query.get()); return m_geospatial_query->NearestPhantomNodes( - input_coordinate, max_results, bearing, bearing_range, approach); - } - - std::vector - NearestPhantomNodes(const util::Coordinate input_coordinate, - const unsigned max_results, - const double max_distance, - const int bearing, - const int bearing_range, - const Approach approach) const override final - { - BOOST_ASSERT(m_geospatial_query.get()); - - return m_geospatial_query->NearestPhantomNodes( - input_coordinate, max_results, max_distance, bearing, bearing_range, approach); - } - - std::pair - NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate, - const Approach approach, - const bool use_all_edges) const override final - { - BOOST_ASSERT(m_geospatial_query.get()); - - return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent( - input_coordinate, approach, use_all_edges); - } - - std::pair - NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate, - const double max_distance, - const Approach approach, - const bool use_all_edges) const override final - { - BOOST_ASSERT(m_geospatial_query.get()); - - return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent( - input_coordinate, max_distance, approach, use_all_edges); - } - - std::pair - NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate, - const double max_distance, - const int bearing, - const int bearing_range, - const Approach approach, - const bool use_all_edges) const override final - { - BOOST_ASSERT(m_geospatial_query.get()); - - return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent( - input_coordinate, max_distance, bearing, bearing_range, approach, use_all_edges); + input_coordinate, approach, max_results, max_distance, bearing, boost::none); } - std::pair - NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate, - const int bearing, - const int bearing_range, - const Approach approach, - const bool use_all_edges) const override final + PhantomCandidateAlternatives + NearestCandidatesWithAlternativeFromBigComponent(const util::Coordinate input_coordinate, + const boost::optional max_distance, + const boost::optional bearing, + const Approach approach, + const bool use_all_edges) const override final { BOOST_ASSERT(m_geospatial_query.get()); - return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent( - input_coordinate, bearing, bearing_range, approach, use_all_edges); + return m_geospatial_query->NearestCandidatesWithAlternativeFromBigComponent( + input_coordinate, approach, max_distance, bearing, use_all_edges); } std::uint32_t GetCheckSum() const override final { return m_check_sum; } diff --git a/include/engine/datafacade/datafacade_base.hpp b/include/engine/datafacade/datafacade_base.hpp index 153810fdd00..936ccf0c2a9 100644 --- a/include/engine/datafacade/datafacade_base.hpp +++ b/include/engine/datafacade/datafacade_base.hpp @@ -35,6 +35,7 @@ #include #include +#include #include #include #include @@ -130,62 +131,24 @@ class BaseDataFacade virtual std::vector NearestPhantomNodesInRange(const util::Coordinate input_coordinate, - const float max_distance, - const int bearing, - const int bearing_range, - const Approach approach, - const bool use_all_edges) const = 0; - virtual std::vector - NearestPhantomNodesInRange(const util::Coordinate input_coordinate, - const float max_distance, + const double max_distance, + const boost::optional bearing, const Approach approach, const bool use_all_edges) const = 0; virtual std::vector NearestPhantomNodes(const util::Coordinate input_coordinate, - const unsigned max_results, - const double max_distance, - const int bearing, - const int bearing_range, - const Approach approach) const = 0; - virtual std::vector - NearestPhantomNodes(const util::Coordinate input_coordinate, - const unsigned max_results, - const int bearing, - const int bearing_range, - const Approach approach) const = 0; - virtual std::vector - NearestPhantomNodes(const util::Coordinate input_coordinate, - const unsigned max_results, - const Approach approach) const = 0; - virtual std::vector - NearestPhantomNodes(const util::Coordinate input_coordinate, - const unsigned max_results, - const double max_distance, + const size_t max_results, + const boost::optional max_distance, + const boost::optional bearing, const Approach approach) const = 0; - virtual std::pair - NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate, - const Approach approach, - const bool use_all_edges) const = 0; - virtual std::pair - NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate, - const double max_distance, - const Approach approach, - const bool use_all_edges) const = 0; - virtual std::pair - NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate, - const double max_distance, - const int bearing, - const int bearing_range, - const Approach approach, - const bool use_all_edges) const = 0; - virtual std::pair - NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate, - const int bearing, - const int bearing_range, - const Approach approach, - const bool use_all_edges = false) const = 0; + virtual PhantomCandidateAlternatives + NearestCandidatesWithAlternativeFromBigComponent(const util::Coordinate input_coordinate, + const boost::optional max_distance, + const boost::optional bearing, + const Approach approach, + const bool use_all_edges) const = 0; virtual bool HasLaneData(const EdgeID edge_based_edge_id) const = 0; virtual util::guidance::LaneTupleIdPair GetLaneData(const EdgeID edge_based_edge_id) const = 0; diff --git a/include/engine/geospatial_query.hpp b/include/engine/geospatial_query.hpp index 2b01b20dd05..48696ce0cad 100644 --- a/include/engine/geospatial_query.hpp +++ b/include/engine/geospatial_query.hpp @@ -2,6 +2,7 @@ #define GEOSPATIAL_QUERY_HPP #include "engine/approach.hpp" +#include "engine/bearing.hpp" #include "engine/phantom_node.hpp" #include "util/bearing.hpp" #include "util/coordinate_calculation.hpp" @@ -22,10 +23,10 @@ namespace osrm namespace engine { -inline std::pair boolPairAnd(const std::pair &A, - const std::pair &B) +inline std::pair operator&&(const std::pair &a, + const std::pair &b) { - return std::make_pair(A.first && B.first, A.second && B.second); + return {a.first && b.first, a.second && b.second}; } // Implements complex queries on top of an RTree and builds PhantomNodes from it. @@ -48,390 +49,241 @@ template class GeospatialQuery return rtree.SearchInBox(bbox); } - // Returns nearest PhantomNodes in the given bearing range within max_distance. - // Does not filter by small/big component! - std::vector - NearestPhantomNodesInRange(const util::Coordinate input_coordinate, - const double max_distance, - const Approach approach, - const bool use_all_edges) const - { - auto results = rtree.Nearest( - input_coordinate, - [this, approach, &input_coordinate, use_all_edges](const CandidateSegment &segment) { - return boolPairAnd( - boolPairAnd(HasValidEdge(segment, use_all_edges), CheckSegmentExclude(segment)), - CheckApproach(input_coordinate, segment, approach)); - }, - [this, max_distance, input_coordinate](const std::size_t, - const CandidateSegment &segment) { - return CheckSegmentDistance(input_coordinate, segment, max_distance); - }); - - return MakePhantomNodes(input_coordinate, results); - } - - // Returns nearest PhantomNodes in the given bearing range within max_distance. - // Does not filter by small/big component! - std::vector - NearestPhantomNodesInRange(const util::Coordinate input_coordinate, - const double max_distance, - const int bearing, - const int bearing_range, - const Approach approach, - const bool use_all_edges) const - { - auto results = rtree.Nearest( - input_coordinate, - [this, approach, &input_coordinate, bearing, bearing_range, use_all_edges]( - const CandidateSegment &segment) { - auto use_direction = - boolPairAnd(CheckSegmentBearing(segment, bearing, bearing_range), - boolPairAnd(HasValidEdge(segment, use_all_edges), - CheckSegmentExclude(segment))); - use_direction = - boolPairAnd(use_direction, CheckApproach(input_coordinate, segment, approach)); - return use_direction; - }, - [this, max_distance, input_coordinate](const std::size_t, - const CandidateSegment &segment) { - return CheckSegmentDistance(input_coordinate, segment, max_distance); - }); - - return MakePhantomNodes(input_coordinate, results); - } - - // Returns max_results nearest PhantomNodes in the given bearing range. - // Does not filter by small/big component! - std::vector - NearestPhantomNodes(const util::Coordinate input_coordinate, - const unsigned max_results, - const int bearing, - const int bearing_range, - const Approach approach) const - { - auto results = rtree.Nearest( - input_coordinate, - [this, approach, &input_coordinate, bearing, bearing_range]( - const CandidateSegment &segment) { - auto use_direction = - boolPairAnd(CheckSegmentBearing(segment, bearing, bearing_range), - boolPairAnd(HasValidEdge(segment), CheckSegmentExclude(segment))); - return boolPairAnd(use_direction, - CheckApproach(input_coordinate, segment, approach)); - }, - [max_results](const std::size_t num_results, const CandidateSegment &) { - return num_results >= max_results; - }); - - return MakePhantomNodes(input_coordinate, results); - } - - // Returns max_results nearest PhantomNodes in the given bearing range within the maximum - // distance. + // Returns max_results nearest PhantomNodes that are valid within the provided parameters. // Does not filter by small/big component! std::vector NearestPhantomNodes(const util::Coordinate input_coordinate, - const unsigned max_results, - const double max_distance, - const int bearing, - const int bearing_range, - const Approach approach) const + const Approach approach, + const boost::optional max_results, + const boost::optional max_distance, + const boost::optional bearing_with_range, + const boost::optional use_all_edges) const { auto results = rtree.Nearest( input_coordinate, - [this, approach, &input_coordinate, bearing, bearing_range]( + [this, approach, &input_coordinate, &bearing_with_range, &use_all_edges]( const CandidateSegment &segment) { - auto use_direction = - boolPairAnd(CheckSegmentBearing(segment, bearing, bearing_range), - boolPairAnd(HasValidEdge(segment), CheckSegmentExclude(segment))); - return boolPairAnd(use_direction, - CheckApproach(input_coordinate, segment, approach)); + auto valid = CheckSegmentExclude(segment) && + CheckApproach(input_coordinate, segment, approach) && + (use_all_edges ? HasValidEdge(segment, *use_all_edges) + : HasValidEdge(segment)) && + (bearing_with_range ? CheckSegmentBearing(segment, *bearing_with_range) + : std::make_pair(true, true)); + return valid; }, - [this, max_distance, max_results, input_coordinate](const std::size_t num_results, - const CandidateSegment &segment) { - return num_results >= max_results || - CheckSegmentDistance(input_coordinate, segment, max_distance); + [this, &max_distance, &max_results, input_coordinate](const std::size_t num_results, + const CandidateSegment &segment) { + return (max_results && num_results >= *max_results) || + (max_distance && + CheckSegmentDistance(input_coordinate, segment, *max_distance)); }); return MakePhantomNodes(input_coordinate, results); } - // Returns max_results nearest PhantomNodes. - // Does not filter by small/big component! - std::vector - NearestPhantomNodes(const util::Coordinate input_coordinate, - const unsigned max_results, - const Approach approach) const + // Returns a list of phantom node candidates from the nearest location that are valid + // within the provided parameters. If there is tie between equidistant locations, + // we only pick candidates from one location. + // If candidates do not include a node from a big component, an alternative list of candidates + // from the nearest location which has nodes from a big component is returned. + PhantomCandidateAlternatives NearestCandidatesWithAlternativeFromBigComponent( + const util::Coordinate input_coordinate, + const Approach approach, + const boost::optional max_distance, + const boost::optional bearing_with_range, + const boost::optional use_all_edges) const { - auto results = rtree.Nearest( - input_coordinate, - [this, approach, &input_coordinate](const CandidateSegment &segment) { - return boolPairAnd(boolPairAnd(HasValidEdge(segment), CheckSegmentExclude(segment)), - CheckApproach(input_coordinate, segment, approach)); - }, - [max_results](const std::size_t num_results, const CandidateSegment &) { - return num_results >= max_results; - }); - - return MakePhantomNodes(input_coordinate, results); - } - - // Returns max_results nearest PhantomNodes in the given max distance. - // Does not filter by small/big component! - std::vector - NearestPhantomNodes(const util::Coordinate input_coordinate, - const unsigned max_results, - const double max_distance, - const Approach approach) const - { - auto results = rtree.Nearest( - input_coordinate, - [this, approach, &input_coordinate](const CandidateSegment &segment) { - return boolPairAnd(boolPairAnd(HasValidEdge(segment), CheckSegmentExclude(segment)), - CheckApproach(input_coordinate, segment, approach)); - }, - [this, max_distance, max_results, input_coordinate](const std::size_t num_results, - const CandidateSegment &segment) { - return num_results >= max_results || - CheckSegmentDistance(input_coordinate, segment, max_distance); - }); - - return MakePhantomNodes(input_coordinate, results); - } - - // Returns the nearest phantom node. If this phantom node is not from a big component - // a second phantom node is return that is the nearest coordinate in a big component. - std::pair - NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate, - const double max_distance, - const Approach approach, - const bool use_all_edges) const - { - bool has_small_component = false; + bool has_nearest = false; bool has_big_component = false; + Coordinate big_component_coord; + double big_component_distance = std::numeric_limits::max(); + Coordinate nearest_coord; auto results = rtree.Nearest( input_coordinate, [this, approach, &input_coordinate, + &has_nearest, &has_big_component, - &has_small_component, - &use_all_edges](const CandidateSegment &segment) { - auto use_segment = - (!has_small_component || (!has_big_component && !IsTinyComponent(segment))); - auto use_directions = std::make_pair(use_segment, use_segment); - const auto valid_edges = HasValidEdge(segment, use_all_edges); - const auto admissible_segments = CheckSegmentExclude(segment); - use_directions = boolPairAnd(use_directions, admissible_segments); - use_directions = boolPairAnd(use_directions, valid_edges); - use_directions = - boolPairAnd(use_directions, CheckApproach(input_coordinate, segment, approach)); - - if (use_directions.first || use_directions.second) + &nearest_coord, + &big_component_coord, + &big_component_distance, + &use_all_edges, + &bearing_with_range](const CandidateSegment &segment) { + auto is_big_component = !IsTinyComponent(segment); + auto not_nearest = + has_nearest && segment.fixed_projected_coordinate != nearest_coord; + auto not_big = + has_big_component && segment.fixed_projected_coordinate != big_component_coord; + + /** + * + * Two reasons why we don't want this candidate: + * 1. A non-big component candidate that is not at the nearest location + * 2. A big component candidate that is not at the big location. + * + * It's possible that 1. could end up having the same location as the nearest big + * component node if we have yet to see one. However, we don't know this and it + * could lead to buffering large numbers of candidates before finding the big + * component location. + * By filtering out 1. nodes, this does mean that the alternative list of + * candidates will not have non-big component candidates. Given the alternative + * list of big component candidates is meant as a backup choice, this seems + * reasonable. + */ + if ((!is_big_component && not_nearest) || (is_big_component && not_big)) + { + return std::make_pair(false, false); + } + auto use_candidate = + CheckSegmentExclude(segment) && + CheckApproach(input_coordinate, segment, approach) && + (use_all_edges ? HasValidEdge(segment, *use_all_edges) + : HasValidEdge(segment)) && + (bearing_with_range ? CheckSegmentBearing(segment, *bearing_with_range) + : std::make_pair(true, true)); + + if (use_candidate.first || use_candidate.second) { - has_big_component = has_big_component || !IsTinyComponent(segment); - has_small_component = has_small_component || IsTinyComponent(segment); + if (!has_nearest) + { + has_nearest = true; + nearest_coord = segment.fixed_projected_coordinate; + } + if (is_big_component && !has_big_component) + { + has_big_component = true; + big_component_coord = segment.fixed_projected_coordinate; + big_component_distance = GetSegmentDistance(input_coordinate, segment); + } } - return use_directions; + return use_candidate; }, - [this, &has_big_component, max_distance, input_coordinate]( - const std::size_t num_results, const CandidateSegment &segment) { - return (num_results > 0 && has_big_component) || - CheckSegmentDistance(input_coordinate, segment, max_distance); + [this, &has_big_component, &max_distance, input_coordinate, &big_component_distance]( + const std::size_t /*num_results*/, const CandidateSegment &segment) { + auto distance = GetSegmentDistance(input_coordinate, segment); + auto further_than_big_component = distance > big_component_distance; + auto no_more_candidates = has_big_component && further_than_big_component; + auto too_far_away = max_distance && distance > *max_distance; + + // Time to terminate the search when: + // 1. We've found a node from a big component and the next candidate is further away + // than that node. + // 2. We're further away from the input then our max allowed distance. + return no_more_candidates || too_far_away; }); - if (results.size() == 0) - { - return std::make_pair(PhantomNode{}, PhantomNode{}); - } - - BOOST_ASSERT(results.size() == 1 || results.size() == 2); - return std::make_pair(MakePhantomNode(input_coordinate, results.front()).phantom_node, - MakePhantomNode(input_coordinate, results.back()).phantom_node); + return MakeAlternativeBigCandidates(input_coordinate, nearest_coord, results); } - // Returns the nearest phantom node. If this phantom node is not from a big component - // a second phantom node is return that is the nearest coordinate in a big component. - std::pair - NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate, - const Approach approach, - const bool use_all_edges) const + private: + PhantomCandidateAlternatives + MakeAlternativeBigCandidates(const util::Coordinate input_coordinate, + const Coordinate nearest_coord, + const std::vector &results) const { - bool has_small_component = false; - bool has_big_component = false; - auto results = rtree.Nearest( - input_coordinate, - [this, - approach, - &input_coordinate, - &has_big_component, - &has_small_component, - &use_all_edges](const CandidateSegment &segment) { - auto use_segment = - (!has_small_component || (!has_big_component && !IsTinyComponent(segment))); - auto use_directions = std::make_pair(use_segment, use_segment); - - const auto valid_edges = HasValidEdge(segment, use_all_edges); - const auto admissible_segments = CheckSegmentExclude(segment); - use_directions = boolPairAnd(use_directions, admissible_segments); - use_directions = boolPairAnd(use_directions, valid_edges); - use_directions = - boolPairAnd(use_directions, CheckApproach(input_coordinate, segment, approach)); - - if (use_directions.first || use_directions.second) - { - has_big_component = has_big_component || !IsTinyComponent(segment); - has_small_component = has_small_component || IsTinyComponent(segment); - } - - return use_directions; - }, - [&has_big_component](const std::size_t num_results, const CandidateSegment &) { - return num_results > 0 && has_big_component; - }); - if (results.size() == 0) { - return std::make_pair(PhantomNode{}, PhantomNode{}); + return std::make_pair(PhantomNodeCandidates{}, PhantomNodeCandidates{}); } - BOOST_ASSERT(results.size() == 1 || results.size() == 2); - return std::make_pair(MakePhantomNode(input_coordinate, results.front()).phantom_node, - MakePhantomNode(input_coordinate, results.back()).phantom_node); - } - - // Returns the nearest phantom node. If this phantom node is not from a big component - // a second phantom node is return that is the nearest coordinate in a big component. - std::pair - NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate, - const int bearing, - const int bearing_range, - const Approach approach, - const bool use_all_edges) const - { - bool has_small_component = false; - bool has_big_component = false; - auto results = rtree.Nearest( - input_coordinate, - [this, - approach, - &input_coordinate, - bearing, - bearing_range, - &has_big_component, - &has_small_component, - &use_all_edges](const CandidateSegment &segment) { - auto use_segment = - (!has_small_component || (!has_big_component && !IsTinyComponent(segment))); - auto use_directions = std::make_pair(use_segment, use_segment); - const auto admissible_segments = CheckSegmentExclude(segment); - - if (use_segment) + PhantomNodeCandidates nearest_phantoms; + PhantomNodeCandidates big_component_phantoms; + + const auto add_to_candidates = [this, &input_coordinate](PhantomNodeCandidates &candidates, + const EdgeData data) { + auto candidate_it = + std::find_if(candidates.begin(), candidates.end(), [&](const PhantomNode &node) { + return data.forward_segment_id.id == node.forward_segment_id.id && + data.reverse_segment_id.id == node.reverse_segment_id.id; + }); + if (candidate_it == candidates.end()) + { + // First candidate from this segment + candidates.push_back(MakePhantomNode(input_coordinate, data).phantom_node); + } + else + { + /** + * Second candidate from this segment (there can be at most two). + * We're snapping at the connection between two edges e1,e2 of the segment. + * + * | e1 | e2 | + * | --- f1 --> | --- f2 --> | + * | <-- r1 --- | <-- r2 --- | + * + * Most of the routing algorithms only support one candidate from each segment. + * Therefore, we have to choose between e1 and e2. + * + * It makes sense to pick one edge over another if that edge offers more + * opportunities to act as a source or target for a route. + * + * For consistency, we use the following logic: + * "Pick e1 unless it makes sense to choose e2" + * + * Representing edge enabled as a truth table: + * f1 | r1 | f2 | r2 | selected + * ____________________________ + * t | t | t | t | e1 + * t | t | t | f | e1 + * t | t | f | t | e1 + * t | f | t | t | e2 + * t | f | t | f | e1 + * t | f | f | t | e1 + * f | t | t | t | e2 + * f | t | t | f | e1 + * f | t | f | t | e1 + * + * The other rows in truth table don't appear as we discard an edge if both + * forward and reverse are disabled. + * + **/ + if (candidate_it->fwd_segment_position < data.fwd_segment_position) { - use_directions = - boolPairAnd(CheckSegmentBearing(segment, bearing, bearing_range), - HasValidEdge(segment, use_all_edges)); - use_directions = boolPairAnd(use_directions, admissible_segments); - use_directions = boolPairAnd( - use_directions, CheckApproach(input_coordinate, segment, approach)); - - if (use_directions.first || use_directions.second) + if (data.forward_segment_id.enabled && data.reverse_segment_id.enabled && + !(candidate_it->forward_segment_id.enabled && + candidate_it->reverse_segment_id.enabled)) { - has_big_component = has_big_component || !IsTinyComponent(segment); - has_small_component = has_small_component || IsTinyComponent(segment); + *candidate_it = MakePhantomNode(input_coordinate, data).phantom_node; } } - - return use_directions; - }, - [&has_big_component](const std::size_t num_results, const CandidateSegment &) { - return num_results > 0 && has_big_component; - }); - - if (results.size() == 0) - { - return std::make_pair(PhantomNode{}, PhantomNode{}); - } - - BOOST_ASSERT(results.size() > 0); - return std::make_pair(MakePhantomNode(input_coordinate, results.front()).phantom_node, - MakePhantomNode(input_coordinate, results.back()).phantom_node); - } - - // Returns the nearest phantom node. If this phantom node is not from a big component - // a second phantom node is return that is the nearest coordinate in a big component. - std::pair - NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate, - const double max_distance, - const int bearing, - const int bearing_range, - const Approach approach, - const bool use_all_edges) const - { - bool has_small_component = false; - bool has_big_component = false; - auto results = rtree.Nearest( - input_coordinate, - [this, - approach, - &input_coordinate, - bearing, - bearing_range, - &has_big_component, - &has_small_component, - &use_all_edges](const CandidateSegment &segment) { - auto use_segment = - (!has_small_component || (!has_big_component && !IsTinyComponent(segment))); - auto use_directions = std::make_pair(use_segment, use_segment); - const auto admissible_segments = CheckSegmentExclude(segment); - - if (use_segment) + else { - use_directions = - boolPairAnd(CheckSegmentBearing(segment, bearing, bearing_range), - HasValidEdge(segment, use_all_edges)); - use_directions = boolPairAnd(use_directions, admissible_segments); - use_directions = boolPairAnd( - use_directions, CheckApproach(input_coordinate, segment, approach)); - - if (use_directions.first || use_directions.second) + if (!candidate_it->forward_segment_id.enabled || + !candidate_it->reverse_segment_id.enabled || + (data.forward_segment_id.enabled && data.reverse_segment_id.enabled)) { - has_big_component = has_big_component || !IsTinyComponent(segment); - has_small_component = has_small_component || IsTinyComponent(segment); + *candidate_it = MakePhantomNode(input_coordinate, data).phantom_node; } } + } + }; - return use_directions; - }, - [this, &has_big_component, max_distance, input_coordinate]( - const std::size_t num_results, const CandidateSegment &segment) { - return (num_results > 0 && has_big_component) || - CheckSegmentDistance(input_coordinate, segment, max_distance); - }); - - if (results.size() == 0) - { - return std::make_pair(PhantomNode{}, PhantomNode{}); - } - - BOOST_ASSERT(results.size() > 0); - return std::make_pair(MakePhantomNode(input_coordinate, results.front()).phantom_node, - MakePhantomNode(input_coordinate, results.back()).phantom_node); + std::for_each(results.begin(), results.end(), [&](const CandidateSegment &segment) { + if (segment.fixed_projected_coordinate == nearest_coord) + { + add_to_candidates(nearest_phantoms, segment.data); + } + else + { + // Can only be from a big component for the alternative candidates + add_to_candidates(big_component_phantoms, segment.data); + } + }); + return std::make_pair(std::move(nearest_phantoms), std::move(big_component_phantoms)); } - private: std::vector MakePhantomNodes(const util::Coordinate input_coordinate, - const std::vector &results) const + const std::vector &results) const { std::vector distance_and_phantoms(results.size()); std::transform(results.begin(), results.end(), distance_and_phantoms.begin(), - [this, &input_coordinate](const EdgeData &data) { - return MakePhantomNode(input_coordinate, data); + [this, &input_coordinate](const CandidateSegment &segment) { + return MakePhantomNode(input_coordinate, segment.data); }); return distance_and_phantoms; } @@ -580,9 +432,8 @@ template class GeospatialQuery return transformed; } - bool CheckSegmentDistance(const Coordinate input_coordinate, - const CandidateSegment &segment, - const double max_distance) const + double GetSegmentDistance(const Coordinate input_coordinate, + const CandidateSegment &segment) const { BOOST_ASSERT(segment.data.forward_segment_id.id != SPECIAL_SEGMENTID || !segment.data.forward_segment_id.enabled); @@ -593,7 +444,14 @@ template class GeospatialQuery util::web_mercator::toWGS84(segment.fixed_projected_coordinate); return util::coordinate_calculation::greatCircleDistance(input_coordinate, - wsg84_coordinate) > max_distance; + wsg84_coordinate); + } + + bool CheckSegmentDistance(const Coordinate input_coordinate, + const CandidateSegment &segment, + const double max_distance) const + { + return GetSegmentDistance(input_coordinate, segment) > max_distance; } std::pair CheckSegmentExclude(const CandidateSegment &segment) const @@ -616,8 +474,7 @@ template class GeospatialQuery } std::pair CheckSegmentBearing(const CandidateSegment &segment, - const int filter_bearing, - const int filter_bearing_range) const + const Bearing filter_bearing) const { BOOST_ASSERT(segment.data.forward_segment_id.id != SPECIAL_SEGMENTID || !segment.data.forward_segment_id.enabled); @@ -633,11 +490,11 @@ template class GeospatialQuery const bool forward_bearing_valid = util::bearing::CheckInBounds( - std::round(forward_edge_bearing), filter_bearing, filter_bearing_range) && + std::round(forward_edge_bearing), filter_bearing.bearing, filter_bearing.range) && segment.data.forward_segment_id.enabled; const bool backward_bearing_valid = util::bearing::CheckInBounds( - std::round(backward_edge_bearing), filter_bearing, filter_bearing_range) && + std::round(backward_edge_bearing), filter_bearing.bearing, filter_bearing.range) && segment.data.reverse_segment_id.enabled; return std::make_pair(forward_bearing_valid, backward_bearing_valid); } @@ -645,7 +502,7 @@ template class GeospatialQuery /** * Checks to see if the edge weights are valid. We might have an edge, * but a traffic update might set the speed to 0 (weight == INVALID_SEGMENT_WEIGHT). - * which means that this edge is not currently traversible. If this is the case, + * which means that this edge is not currently traversable. If this is the case, * then we shouldn't snap to this edge. */ std::pair HasValidEdge(const CandidateSegment &segment, @@ -682,7 +539,7 @@ template class GeospatialQuery bool IsTinyComponent(const CandidateSegment &segment) const { const auto &data = segment.data; - BOOST_ASSERT(data.forward_segment_id.enabled); + BOOST_ASSERT(data.forward_segment_id.enabled || data.reverse_segment_id.enabled); BOOST_ASSERT(data.forward_segment_id.id != SPECIAL_NODEID); return datafacade.GetComponentID(data.forward_segment_id.id).is_tiny; } diff --git a/include/engine/hint.hpp b/include/engine/hint.hpp index 50e7cdb1671..58b80184047 100644 --- a/include/engine/hint.hpp +++ b/include/engine/hint.hpp @@ -47,8 +47,9 @@ namespace datafacade class BaseDataFacade; } -// Is returned as a temporary identifier for snapped coodinates -struct Hint +// SegmentHint represents an individual segment position that could be used +// as the waypoint for a given input location +struct SegmentHint { PhantomNode phantom; std::uint32_t data_checksum; @@ -57,16 +58,31 @@ struct Hint const datafacade::BaseDataFacade &facade) const; std::string ToBase64() const; - static Hint FromBase64(const std::string &base64Hint); + static SegmentHint FromBase64(const std::string &base64Hint); - friend bool operator==(const Hint &, const Hint &); - friend std::ostream &operator<<(std::ostream &, const Hint &); + friend bool operator==(const SegmentHint &, const SegmentHint &); + friend bool operator!=(const SegmentHint &, const SegmentHint &); + friend std::ostream &operator<<(std::ostream &, const SegmentHint &); }; -static_assert(sizeof(Hint) == 80 + 4, "Hint is bigger than expected"); -constexpr std::size_t ENCODED_HINT_SIZE = 112; -static_assert(ENCODED_HINT_SIZE / 4 * 3 >= sizeof(Hint), - "ENCODED_HINT_SIZE does not match size of Hint"); +// Hint represents the suggested segment positions that could be used +// as the waypoint for a given input location +struct Hint +{ + std::vector segment_hints; + + bool IsValid(const util::Coordinate new_input_coordinates, + const datafacade::BaseDataFacade &facade) const; + + std::string ToBase64() const; + static Hint FromBase64(const std::string &base64Hint); +}; + +static_assert(sizeof(SegmentHint) == 80 + 4, "Hint is bigger than expected"); +constexpr std::size_t ENCODED_SEGMENT_HINT_SIZE = 112; +static_assert(ENCODED_SEGMENT_HINT_SIZE / 4 * 3 >= sizeof(SegmentHint), + "ENCODED_SEGMENT_HINT_SIZE does not match size of SegmentHint"); + } // namespace engine } // namespace osrm diff --git a/include/engine/internal_route_result.hpp b/include/engine/internal_route_result.hpp index 4ee80ab6a35..6962c9c5d08 100644 --- a/include/engine/internal_route_result.hpp +++ b/include/engine/internal_route_result.hpp @@ -50,7 +50,7 @@ struct PathData struct InternalRouteResult { std::vector> unpacked_path_segments; - std::vector segment_end_coordinates; + std::vector leg_endpoints; std::vector source_traversed_in_reverse; std::vector target_traversed_in_reverse; EdgeWeight shortest_path_weight = INVALID_EDGE_WEIGHT; @@ -96,7 +96,7 @@ inline InternalRouteResult CollapseInternalRouteResult(const InternalRouteResult if (leggy_result.unpacked_path_segments.size() == 1) return leggy_result; - BOOST_ASSERT(leggy_result.segment_end_coordinates.size() > 1); + BOOST_ASSERT(leggy_result.leg_endpoints.size() > 1); InternalRouteResult collapsed; collapsed.shortest_path_weight = leggy_result.shortest_path_weight; @@ -107,7 +107,7 @@ inline InternalRouteResult CollapseInternalRouteResult(const InternalRouteResult // start another leg vector collapsed.unpacked_path_segments.push_back(leggy_result.unpacked_path_segments[i]); // save new phantom node pair - collapsed.segment_end_coordinates.push_back(leggy_result.segment_end_coordinates[i]); + collapsed.leg_endpoints.push_back(leggy_result.leg_endpoints[i]); // save data about phantom nodes collapsed.source_traversed_in_reverse.push_back( leggy_result.source_traversed_in_reverse[i]); @@ -119,9 +119,9 @@ inline InternalRouteResult CollapseInternalRouteResult(const InternalRouteResult { BOOST_ASSERT(!collapsed.unpacked_path_segments.empty()); auto &last_segment = collapsed.unpacked_path_segments.back(); - BOOST_ASSERT(!collapsed.segment_end_coordinates.empty()); - collapsed.segment_end_coordinates.back().target_phantom = - leggy_result.segment_end_coordinates[i].target_phantom; + BOOST_ASSERT(!collapsed.leg_endpoints.empty()); + collapsed.leg_endpoints.back().target_phantom = + leggy_result.leg_endpoints[i].target_phantom; collapsed.target_traversed_in_reverse.back() = leggy_result.target_traversed_in_reverse[i]; // copy path segments into current leg @@ -138,19 +138,18 @@ inline InternalRouteResult CollapseInternalRouteResult(const InternalRouteResult last_segment[old_size].weight_until_turn += leggy_result.source_traversed_in_reverse[i] - ? leggy_result.segment_end_coordinates[i].source_phantom.reverse_weight - : leggy_result.segment_end_coordinates[i].source_phantom.forward_weight; + ? leggy_result.leg_endpoints[i].source_phantom.reverse_weight + : leggy_result.leg_endpoints[i].source_phantom.forward_weight; last_segment[old_size].duration_until_turn += leggy_result.source_traversed_in_reverse[i] - ? leggy_result.segment_end_coordinates[i].source_phantom.reverse_duration - : leggy_result.segment_end_coordinates[i].source_phantom.forward_duration; + ? leggy_result.leg_endpoints[i].source_phantom.reverse_duration + : leggy_result.leg_endpoints[i].source_phantom.forward_duration; } } } - BOOST_ASSERT(collapsed.segment_end_coordinates.size() == - collapsed.unpacked_path_segments.size()); + BOOST_ASSERT(collapsed.leg_endpoints.size() == collapsed.unpacked_path_segments.size()); return collapsed; } } // namespace engine diff --git a/include/engine/phantom_node.hpp b/include/engine/phantom_node.hpp index d535283d336..5d6c0983924 100644 --- a/include/engine/phantom_node.hpp +++ b/include/engine/phantom_node.hpp @@ -28,6 +28,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef OSRM_ENGINE_PHANTOM_NODES_H #define OSRM_ENGINE_PHANTOM_NODES_H +#include + #include "extractor/travel_mode.hpp" #include "util/bearing.hpp" @@ -223,7 +225,8 @@ struct PhantomNode static_assert(sizeof(PhantomNode) == 80, "PhantomNode has more padding then expected"); -using PhantomNodePair = std::pair; +using PhantomNodeCandidates = std::vector; +using PhantomCandidateAlternatives = std::pair; struct PhantomNodeWithDistance { @@ -231,11 +234,44 @@ struct PhantomNodeWithDistance double distance; }; -struct PhantomNodes +struct PhantomEndpointCandidates +{ + const PhantomNodeCandidates &source_phantoms; + const PhantomNodeCandidates &target_phantoms; +}; + +struct PhantomCandidatesToTarget +{ + const PhantomNodeCandidates &source_phantoms; + const PhantomNode &target_phantom; +}; + +inline util::Coordinate candidatesSnappedLocation(const PhantomNodeCandidates &candidates) +{ + BOOST_ASSERT(!candidates.empty()); + return candidates.front().location; +} + +inline util::Coordinate candidatesInputLocation(const PhantomNodeCandidates &candidates) +{ + BOOST_ASSERT(!candidates.empty()); + return candidates.front().input_location; +} + +inline bool candidatesHaveComponent(const PhantomNodeCandidates &candidates, uint32_t component_id) +{ + return std::any_of( + candidates.begin(), candidates.end(), [component_id](const PhantomNode &node) { + return node.component.id == component_id; + }); +} + +struct PhantomEndpoints { PhantomNode source_phantom; PhantomNode target_phantom; }; + } // namespace engine } // namespace osrm diff --git a/include/engine/plugins/plugin_base.hpp b/include/engine/plugins/plugin_base.hpp index b437b1a6c1e..11700e52b5e 100644 --- a/include/engine/plugins/plugin_base.hpp +++ b/include/engine/plugins/plugin_base.hpp @@ -99,64 +99,67 @@ class BasePlugin return Status::Error; } - // Decides whether to use the phantom node from a big or small component if both are found. - // Returns true if all phantom nodes are in the same component after snapping. - std::vector - SnapPhantomNodes(const std::vector &phantom_node_pair_list) const + // Decides whether to use the phantom candidates from big or small components if both are found. + std::vector + SnapPhantomNodes(std::vector alternatives_list) const { - const auto check_component_id_is_tiny = - [](const std::pair &phantom_pair) { - return phantom_pair.first.component.is_tiny; - }; - // are all phantoms from a tiny cc? - const auto check_all_in_same_component = - [](const std::vector> &nodes) { - const auto component_id = nodes.front().first.component.id; - - return std::all_of(std::begin(nodes), - std::end(nodes), - [component_id](const PhantomNodePair &phantom_pair) { - return component_id == phantom_pair.first.component.id; - }); - }; - - const auto fallback_to_big_component = - [](const std::pair &phantom_pair) { - if (phantom_pair.first.component.is_tiny && phantom_pair.second.IsValid() && - !phantom_pair.second.component.is_tiny) - { - return phantom_pair.second; - } - return phantom_pair.first; + const auto all_in_same_tiny_component = + [](const std::vector &alts_list) { + return std::any_of( + alts_list.front().first.begin(), + alts_list.front().first.end(), + // For each of the first possible phantoms, check if all other + // positions in the list have a phantom from the same small component. + [&](const PhantomNode &phantom) { + if (!phantom.component.is_tiny) + { + return false; + } + const auto component_id = phantom.component.id; + return std::all_of( + std::next(alts_list.begin()), + std::end(alts_list), + [component_id](const PhantomCandidateAlternatives &alternatives) { + return candidatesHaveComponent(alternatives.first, component_id); + }); + }); }; - const auto use_closed_phantom = - [](const std::pair &phantom_pair) { - return phantom_pair.first; - }; + // Move the alternative into the final list + const auto fallback_to_big_component = [](PhantomCandidateAlternatives &alternatives) { + auto no_big_alternative = alternatives.second.empty(); + return no_big_alternative ? std::move(alternatives.first) + : std::move(alternatives.second); + }; - const bool every_phantom_is_in_tiny_cc = std::all_of(std::begin(phantom_node_pair_list), - std::end(phantom_node_pair_list), - check_component_id_is_tiny); - auto all_in_same_component = check_all_in_same_component(phantom_node_pair_list); + // Move the alternative into the final list + const auto use_closed_phantom = [](PhantomCandidateAlternatives &alternatives) { + return std::move(alternatives.first); + }; - std::vector snapped_phantoms; - snapped_phantoms.reserve(phantom_node_pair_list.size()); + const auto no_alternatives = + std::all_of(alternatives_list.begin(), + alternatives_list.end(), + [](const PhantomCandidateAlternatives &alternatives) { + return alternatives.second.empty(); + }); + std::vector snapped_phantoms; + snapped_phantoms.reserve(alternatives_list.size()); // The only case we don't snap to the big component if all phantoms are in the same small // component - if (every_phantom_is_in_tiny_cc && all_in_same_component) + if (no_alternatives || all_in_same_tiny_component(alternatives_list)) { - std::transform(phantom_node_pair_list.begin(), - phantom_node_pair_list.end(), + std::transform(alternatives_list.begin(), + alternatives_list.end(), std::back_inserter(snapped_phantoms), use_closed_phantom); } else { - std::transform(phantom_node_pair_list.begin(), - phantom_node_pair_list.end(), + std::transform(alternatives_list.begin(), + alternatives_list.end(), std::back_inserter(snapped_phantoms), fallback_to_big_component); } @@ -181,35 +184,26 @@ class BasePlugin for (const auto i : util::irange(0UL, parameters.coordinates.size())) { - Approach approach = engine::Approach::UNRESTRICTED; - if (use_approaches && parameters.approaches[i]) - approach = parameters.approaches[i].get(); - - if (use_hints && parameters.hints[i] && + if (use_hints && parameters.hints[i] && !parameters.hints[i]->segment_hints.empty() && parameters.hints[i]->IsValid(parameters.coordinates[i], facade)) { - phantom_nodes[i].push_back(PhantomNodeWithDistance{ - parameters.hints[i]->phantom, - util::coordinate_calculation::greatCircleDistance( - parameters.coordinates[i], parameters.hints[i]->phantom.location), - }); + for (const auto &seg_hint : parameters.hints[i]->segment_hints) + { + phantom_nodes[i].push_back(PhantomNodeWithDistance{ + seg_hint.phantom, + util::coordinate_calculation::greatCircleDistance( + parameters.coordinates[i], seg_hint.phantom.location)}); + } continue; } - if (use_bearings && parameters.bearings[i]) - { - phantom_nodes[i] = - facade.NearestPhantomNodesInRange(parameters.coordinates[i], - radiuses[i], - parameters.bearings[i]->bearing, - parameters.bearings[i]->range, - approach, - use_all_edges); - } - else - { - phantom_nodes[i] = facade.NearestPhantomNodesInRange( - parameters.coordinates[i], radiuses[i], approach, use_all_edges); - } + + phantom_nodes[i] = facade.NearestPhantomNodesInRange( + parameters.coordinates[i], + radiuses[i], + use_bearings ? parameters.bearings[i] : boost::none, + use_approaches && parameters.approaches[i] ? parameters.approaches[i].get() + : engine::Approach::UNRESTRICTED, + use_all_edges); } return phantom_nodes; @@ -218,7 +212,7 @@ class BasePlugin std::vector> GetPhantomNodes(const datafacade::BaseDataFacade &facade, const api::BaseParameters ¶meters, - unsigned number_of_results) const + size_t number_of_results) const { std::vector> phantom_nodes( parameters.coordinates.size()); @@ -231,57 +225,27 @@ class BasePlugin BOOST_ASSERT(parameters.IsValid()); for (const auto i : util::irange(0UL, parameters.coordinates.size())) { - Approach approach = engine::Approach::UNRESTRICTED; - if (use_approaches && parameters.approaches[i]) - approach = parameters.approaches[i].get(); - - if (use_hints && parameters.hints[i] && + if (use_hints && parameters.hints[i] && !parameters.hints[i]->segment_hints.empty() && parameters.hints[i]->IsValid(parameters.coordinates[i], facade)) { - phantom_nodes[i].push_back(PhantomNodeWithDistance{ - parameters.hints[i]->phantom, - util::coordinate_calculation::greatCircleDistance( - parameters.coordinates[i], parameters.hints[i]->phantom.location), - }); - continue; - } - - if (use_bearings && parameters.bearings[i]) - { - if (use_radiuses && parameters.radiuses[i]) - { - phantom_nodes[i] = facade.NearestPhantomNodes(parameters.coordinates[i], - number_of_results, - *parameters.radiuses[i], - parameters.bearings[i]->bearing, - parameters.bearings[i]->range, - approach); - } - else + for (const auto &seg_hint : parameters.hints[i]->segment_hints) { - phantom_nodes[i] = facade.NearestPhantomNodes(parameters.coordinates[i], - number_of_results, - parameters.bearings[i]->bearing, - parameters.bearings[i]->range, - approach); - } - } - else - { - if (use_radiuses && parameters.radiuses[i]) - { - phantom_nodes[i] = facade.NearestPhantomNodes(parameters.coordinates[i], - number_of_results, - *parameters.radiuses[i], - approach); - } - else - { - phantom_nodes[i] = facade.NearestPhantomNodes( - parameters.coordinates[i], number_of_results, approach); + phantom_nodes[i].push_back(PhantomNodeWithDistance{ + seg_hint.phantom, + util::coordinate_calculation::greatCircleDistance( + parameters.coordinates[i], seg_hint.phantom.location)}); } + continue; } + phantom_nodes[i] = facade.NearestPhantomNodes( + parameters.coordinates[i], + number_of_results, + use_radiuses ? parameters.radiuses[i] : boost::none, + use_bearings ? parameters.bearings[i] : boost::none, + use_approaches && parameters.approaches[i] ? parameters.approaches[i].get() + : engine::Approach::UNRESTRICTED); + // we didn't find a fitting node, return error if (phantom_nodes[i].empty()) { @@ -291,10 +255,11 @@ class BasePlugin return phantom_nodes; } - std::vector GetPhantomNodes(const datafacade::BaseDataFacade &facade, - const api::BaseParameters ¶meters) const + std::vector + GetPhantomNodes(const datafacade::BaseDataFacade &facade, + const api::BaseParameters ¶meters) const { - std::vector phantom_node_pairs(parameters.coordinates.size()); + std::vector alternatives(parameters.coordinates.size()); const bool use_hints = !parameters.hints.empty(); const bool use_bearings = !parameters.bearings.empty(); @@ -305,87 +270,57 @@ class BasePlugin BOOST_ASSERT(parameters.IsValid()); for (const auto i : util::irange(0UL, parameters.coordinates.size())) { - Approach approach = engine::Approach::UNRESTRICTED; - if (use_approaches && parameters.approaches[i]) - approach = parameters.approaches[i].get(); - - if (use_hints && parameters.hints[i] && + if (use_hints && parameters.hints[i] && !parameters.hints[i]->segment_hints.empty() && parameters.hints[i]->IsValid(parameters.coordinates[i], facade)) { - phantom_node_pairs[i].first = parameters.hints[i]->phantom; + std::transform(parameters.hints[i]->segment_hints.begin(), + parameters.hints[i]->segment_hints.end(), + std::back_inserter(alternatives[i].first), + [](const auto &seg_hint) { return seg_hint.phantom; }); // we don't set the second one - it will be marked as invalid continue; } - if (use_bearings && parameters.bearings[i]) - { - if (use_radiuses && parameters.radiuses[i]) - { - phantom_node_pairs[i] = - facade.NearestPhantomNodeWithAlternativeFromBigComponent( - parameters.coordinates[i], - *parameters.radiuses[i], - parameters.bearings[i]->bearing, - parameters.bearings[i]->range, - approach, - use_all_edges); - } - else - { - phantom_node_pairs[i] = - facade.NearestPhantomNodeWithAlternativeFromBigComponent( - parameters.coordinates[i], - parameters.bearings[i]->bearing, - parameters.bearings[i]->range, - approach, - use_all_edges); - } - } - else - { - if (use_radiuses && parameters.radiuses[i]) - { - phantom_node_pairs[i] = - facade.NearestPhantomNodeWithAlternativeFromBigComponent( - parameters.coordinates[i], - *parameters.radiuses[i], - approach, - use_all_edges); - } - else - { - phantom_node_pairs[i] = - facade.NearestPhantomNodeWithAlternativeFromBigComponent( - parameters.coordinates[i], approach, use_all_edges); - } - } + alternatives[i] = facade.NearestCandidatesWithAlternativeFromBigComponent( + parameters.coordinates[i], + use_radiuses ? parameters.radiuses[i] : boost::none, + use_bearings ? parameters.bearings[i] : boost::none, + use_approaches && parameters.approaches[i] ? parameters.approaches[i].get() + : engine::Approach::UNRESTRICTED, + use_all_edges); // we didn't find a fitting node, return error - if (!phantom_node_pairs[i].first.IsValid()) + if (alternatives[i].first.empty()) { // This ensures the list of phantom nodes only consists of valid nodes. // We can use this on the call-site to detect an error. - phantom_node_pairs.pop_back(); + alternatives.pop_back(); break; } - BOOST_ASSERT(phantom_node_pairs[i].first.IsValid()); - BOOST_ASSERT(phantom_node_pairs[i].second.IsValid()); + + BOOST_ASSERT(!alternatives[i].first.empty()); } - return phantom_node_pairs; + return alternatives; } - std::string MissingPhantomErrorMessage(const std::vector &phantom_nodes, - const std::vector &coordinates) const + std::string + MissingPhantomErrorMessage(const std::vector &alternatives, + const std::vector &coordinates) const { - BOOST_ASSERT(phantom_nodes.size() < coordinates.size()); - auto mismatch = std::mismatch(phantom_nodes.begin(), - phantom_nodes.end(), - coordinates.begin(), - coordinates.end(), - [](const auto &phantom_node, const auto &coordinate) { - return phantom_node.first.input_location == coordinate; - }); - std::size_t missing_index = std::distance(phantom_nodes.begin(), mismatch.first); + BOOST_ASSERT(alternatives.size() < coordinates.size()); + auto mismatch = + std::mismatch(alternatives.begin(), + alternatives.end(), + coordinates.begin(), + coordinates.end(), + [](const auto &candidates_pair, const auto &coordinate) { + return std::any_of(candidates_pair.first.begin(), + candidates_pair.first.end(), + [&](const auto &phantom) { + return phantom.input_location == coordinate; + }); + }); + std::size_t missing_index = std::distance(alternatives.begin(), mismatch.first); return std::string("Could not find a matching segment for coordinate ") + std::to_string(missing_index); } diff --git a/include/engine/plugins/trip.hpp b/include/engine/plugins/trip.hpp index 2cf9da4dec6..cd8ffb5a48a 100644 --- a/include/engine/plugins/trip.hpp +++ b/include/engine/plugins/trip.hpp @@ -31,7 +31,7 @@ class TripPlugin final : public BasePlugin const int max_locations_trip; InternalRouteResult ComputeRoute(const RoutingAlgorithmsInterface &algorithms, - const std::vector &phantom_node_list, + const std::vector &candidates_list, const std::vector &trip, const bool roundtrip) const; diff --git a/include/engine/routing_algorithms.hpp b/include/engine/routing_algorithms.hpp index 8bedcb8d861..6f41b0be334 100644 --- a/include/engine/routing_algorithms.hpp +++ b/include/engine/routing_algorithms.hpp @@ -20,18 +20,18 @@ class RoutingAlgorithmsInterface { public: virtual InternalManyRoutesResult - AlternativePathSearch(const PhantomNodes &phantom_node_pair, + AlternativePathSearch(const PhantomEndpointCandidates &endpoint_candidates, unsigned number_of_alternatives) const = 0; virtual InternalRouteResult - ShortestPathSearch(const std::vector &phantom_node_pair, + ShortestPathSearch(const std::vector &waypoint_candidates, const boost::optional continue_straight_at_waypoint) const = 0; virtual InternalRouteResult - DirectShortestPathSearch(const PhantomNodes &phantom_node_pair) const = 0; + DirectShortestPathSearch(const PhantomEndpointCandidates &endpoint_candidates) const = 0; virtual std::pair, std::vector> - ManyToManySearch(const std::vector &phantom_nodes, + ManyToManySearch(const std::vector &candidates_list, const std::vector &source_indices, const std::vector &target_indices, const bool calculate_distance) const = 0; @@ -73,18 +73,18 @@ template class RoutingAlgorithms final : public RoutingAlgo virtual ~RoutingAlgorithms() = default; InternalManyRoutesResult - AlternativePathSearch(const PhantomNodes &phantom_node_pair, + AlternativePathSearch(const PhantomEndpointCandidates &endpoint_candidates, unsigned number_of_alternatives) const final override; InternalRouteResult ShortestPathSearch( - const std::vector &phantom_node_pair, + const std::vector &waypoint_candidates, const boost::optional continue_straight_at_waypoint) const final override; - InternalRouteResult - DirectShortestPathSearch(const PhantomNodes &phantom_nodes) const final override; + InternalRouteResult DirectShortestPathSearch( + const PhantomEndpointCandidates &endpoint_candidates) const final override; - virtual std::pair, std::vector> - ManyToManySearch(const std::vector &phantom_nodes, + std::pair, std::vector> + ManyToManySearch(const std::vector &candidates_list, const std::vector &source_indices, const std::vector &target_indices, const bool calculate_distance) const final override; @@ -150,28 +150,27 @@ template class RoutingAlgorithms final : public RoutingAlgo }; template -InternalManyRoutesResult -RoutingAlgorithms::AlternativePathSearch(const PhantomNodes &phantom_node_pair, - unsigned number_of_alternatives) const +InternalManyRoutesResult RoutingAlgorithms::AlternativePathSearch( + const PhantomEndpointCandidates &endpoint_candidates, unsigned number_of_alternatives) const { return routing_algorithms::alternativePathSearch( - heaps, *facade, phantom_node_pair, number_of_alternatives); + heaps, *facade, endpoint_candidates, number_of_alternatives); } template InternalRouteResult RoutingAlgorithms::ShortestPathSearch( - const std::vector &phantom_node_pair, + const std::vector &waypoint_candidates, const boost::optional continue_straight_at_waypoint) const { return routing_algorithms::shortestPathSearch( - heaps, *facade, phantom_node_pair, continue_straight_at_waypoint); + heaps, *facade, waypoint_candidates, continue_straight_at_waypoint); } template -InternalRouteResult -RoutingAlgorithms::DirectShortestPathSearch(const PhantomNodes &phantom_nodes) const +InternalRouteResult RoutingAlgorithms::DirectShortestPathSearch( + const PhantomEndpointCandidates &endpoint_candidates) const { - return routing_algorithms::directShortestPathSearch(heaps, *facade, phantom_nodes); + return routing_algorithms::directShortestPathSearch(heaps, *facade, endpoint_candidates); } template @@ -193,30 +192,31 @@ inline routing_algorithms::SubMatchingList RoutingAlgorithms::MapMatc template std::pair, std::vector> -RoutingAlgorithms::ManyToManySearch(const std::vector &phantom_nodes, - const std::vector &_source_indices, - const std::vector &_target_indices, - const bool calculate_distance) const +RoutingAlgorithms::ManyToManySearch( + const std::vector &candidates_list, + const std::vector &_source_indices, + const std::vector &_target_indices, + const bool calculate_distance) const { - BOOST_ASSERT(!phantom_nodes.empty()); + BOOST_ASSERT(!candidates_list.empty()); auto source_indices = _source_indices; auto target_indices = _target_indices; if (source_indices.empty()) { - source_indices.resize(phantom_nodes.size()); + source_indices.resize(candidates_list.size()); std::iota(source_indices.begin(), source_indices.end(), 0); } if (target_indices.empty()) { - target_indices.resize(phantom_nodes.size()); + target_indices.resize(candidates_list.size()); std::iota(target_indices.begin(), target_indices.end(), 0); } return routing_algorithms::manyToManySearch(heaps, *facade, - phantom_nodes, + candidates_list, std::move(source_indices), std::move(target_indices), calculate_distance); diff --git a/include/engine/routing_algorithms/alternative_path.hpp b/include/engine/routing_algorithms/alternative_path.hpp index a7801349b9d..25cc339ebc0 100644 --- a/include/engine/routing_algorithms/alternative_path.hpp +++ b/include/engine/routing_algorithms/alternative_path.hpp @@ -18,12 +18,12 @@ namespace routing_algorithms InternalManyRoutesResult alternativePathSearch(SearchEngineData &search_engine_data, const DataFacade &facade, - const PhantomNodes &phantom_node_pair, + const PhantomEndpointCandidates &endpoint_candidates, unsigned number_of_alternatives); InternalManyRoutesResult alternativePathSearch(SearchEngineData &search_engine_data, const DataFacade &facade, - const PhantomNodes &phantom_node_pair, + const PhantomEndpointCandidates &endpoint_candidates, unsigned number_of_alternatives); } // namespace routing_algorithms diff --git a/include/engine/routing_algorithms/direct_shortest_path.hpp b/include/engine/routing_algorithms/direct_shortest_path.hpp index 055e789a59c..a6bb44df223 100644 --- a/include/engine/routing_algorithms/direct_shortest_path.hpp +++ b/include/engine/routing_algorithms/direct_shortest_path.hpp @@ -24,7 +24,7 @@ namespace routing_algorithms template InternalRouteResult directShortestPathSearch(SearchEngineData &engine_working_data, const DataFacade &facade, - const PhantomNodes &phantom_nodes); + const PhantomEndpointCandidates &endpoint_candidates); } // namespace routing_algorithms } // namespace engine diff --git a/include/engine/routing_algorithms/many_to_many.hpp b/include/engine/routing_algorithms/many_to_many.hpp index ddd861f0602..96d4f973d1b 100644 --- a/include/engine/routing_algorithms/many_to_many.hpp +++ b/include/engine/routing_algorithms/many_to_many.hpp @@ -94,7 +94,7 @@ template std::pair, std::vector> manyToManySearch(SearchEngineData &engine_working_data, const DataFacade &facade, - const std::vector &phantom_nodes, + const std::vector &candidates_list, const std::vector &source_indices, const std::vector &target_indices, const bool calculate_distance); diff --git a/include/engine/routing_algorithms/routing_base.hpp b/include/engine/routing_algorithms/routing_base.hpp index 59a0113fe8e..a416a4429cc 100644 --- a/include/engine/routing_algorithms/routing_base.hpp +++ b/include/engine/routing_algorithms/routing_base.hpp @@ -34,20 +34,12 @@ namespace engine namespace routing_algorithms { -static constexpr bool FORWARD_DIRECTION = true; -static constexpr bool REVERSE_DIRECTION = false; -static constexpr bool DO_NOT_FORCE_LOOPS = false; - -bool needsLoopForward(const PhantomNode &source_phantom, const PhantomNode &target_phantom); -bool needsLoopBackwards(const PhantomNode &source_phantom, const PhantomNode &target_phantom); - -bool needsLoopForward(const PhantomNodes &phantoms); -bool needsLoopBackwards(const PhantomNodes &phantoms); +namespace details +{ template -void insertNodesInHeaps(Heap &forward_heap, Heap &reverse_heap, const PhantomNodes &nodes) +void insertSourceInForwardHeap(Heap &forward_heap, const PhantomNode &source) { - const auto &source = nodes.source_phantom; if (source.IsValidForwardSource()) { forward_heap.Insert(source.forward_segment_id.id, @@ -61,8 +53,11 @@ void insertNodesInHeaps(Heap &forward_heap, Heap &reverse_heap, const PhantomNod -source.GetReverseWeightPlusOffset(), source.reverse_segment_id.id); } +} - const auto &target = nodes.target_phantom; +template +void insertTargetInReverseHeap(Heap &reverse_heap, const PhantomNode &target) +{ if (target.IsValidForwardTarget()) { reverse_heap.Insert(target.forward_segment_id.id, @@ -77,52 +72,104 @@ void insertNodesInHeaps(Heap &forward_heap, Heap &reverse_heap, const PhantomNod target.reverse_segment_id.id); } } +} // namespace details +static constexpr bool FORWARD_DIRECTION = true; +static constexpr bool REVERSE_DIRECTION = false; + +// Identify nodes in the forward(reverse) search direction that will require loop forcing +// e.g. if source and destination nodes are on the same segment. +std::vector getForwardLoopNodes(const PhantomEndpointCandidates &candidates); +std::vector getForwardLoopNodes(const PhantomCandidatesToTarget &candidates); +std::vector getBackwardLoopNodes(const PhantomEndpointCandidates &candidates); +std::vector getBackwardLoopNodes(const PhantomCandidatesToTarget &candidates); + +// Find the specific phantom node endpoints for a given path from a list of candidates. +PhantomEndpoints endpointsFromCandidates(const PhantomEndpointCandidates &candidates, + const std::vector &path); + +template +inline bool force_loop(const std::vector &force_nodes, const HeapNodeT &heap_node) +{ + // if loops are forced, they are so at the source + return !force_nodes.empty() && + std::find(force_nodes.begin(), force_nodes.end(), heap_node.node) != force_nodes.end() && + heap_node.data.parent == heap_node.node; +} -template -void insertSourceInHeap(ManyToManyQueryHeap &heap, const PhantomNode &phantom_node) +template +void insertNodesInHeaps(Heap &forward_heap, Heap &reverse_heap, const PhantomEndpoints &endpoints) { - if (phantom_node.IsValidForwardSource()) + details::insertSourceInForwardHeap(forward_heap, endpoints.source_phantom); + details::insertTargetInReverseHeap(reverse_heap, endpoints.target_phantom); +} + +template +void insertNodesInHeaps(Heap &forward_heap, + Heap &reverse_heap, + const PhantomEndpointCandidates &endpoint_candidates) +{ + for (const auto &source : endpoint_candidates.source_phantoms) { - heap.Insert(phantom_node.forward_segment_id.id, - -phantom_node.GetForwardWeightPlusOffset(), - {phantom_node.forward_segment_id.id, - -phantom_node.GetForwardDuration(), - -phantom_node.GetForwardDistance()}); + details::insertSourceInForwardHeap(forward_heap, source); } - if (phantom_node.IsValidReverseSource()) + + for (const auto &target : endpoint_candidates.target_phantoms) { - heap.Insert(phantom_node.reverse_segment_id.id, - -phantom_node.GetReverseWeightPlusOffset(), - {phantom_node.reverse_segment_id.id, - -phantom_node.GetReverseDuration(), - -phantom_node.GetReverseDistance()}); + details::insertTargetInReverseHeap(reverse_heap, target); } } template -void insertTargetInHeap(ManyToManyQueryHeap &heap, const PhantomNode &phantom_node) +void insertSourceInHeap(ManyToManyQueryHeap &heap, const PhantomNodeCandidates &source_candidates) { - if (phantom_node.IsValidForwardTarget()) + for (const auto &phantom_node : source_candidates) { - heap.Insert(phantom_node.forward_segment_id.id, - phantom_node.GetForwardWeightPlusOffset(), - {phantom_node.forward_segment_id.id, - phantom_node.GetForwardDuration(), - phantom_node.GetForwardDistance()}); + if (phantom_node.IsValidForwardSource()) + { + heap.Insert(phantom_node.forward_segment_id.id, + -phantom_node.GetForwardWeightPlusOffset(), + {phantom_node.forward_segment_id.id, + -phantom_node.GetForwardDuration(), + -phantom_node.GetForwardDistance()}); + } + if (phantom_node.IsValidReverseSource()) + { + heap.Insert(phantom_node.reverse_segment_id.id, + -phantom_node.GetReverseWeightPlusOffset(), + {phantom_node.reverse_segment_id.id, + -phantom_node.GetReverseDuration(), + -phantom_node.GetReverseDistance()}); + } } - if (phantom_node.IsValidReverseTarget()) +} + +template +void insertTargetInHeap(ManyToManyQueryHeap &heap, const PhantomNodeCandidates &target_candidates) +{ + for (const auto &phantom_node : target_candidates) { - heap.Insert(phantom_node.reverse_segment_id.id, - phantom_node.GetReverseWeightPlusOffset(), - {phantom_node.reverse_segment_id.id, - phantom_node.GetReverseDuration(), - phantom_node.GetReverseDistance()}); + if (phantom_node.IsValidForwardTarget()) + { + heap.Insert(phantom_node.forward_segment_id.id, + phantom_node.GetForwardWeightPlusOffset(), + {phantom_node.forward_segment_id.id, + phantom_node.GetForwardDuration(), + phantom_node.GetForwardDistance()}); + } + if (phantom_node.IsValidReverseTarget()) + { + heap.Insert(phantom_node.reverse_segment_id.id, + phantom_node.GetReverseWeightPlusOffset(), + {phantom_node.reverse_segment_id.id, + phantom_node.GetReverseDuration(), + phantom_node.GetReverseDistance()}); + } } } template void annotatePath(const FacadeT &facade, - const PhantomNodes &phantom_node_pair, + const PhantomEndpoints &endpoints, const std::vector &unpacked_nodes, const std::vector &unpacked_edges, std::vector &unpacked_path) @@ -133,14 +180,14 @@ void annotatePath(const FacadeT &facade, const auto source_node_id = unpacked_nodes.front(); const auto target_node_id = unpacked_nodes.back(); const bool start_traversed_in_reverse = - phantom_node_pair.source_phantom.forward_segment_id.id != source_node_id; + endpoints.source_phantom.forward_segment_id.id != source_node_id; const bool target_traversed_in_reverse = - phantom_node_pair.target_phantom.forward_segment_id.id != target_node_id; + endpoints.target_phantom.forward_segment_id.id != target_node_id; - BOOST_ASSERT(phantom_node_pair.source_phantom.forward_segment_id.id == source_node_id || - phantom_node_pair.source_phantom.reverse_segment_id.id == source_node_id); - BOOST_ASSERT(phantom_node_pair.target_phantom.forward_segment_id.id == target_node_id || - phantom_node_pair.target_phantom.reverse_segment_id.id == target_node_id); + BOOST_ASSERT(endpoints.source_phantom.forward_segment_id.id == source_node_id || + endpoints.source_phantom.reverse_segment_id.id == source_node_id); + BOOST_ASSERT(endpoints.target_phantom.forward_segment_id.id == target_node_id || + endpoints.target_phantom.reverse_segment_id.id == target_node_id); // datastructures to hold extracted data from geometry std::vector id_vector; @@ -180,8 +227,8 @@ void annotatePath(const FacadeT &facade, const auto geometry_index = facade.GetGeometryIndex(node_id); get_segment_geometry(geometry_index); - BOOST_ASSERT(id_vector.size() > 0); - BOOST_ASSERT(datasource_vector.size() > 0); + BOOST_ASSERT(!id_vector.empty()); + BOOST_ASSERT(!datasource_vector.empty()); BOOST_ASSERT(weight_vector.size() + 1 == id_vector.size()); BOOST_ASSERT(duration_vector.size() + 1 == id_vector.size()); @@ -190,11 +237,11 @@ void annotatePath(const FacadeT &facade, std::size_t start_index = 0; if (is_first_segment) { - unsigned short segment_position = phantom_node_pair.source_phantom.fwd_segment_position; + unsigned short segment_position = endpoints.source_phantom.fwd_segment_position; if (start_traversed_in_reverse) { - segment_position = weight_vector.size() - - phantom_node_pair.source_phantom.fwd_segment_position - 1; + segment_position = + weight_vector.size() - endpoints.source_phantom.fwd_segment_position - 1; } BOOST_ASSERT(segment_position >= 0); start_index = static_cast(segment_position); @@ -214,7 +261,7 @@ void annotatePath(const FacadeT &facade, datasource_vector[segment_idx], boost::none}); } - BOOST_ASSERT(unpacked_path.size() > 0); + BOOST_ASSERT(!unpacked_path.empty()); const auto turn_duration = facade.GetDurationPenaltyForEdgeID(turn_id); const auto turn_weight = facade.GetWeightPenaltyForEdgeID(turn_id); @@ -237,19 +284,17 @@ void annotatePath(const FacadeT &facade, { if (is_local_path) { - start_index = - weight_vector.size() - phantom_node_pair.source_phantom.fwd_segment_position - 1; + start_index = weight_vector.size() - endpoints.source_phantom.fwd_segment_position - 1; } - end_index = - weight_vector.size() - phantom_node_pair.target_phantom.fwd_segment_position - 1; + end_index = weight_vector.size() - endpoints.target_phantom.fwd_segment_position - 1; } else { if (is_local_path) { - start_index = phantom_node_pair.source_phantom.fwd_segment_position; + start_index = endpoints.source_phantom.fwd_segment_position; } - end_index = phantom_node_pair.target_phantom.fwd_segment_position; + end_index = endpoints.target_phantom.fwd_segment_position; } // Given the following compressed geometry: @@ -277,11 +322,11 @@ void annotatePath(const FacadeT &facade, if (!unpacked_path.empty()) { const auto source_weight = start_traversed_in_reverse - ? phantom_node_pair.source_phantom.reverse_weight - : phantom_node_pair.source_phantom.forward_weight; + ? endpoints.source_phantom.reverse_weight + : endpoints.source_phantom.forward_weight; const auto source_duration = start_traversed_in_reverse - ? phantom_node_pair.source_phantom.reverse_duration - : phantom_node_pair.source_phantom.forward_duration; + ? endpoints.source_phantom.reverse_duration + : endpoints.source_phantom.forward_duration; // The above code will create segments for (v, w), (w,x), (x, y) and (y, Z). // However the first segment duration needs to be adjusted to the fact that the source // phantom is in the middle of the segment. We do this by subtracting v--s from the @@ -358,12 +403,11 @@ double getPathDistance(const DataFacade &facade, template InternalRouteResult extractRoute(const DataFacade &facade, const EdgeWeight weight, - const PhantomNodes &phantom_nodes, + const PhantomEndpointCandidates &endpoint_candidates, const std::vector &unpacked_nodes, const std::vector &unpacked_edges) { InternalRouteResult raw_route_data; - raw_route_data.segment_end_coordinates = {phantom_nodes}; // No path found for both target nodes? if (INVALID_EDGE_WEIGHT == weight) @@ -371,15 +415,18 @@ InternalRouteResult extractRoute(const DataFacade &facade, return raw_route_data; } + auto phantom_endpoints = endpointsFromCandidates(endpoint_candidates, unpacked_nodes); + raw_route_data.leg_endpoints = {phantom_endpoints}; + raw_route_data.shortest_path_weight = weight; raw_route_data.unpacked_path_segments.resize(1); raw_route_data.source_traversed_in_reverse.push_back( - (unpacked_nodes.front() != phantom_nodes.source_phantom.forward_segment_id.id)); + (unpacked_nodes.front() != phantom_endpoints.source_phantom.forward_segment_id.id)); raw_route_data.target_traversed_in_reverse.push_back( - (unpacked_nodes.back() != phantom_nodes.target_phantom.forward_segment_id.id)); + (unpacked_nodes.back() != phantom_endpoints.target_phantom.forward_segment_id.id)); annotatePath(facade, - phantom_nodes, + phantom_endpoints, unpacked_nodes, unpacked_edges, raw_route_data.unpacked_path_segments.front()); diff --git a/include/engine/routing_algorithms/routing_base_ch.hpp b/include/engine/routing_algorithms/routing_base_ch.hpp index 863d29cf335..d4e77b839a7 100644 --- a/include/engine/routing_algorithms/routing_base_ch.hpp +++ b/include/engine/routing_algorithms/routing_base_ch.hpp @@ -120,8 +120,8 @@ void routingStep(const DataFacade &facade, NodeID &middle_node_id, EdgeWeight &upper_bound, EdgeWeight min_edge_offset, - const bool force_loop_forward, - const bool force_loop_reverse) + const std::vector &force_loop_forward_nodes, + const std::vector &force_loop_reverse_nodes) { auto heapNode = forward_heap.DeleteMinGetHeapNode(); const auto reverseHeapNode = reverse_heap.GetHeapNodeIfWasInserted(heapNode.node); @@ -131,9 +131,8 @@ void routingStep(const DataFacade &facade, const EdgeWeight new_weight = reverseHeapNode->weight + heapNode.weight; if (new_weight < upper_bound) { - // if loops are forced, they are so at the source - if ((force_loop_forward && heapNode.data.parent == heapNode.node) || - (force_loop_reverse && reverseHeapNode->data.parent == heapNode.node) || + if (force_loop(force_loop_forward_nodes, heapNode) || + force_loop(force_loop_reverse_nodes, heapNode) || // in this case we are looking at a bi-directional way where the source // and target phantom are on the same edge based node new_weight < 0) @@ -398,7 +397,7 @@ template void unpackPath(const FacadeT &facade, RandomIter packed_path_begin, RandomIter packed_path_end, - const PhantomNodes &phantom_nodes, + const PhantomEndpoints &route_endpoints, std::vector &unpacked_path) { const auto nodes_number = std::distance(packed_path_begin, packed_path_end); @@ -422,7 +421,7 @@ void unpackPath(const FacadeT &facade, }); } - annotatePath(facade, phantom_nodes, unpacked_nodes, unpacked_edges, unpacked_path); + annotatePath(facade, route_endpoints, unpacked_nodes, unpacked_edges, unpacked_path); } /** @@ -467,12 +466,35 @@ void search(SearchEngineData &engine_working_data, const DataFacade &facade, SearchEngineData::QueryHeap &forward_heap, SearchEngineData::QueryHeap &reverse_heap, - std::int32_t &weight, + EdgeWeight &weight, std::vector &packed_leg, - const bool force_loop_forward, - const bool force_loop_reverse, - const PhantomNodes &phantom_nodes, - const int duration_upper_bound = INVALID_EDGE_WEIGHT); + const std::vector &force_loop_forward_node, + const std::vector &force_loop_reverse_node, + const EdgeWeight duration_upper_bound = INVALID_EDGE_WEIGHT); + +template +void search(SearchEngineData &engine_working_data, + const DataFacade &facade, + SearchEngineData::QueryHeap &forward_heap, + SearchEngineData::QueryHeap &reverse_heap, + EdgeWeight &weight, + std::vector &packed_leg, + const std::vector &force_loop_forward_node, + const std::vector &force_loop_reverse_node, + const PhantomEndpointT & /*endpoints*/, + const EdgeWeight duration_upper_bound = INVALID_EDGE_WEIGHT) +{ + // Avoid templating the CH search implementations. + return search(engine_working_data, + facade, + forward_heap, + reverse_heap, + weight, + packed_leg, + force_loop_forward_node, + force_loop_reverse_node, + duration_upper_bound); +} // Requires the heaps for be empty // If heaps should be adjusted to be initialized outside of this function, diff --git a/include/engine/routing_algorithms/routing_base_mld.hpp b/include/engine/routing_algorithms/routing_base_mld.hpp index 55f366574b7..2fdb61f98bb 100644 --- a/include/engine/routing_algorithms/routing_base_mld.hpp +++ b/include/engine/routing_algorithms/routing_base_mld.hpp @@ -33,24 +33,75 @@ namespace template inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition, NodeID node, - const PhantomNodes &phantom_nodes) + const PhantomNode &source, + const PhantomNode &target) { auto level = [&partition, node](const SegmentID &source, const SegmentID &target) { if (source.enabled && target.enabled) return partition.GetQueryLevel(source.id, target.id, node); return INVALID_LEVEL_ID; }; - return std::min(std::min(level(phantom_nodes.source_phantom.forward_segment_id, - phantom_nodes.target_phantom.forward_segment_id), - level(phantom_nodes.source_phantom.forward_segment_id, - phantom_nodes.target_phantom.reverse_segment_id)), - std::min(level(phantom_nodes.source_phantom.reverse_segment_id, - phantom_nodes.target_phantom.forward_segment_id), - level(phantom_nodes.source_phantom.reverse_segment_id, - phantom_nodes.target_phantom.reverse_segment_id))); + + return std::min(std::min(level(source.forward_segment_id, target.forward_segment_id), + level(source.forward_segment_id, target.reverse_segment_id)), + std::min(level(source.reverse_segment_id, target.forward_segment_id), + level(source.reverse_segment_id, target.reverse_segment_id))); +} + +template +inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition, + NodeID node, + const PhantomEndpoints &endpoints) +{ + return getNodeQueryLevel(partition, node, endpoints.source_phantom, endpoints.target_phantom); +} + +template +inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition, + NodeID node, + const PhantomCandidatesToTarget &endpoint_candidates) +{ + auto min_level = std::accumulate( + endpoint_candidates.source_phantoms.begin(), + endpoint_candidates.source_phantoms.end(), + INVALID_LEVEL_ID, + [&](LevelID current_level, const PhantomNode &source) { + return std::min( + current_level, + getNodeQueryLevel(partition, node, source, endpoint_candidates.target_phantom)); + }); + return min_level; +} + +template +inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition, + NodeID node, + const PhantomEndpointCandidates &endpoint_candidates) +{ + auto min_level = std::accumulate( + endpoint_candidates.source_phantoms.begin(), + endpoint_candidates.source_phantoms.end(), + INVALID_LEVEL_ID, + [&](LevelID level_1, const PhantomNode &source) { + return std::min( + level_1, + std::accumulate(endpoint_candidates.target_phantoms.begin(), + endpoint_candidates.target_phantoms.end(), + level_1, + [&](LevelID level_2, const PhantomNode &target) { + return std::min( + level_2, + getNodeQueryLevel(partition, node, source, target)); + })); + }); + return min_level; } -inline bool checkParentCellRestriction(CellID, const PhantomNodes &) { return true; } +template +inline bool checkParentCellRestriction(CellID, const PhantomCandidateT &) +{ + return true; +} // Restricted search (Args is LevelID, CellID): // * use the fixed level for queries @@ -72,17 +123,23 @@ inline bool checkParentCellRestriction(CellID cell, LevelID, CellID parent) template inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition, const NodeID node, - const PhantomNode &phantom_node) + const PhantomNodeCandidates &candidates) { - auto highest_diffrent_level = [&partition, node](const SegmentID &phantom_node) { - if (phantom_node.enabled) - return partition.GetHighestDifferentLevel(phantom_node.id, node); - return INVALID_LEVEL_ID; + auto highest_different_level = [&partition, node](const SegmentID &segment) { + return segment.enabled ? partition.GetHighestDifferentLevel(segment.id, node) + : INVALID_LEVEL_ID; }; - const auto node_level = std::min(highest_diffrent_level(phantom_node.forward_segment_id), - highest_diffrent_level(phantom_node.reverse_segment_id)); - + auto node_level = + std::accumulate(candidates.begin(), + candidates.end(), + INVALID_LEVEL_ID, + [&](LevelID current_level, const PhantomNode &phantom_node) { + auto highest_level = + std::min(highest_different_level(phantom_node.forward_segment_id), + highest_different_level(phantom_node.reverse_segment_id)); + return std::min(current_level, highest_level); + }); return node_level; } @@ -92,31 +149,17 @@ inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition, template inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition, NodeID node, - const std::vector &phantom_nodes, + const std::vector &candidates_list, const std::size_t phantom_index, const std::vector &phantom_indices) { - auto min_level = [&partition, node](const PhantomNode &phantom_node) { - const auto &forward_segment = phantom_node.forward_segment_id; - const auto forward_level = - forward_segment.enabled ? partition.GetHighestDifferentLevel(node, forward_segment.id) - : INVALID_LEVEL_ID; - - const auto &reverse_segment = phantom_node.reverse_segment_id; - const auto reverse_level = - reverse_segment.enabled ? partition.GetHighestDifferentLevel(node, reverse_segment.id) - : INVALID_LEVEL_ID; - - return std::min(forward_level, reverse_level); - }; - // Get minimum level over all phantoms of the highest different level with respect to node // This is equivalent to min_{∀ source, target} partition.GetQueryLevel(source, node, target) - auto result = min_level(phantom_nodes[phantom_index]); - for (const auto &index : phantom_indices) - { - result = std::min(result, min_level(phantom_nodes[index])); - } + auto init = getNodeQueryLevel(partition, node, candidates_list[phantom_index]); + auto result = std::accumulate( + phantom_indices.begin(), phantom_indices.end(), init, [&](LevelID level, size_t index) { + return std::min(level, getNodeQueryLevel(partition, node, candidates_list[index])); + }); return result; } } // namespace @@ -229,7 +272,7 @@ template void relaxOutgoingEdges(const DataFacade &facade, typename SearchEngineData::QueryHeap &forward_heap, const typename SearchEngineData::QueryHeap::HeapNode &heapNode, - Args... args) + const Args &... args) { const auto &partition = facade.GetMultiLevelPartition(); const auto &cells = facade.GetCellStorage(); @@ -344,9 +387,9 @@ void routingStep(const DataFacade &facade, typename SearchEngineData::QueryHeap &reverse_heap, NodeID &middle_node, EdgeWeight &path_upper_bound, - const bool force_loop_forward, - const bool force_loop_reverse, - Args... args) + const std::vector &force_loop_forward_nodes, + const std::vector &force_loop_reverse_nodes, + const Args &... args) { const auto heapNode = forward_heap.DeleteMinGetHeapNode(); const auto weight = heapNode.weight; @@ -366,9 +409,9 @@ void routingStep(const DataFacade &facade, // MLD uses loops forcing only to prune single node paths in forward and/or // backward direction (there is no need to force loops in MLD but in CH) - if (!(force_loop_forward && heapNode.data.parent == heapNode.node) && - !(force_loop_reverse && reverseHeapNode->data.parent == heapNode.node) && - (path_weight >= 0) && (path_weight < path_upper_bound)) + if (!force_loop(force_loop_forward_nodes, heapNode) && + !force_loop(force_loop_reverse_nodes, heapNode) && (path_weight >= 0) && + (path_weight < path_upper_bound)) { middle_node = heapNode.node; path_upper_bound = path_weight; @@ -393,10 +436,10 @@ UnpackedPath search(SearchEngineData &engine_working_data, const DataFacade &facade, typename SearchEngineData::QueryHeap &forward_heap, typename SearchEngineData::QueryHeap &reverse_heap, - const bool force_loop_forward, - const bool force_loop_reverse, + const std::vector &force_loop_forward_nodes, + const std::vector &force_loop_reverse_nodes, EdgeWeight weight_upper_bound, - Args... args) + const Args &... args) { if (forward_heap.Empty() || reverse_heap.Empty()) { @@ -423,8 +466,8 @@ UnpackedPath search(SearchEngineData &engine_working_data, reverse_heap, middle, weight, - force_loop_forward, - force_loop_reverse, + force_loop_forward_nodes, + force_loop_reverse_nodes, args...); if (!forward_heap.Empty()) forward_heap_min = forward_heap.MinKey(); @@ -436,8 +479,8 @@ UnpackedPath search(SearchEngineData &engine_working_data, forward_heap, middle, weight, - force_loop_reverse, - force_loop_forward, + force_loop_reverse_nodes, + force_loop_forward_nodes, args...); if (!reverse_heap.Empty()) reverse_heap_min = reverse_heap.MinKey(); @@ -494,15 +537,16 @@ UnpackedPath search(SearchEngineData &engine_working_data, EdgeWeight subpath_weight; std::vector subpath_nodes; std::vector subpath_edges; - std::tie(subpath_weight, subpath_nodes, subpath_edges) = search(engine_working_data, - facade, - forward_heap, - reverse_heap, - force_loop_forward, - force_loop_reverse, - INVALID_EDGE_WEIGHT, - sublevel, - parent_cell_id); + std::tie(subpath_weight, subpath_nodes, subpath_edges) = + search(engine_working_data, + facade, + forward_heap, + reverse_heap, + force_loop_forward_nodes, + force_loop_reverse_nodes, + INVALID_EDGE_WEIGHT, + sublevel, + parent_cell_id); BOOST_ASSERT(!subpath_edges.empty()); BOOST_ASSERT(subpath_nodes.size() > 1); BOOST_ASSERT(subpath_nodes.front() == source); @@ -517,16 +561,16 @@ UnpackedPath search(SearchEngineData &engine_working_data, } // Alias to be compatible with the CH-based search -template +template inline void search(SearchEngineData &engine_working_data, const DataFacade &facade, typename SearchEngineData::QueryHeap &forward_heap, typename SearchEngineData::QueryHeap &reverse_heap, EdgeWeight &weight, std::vector &unpacked_nodes, - const bool force_loop_forward, - const bool force_loop_reverse, - const PhantomNodes &phantom_nodes, + const std::vector &force_loop_forward_node, + const std::vector &force_loop_reverse_node, + const PhantomEndpointT &endpoints, const EdgeWeight weight_upper_bound = INVALID_EDGE_WEIGHT) { // TODO: change search calling interface to use unpacked_edges result @@ -534,10 +578,10 @@ inline void search(SearchEngineData &engine_working_data, facade, forward_heap, reverse_heap, - force_loop_forward, - force_loop_reverse, + force_loop_forward_node, + force_loop_reverse_node, weight_upper_bound, - phantom_nodes); + endpoints); } // TODO: refactor CH-related stub to use unpacked_edges @@ -545,7 +589,7 @@ template void unpackPath(const FacadeT &facade, RandomIter packed_path_begin, RandomIter packed_path_end, - const PhantomNodes &phantom_nodes, + const PhantomEndpoints &route_endpoints, std::vector &unpacked_path) { const auto nodes_number = std::distance(packed_path_begin, packed_path_end); @@ -568,7 +612,7 @@ void unpackPath(const FacadeT &facade, }); } - annotatePath(facade, phantom_nodes, unpacked_nodes, unpacked_edges, unpacked_path); + annotatePath(facade, route_endpoints, unpacked_nodes, unpacked_edges, unpacked_path); } template @@ -583,8 +627,8 @@ double getNetworkDistance(SearchEngineData &engine_working_data, forward_heap.Clear(); reverse_heap.Clear(); - const PhantomNodes phantom_nodes{source_phantom, target_phantom}; - insertNodesInHeaps(forward_heap, reverse_heap, phantom_nodes); + const PhantomEndpoints endpoints{source_phantom, target_phantom}; + insertNodesInHeaps(forward_heap, reverse_heap, endpoints); EdgeWeight weight = INVALID_EDGE_WEIGHT; std::vector unpacked_nodes; @@ -593,10 +637,10 @@ double getNetworkDistance(SearchEngineData &engine_working_data, facade, forward_heap, reverse_heap, - DO_NOT_FORCE_LOOPS, - DO_NOT_FORCE_LOOPS, + {}, + {}, weight_upper_bound, - phantom_nodes); + endpoints); if (weight == INVALID_EDGE_WEIGHT) { @@ -605,7 +649,7 @@ double getNetworkDistance(SearchEngineData &engine_working_data, std::vector unpacked_path; - annotatePath(facade, phantom_nodes, unpacked_nodes, unpacked_edges, unpacked_path); + annotatePath(facade, endpoints, unpacked_nodes, unpacked_edges, unpacked_path); return getPathDistance(facade, unpacked_path, source_phantom, target_phantom); } diff --git a/include/engine/routing_algorithms/shortest_path.hpp b/include/engine/routing_algorithms/shortest_path.hpp index e9869469440..fadf59a4573 100644 --- a/include/engine/routing_algorithms/shortest_path.hpp +++ b/include/engine/routing_algorithms/shortest_path.hpp @@ -14,10 +14,11 @@ namespace routing_algorithms { template -InternalRouteResult shortestPathSearch(SearchEngineData &engine_working_data, - const DataFacade &facade, - const std::vector &phantom_nodes_vector, - const boost::optional continue_straight_at_waypoint); +InternalRouteResult +shortestPathSearch(SearchEngineData &engine_working_data, + const DataFacade &facade, + const std::vector &waypoint_candidates, + const boost::optional continue_straight_at_waypoint); } // namespace routing_algorithms } // namespace engine diff --git a/include/engine/routing_algorithms/shortest_path_impl.hpp b/include/engine/routing_algorithms/shortest_path_impl.hpp index 304f5ee5e5d..cfeb91463db 100644 --- a/include/engine/routing_algorithms/shortest_path_impl.hpp +++ b/include/engine/routing_algorithms/shortest_path_impl.hpp @@ -15,121 +15,112 @@ namespace routing_algorithms namespace { +const size_t INVALID_LEG_INDEX = std::numeric_limits::max(); -const static constexpr bool DO_NOT_FORCE_LOOP = false; - -// allows a uturn at the target_phantom -// searches source forward/reverse -> target forward/reverse template void searchWithUTurn(SearchEngineData &engine_working_data, const DataFacade &facade, typename SearchEngineData::QueryHeap &forward_heap, typename SearchEngineData::QueryHeap &reverse_heap, - const bool search_from_forward_node, - const bool search_from_reverse_node, - const bool search_to_forward_node, - const bool search_to_reverse_node, - const PhantomNode &source_phantom, - const PhantomNode &target_phantom, - const int total_weight_to_forward, - const int total_weight_to_reverse, - int &new_total_weight, + const PhantomEndpointCandidates &candidates, + const EdgeWeight &total_weight, + EdgeWeight &new_total_weight, std::vector &leg_packed_path) { forward_heap.Clear(); reverse_heap.Clear(); - if (search_from_forward_node) - { - forward_heap.Insert(source_phantom.forward_segment_id.id, - -source_phantom.GetForwardWeightPlusOffset(), - source_phantom.forward_segment_id.id); - } - if (search_from_reverse_node) - { - forward_heap.Insert(source_phantom.reverse_segment_id.id, - -source_phantom.GetReverseWeightPlusOffset(), - source_phantom.reverse_segment_id.id); - } - if (search_to_forward_node) + + for (const auto &source : candidates.source_phantoms) { - reverse_heap.Insert(target_phantom.forward_segment_id.id, - target_phantom.GetForwardWeightPlusOffset(), - target_phantom.forward_segment_id.id); + if (source.IsValidForwardSource()) + { + forward_heap.Insert(source.forward_segment_id.id, + total_weight - source.GetForwardWeightPlusOffset(), + source.forward_segment_id.id); + } + + if (source.IsValidReverseSource()) + { + forward_heap.Insert(source.reverse_segment_id.id, + total_weight - source.GetReverseWeightPlusOffset(), + source.reverse_segment_id.id); + } } - if (search_to_reverse_node) + for (const auto &target : candidates.target_phantoms) { - reverse_heap.Insert(target_phantom.reverse_segment_id.id, - target_phantom.GetReverseWeightPlusOffset(), - target_phantom.reverse_segment_id.id); + if (target.IsValidForwardTarget()) + { + reverse_heap.Insert(target.forward_segment_id.id, + target.GetForwardWeightPlusOffset(), + target.forward_segment_id.id); + } + if (target.IsValidReverseTarget()) + { + reverse_heap.Insert(target.reverse_segment_id.id, + target.GetReverseWeightPlusOffset(), + target.reverse_segment_id.id); + } } - // this is only relevent if source and target are on the same compressed edge - auto is_oneway_source = !(search_from_forward_node && search_from_reverse_node); - auto is_oneway_target = !(search_to_forward_node && search_to_reverse_node); - // we only enable loops here if we can't search from forward to backward node - auto needs_loop_forwards = is_oneway_source && needsLoopForward(source_phantom, target_phantom); - auto needs_loop_backwards = - is_oneway_target && needsLoopBackwards(source_phantom, target_phantom); - search(engine_working_data, facade, forward_heap, reverse_heap, new_total_weight, leg_packed_path, - needs_loop_forwards, - needs_loop_backwards, - {source_phantom, target_phantom}); - - // if no route is found between two parts of the via-route, the entire route becomes - // invalid. Adding to invalid edge weight sadly doesn't return an invalid edge weight. Here - // we prevent the possible overflow, faking the addition of infinity + x == infinity - if (new_total_weight != INVALID_EDGE_WEIGHT) - new_total_weight += std::min(total_weight_to_forward, total_weight_to_reverse); + getForwardLoopNodes(candidates), + getBackwardLoopNodes(candidates), + candidates); } -// searches shortest path between: -// source forward/reverse -> target forward -// source forward/reverse -> target reverse template void search(SearchEngineData &engine_working_data, const DataFacade &facade, typename SearchEngineData::QueryHeap &forward_heap, typename SearchEngineData::QueryHeap &reverse_heap, - const bool search_from_forward_node, - const bool search_from_reverse_node, - const bool search_to_forward_node, - const bool search_to_reverse_node, - const PhantomNode &source_phantom, - const PhantomNode &target_phantom, - const int total_weight_to_forward, - const int total_weight_to_reverse, - int &new_total_weight_to_forward, - int &new_total_weight_to_reverse, + const std::vector &search_from_forward_node, + const std::vector &search_from_reverse_node, + const PhantomCandidatesToTarget &candidates, + const std::vector &total_weight_to_forward, + const std::vector &total_weight_to_reverse, + EdgeWeight &new_total_weight_to_forward, + EdgeWeight &new_total_weight_to_reverse, std::vector &leg_packed_path_forward, std::vector &leg_packed_path_reverse) { - if (search_to_forward_node) + // We want to find the shortest distance from any of the source candidate segments to this + // specific target. + const auto &source_candidates = candidates.source_phantoms; + const auto &target = candidates.target_phantom; + BOOST_ASSERT(search_from_forward_node.size() == source_candidates.size()); + BOOST_ASSERT(search_from_reverse_node.size() == source_candidates.size()); + + if (target.IsValidForwardTarget()) { forward_heap.Clear(); reverse_heap.Clear(); - reverse_heap.Insert(target_phantom.forward_segment_id.id, - target_phantom.GetForwardWeightPlusOffset(), - target_phantom.forward_segment_id.id); + reverse_heap.Insert(target.forward_segment_id.id, + target.GetForwardWeightPlusOffset(), + target.forward_segment_id.id); - if (search_from_forward_node) - { - forward_heap.Insert(source_phantom.forward_segment_id.id, - total_weight_to_forward - - source_phantom.GetForwardWeightPlusOffset(), - source_phantom.forward_segment_id.id); - } - if (search_from_reverse_node) + for (const auto i : util::irange(0UL, source_candidates.size())) { - forward_heap.Insert(source_phantom.reverse_segment_id.id, - total_weight_to_reverse - - source_phantom.GetReverseWeightPlusOffset(), - source_phantom.reverse_segment_id.id); + const auto &candidate = source_candidates[i]; + if (search_from_forward_node[i] && candidate.IsValidForwardSource()) + { + forward_heap.Insert(candidate.forward_segment_id.id, + total_weight_to_forward[i] - + candidate.GetForwardWeightPlusOffset(), + candidate.forward_segment_id.id); + } + + if (search_from_reverse_node[i] && candidate.IsValidReverseSource()) + { + forward_heap.Insert(candidate.reverse_segment_id.id, + total_weight_to_reverse[i] - + candidate.GetReverseWeightPlusOffset(), + candidate.reverse_segment_id.id); + } } search(engine_working_data, @@ -138,31 +129,38 @@ void search(SearchEngineData &engine_working_data, reverse_heap, new_total_weight_to_forward, leg_packed_path_forward, - needsLoopForward(source_phantom, target_phantom), - routing_algorithms::DO_NOT_FORCE_LOOP, - {source_phantom, target_phantom}); + getForwardLoopNodes(candidates), + {}, + candidates); } - if (search_to_reverse_node) + if (target.IsValidReverseTarget()) { forward_heap.Clear(); reverse_heap.Clear(); - reverse_heap.Insert(target_phantom.reverse_segment_id.id, - target_phantom.GetReverseWeightPlusOffset(), - target_phantom.reverse_segment_id.id); - if (search_from_forward_node) - { - forward_heap.Insert(source_phantom.forward_segment_id.id, - total_weight_to_forward - - source_phantom.GetForwardWeightPlusOffset(), - source_phantom.forward_segment_id.id); - } - if (search_from_reverse_node) + reverse_heap.Insert(target.reverse_segment_id.id, + target.GetReverseWeightPlusOffset(), + target.reverse_segment_id.id); + + BOOST_ASSERT(search_from_forward_node.size() == source_candidates.size()); + for (const auto i : util::irange(0UL, source_candidates.size())) { - forward_heap.Insert(source_phantom.reverse_segment_id.id, - total_weight_to_reverse - - source_phantom.GetReverseWeightPlusOffset(), - source_phantom.reverse_segment_id.id); + const auto &candidate = source_candidates[i]; + if (search_from_forward_node[i] && candidate.IsValidForwardSource()) + { + forward_heap.Insert(candidate.forward_segment_id.id, + total_weight_to_forward[i] - + candidate.GetForwardWeightPlusOffset(), + candidate.forward_segment_id.id); + } + + if (search_from_reverse_node[i] && candidate.IsValidReverseSource()) + { + forward_heap.Insert(candidate.reverse_segment_id.id, + total_weight_to_reverse[i] - + candidate.GetReverseWeightPlusOffset(), + candidate.reverse_segment_id.id); + } } search(engine_working_data, @@ -171,29 +169,32 @@ void search(SearchEngineData &engine_working_data, reverse_heap, new_total_weight_to_reverse, leg_packed_path_reverse, - routing_algorithms::DO_NOT_FORCE_LOOP, - needsLoopBackwards(source_phantom, target_phantom), - {source_phantom, target_phantom}); + {}, + getBackwardLoopNodes(candidates), + candidates); } } template void unpackLegs(const DataFacade &facade, - const std::vector &phantom_nodes_vector, + const std::vector &leg_endpoints, + const std::vector &route_path_indices, const std::vector &total_packed_path, const std::vector &packed_leg_begin, const EdgeWeight shortest_path_weight, InternalRouteResult &raw_route_data) { - raw_route_data.unpacked_path_segments.resize(packed_leg_begin.size() - 1); + raw_route_data.unpacked_path_segments.resize(route_path_indices.size()); raw_route_data.shortest_path_weight = shortest_path_weight; - for (const auto current_leg : util::irange(0UL, packed_leg_begin.size() - 1)) + for (const auto current_leg : util::irange(0UL, route_path_indices.size())) { - auto leg_begin = total_packed_path.begin() + packed_leg_begin[current_leg]; - auto leg_end = total_packed_path.begin() + packed_leg_begin[current_leg + 1]; - const auto &unpack_phantom_node_pair = phantom_nodes_vector[current_leg]; + auto leg_begin = + total_packed_path.begin() + packed_leg_begin[route_path_indices[current_leg]]; + auto leg_end = + total_packed_path.begin() + packed_leg_begin[route_path_indices[current_leg] + 1]; + const auto &unpack_phantom_node_pair = leg_endpoints[current_leg]; unpackPath(facade, leg_begin, leg_end, @@ -201,10 +202,10 @@ void unpackLegs(const DataFacade &facade, raw_route_data.unpacked_path_segments[current_leg]); raw_route_data.source_traversed_in_reverse.push_back( - (*leg_begin != phantom_nodes_vector[current_leg].source_phantom.forward_segment_id.id)); + (*leg_begin != leg_endpoints[current_leg].source_phantom.forward_segment_id.id)); raw_route_data.target_traversed_in_reverse.push_back( (*std::prev(leg_end) != - phantom_nodes_vector[current_leg].target_phantom.forward_segment_id.id)); + leg_endpoints[current_leg].target_phantom.forward_segment_id.id)); } } @@ -226,255 +227,566 @@ inline void initializeHeap(SearchEngineData &eng const auto border_nodes_number = facade.GetMaxBorderNodeID() + 1; engine_working_data.InitializeOrClearFirstThreadLocalStorage(nodes_number, border_nodes_number); } -} // namespace template -InternalRouteResult shortestPathSearch(SearchEngineData &engine_working_data, - const DataFacade &facade, - const std::vector &phantom_nodes_vector, - const boost::optional continue_straight_at_waypoint) +InternalRouteResult +constructRouteResult(const DataFacade &facade, + const std::vector &waypoint_candidates, + const std::vector &route_path_indices, + const std::vector &packed_paths, + const std::vector &packed_path_begin, + const EdgeWeight min_weight) { + InternalRouteResult raw_route_data; - raw_route_data.segment_end_coordinates = phantom_nodes_vector; - const bool allow_uturn_at_waypoint = - !(continue_straight_at_waypoint ? *continue_straight_at_waypoint - : facade.GetContinueStraightDefault()); + // Find the start/end phantom endpoints + std::vector path_endpoints; + for (const auto i : util::irange(0UL, waypoint_candidates.size() - 1)) + { + const auto &source_candidates = waypoint_candidates[i]; + const auto &target_candidates = waypoint_candidates[i + 1]; + const auto path_index = route_path_indices[i]; + const auto start_node = packed_paths[packed_path_begin[path_index]]; + const auto end_node = packed_paths[packed_path_begin[path_index + 1] - 1]; + + auto source_it = + std::find_if(source_candidates.begin(), + source_candidates.end(), + [&start_node](const auto &source_phantom) { + return (start_node == source_phantom.forward_segment_id.id || + start_node == source_phantom.reverse_segment_id.id); + }); + BOOST_ASSERT(source_it != source_candidates.end()); + + auto target_it = + std::find_if(target_candidates.begin(), + target_candidates.end(), + [&end_node](const auto &target_phantom) { + return (end_node == target_phantom.forward_segment_id.id || + end_node == target_phantom.reverse_segment_id.id); + }); + BOOST_ASSERT(target_it != target_candidates.end()); + + path_endpoints.push_back({*source_it, *target_it}); + }; + raw_route_data.leg_endpoints = path_endpoints; + + unpackLegs(facade, + path_endpoints, + route_path_indices, + packed_paths, + packed_path_begin, + min_weight, + raw_route_data); + + return raw_route_data; +} + +// Allows a uturn at the waypoints. +// Given that all candidates for a waypoint have the same location, +// this allows us to just find the shortest path from any of the source to any of the targets. +template +InternalRouteResult +shortestPathWithWaypointUTurns(SearchEngineData &engine_working_data, + const DataFacade &facade, + const std::vector &waypoint_candidates) +{ + + EdgeWeight total_weight = 0; + std::vector total_packed_path; + std::vector packed_leg_begin; initializeHeap(engine_working_data, facade); auto &forward_heap = *engine_working_data.forward_heap_1; auto &reverse_heap = *engine_working_data.reverse_heap_1; - int total_weight_to_forward = 0; - int total_weight_to_reverse = 0; - bool search_from_forward_node = - phantom_nodes_vector.front().source_phantom.IsValidForwardSource(); - bool search_from_reverse_node = - phantom_nodes_vector.front().source_phantom.IsValidReverseSource(); + for (const auto i : util::irange(0UL, waypoint_candidates.size() - 1)) + { + PhantomEndpointCandidates search_candidates{waypoint_candidates[i], + waypoint_candidates[i + 1]}; + std::vector packed_leg; + EdgeWeight new_total_weight = INVALID_EDGE_WEIGHT; + + // We have a valid path up to this leg + BOOST_ASSERT(total_weight != INVALID_EDGE_WEIGHT); + searchWithUTurn(engine_working_data, + facade, + forward_heap, + reverse_heap, + search_candidates, + total_weight, + new_total_weight, + packed_leg); + + if (new_total_weight == INVALID_EDGE_WEIGHT) + return {}; + + packed_leg_begin.push_back(total_packed_path.size()); + total_packed_path.insert(total_packed_path.end(), packed_leg.begin(), packed_leg.end()); + total_weight = new_total_weight; + }; + + // Add sentinel + packed_leg_begin.push_back(total_packed_path.size()); + + BOOST_ASSERT(packed_leg_begin.size() == waypoint_candidates.size()); + + std::vector sequential_indices(packed_leg_begin.size() - 1); + std::iota(sequential_indices.begin(), sequential_indices.end(), 0); + return constructRouteResult(facade, + waypoint_candidates, + sequential_indices, + total_packed_path, + packed_leg_begin, + total_weight); +} - std::vector prev_packed_leg_to_forward; - std::vector prev_packed_leg_to_reverse; +struct leg_connections +{ + // X_to_Y = i can be read as + // sources[i].X is the source of the shortest leg path to target.Y + boost::optional forward_to_forward; + boost::optional reverse_to_forward; + boost::optional forward_to_reverse; + boost::optional reverse_to_reverse; +}; + +// Identify which of the source candidates segments is being used for paths to the +// forward and reverse segment targets. +leg_connections getLegConnections(const PhantomNodeCandidates &source_candidates, + const std::vector &packed_leg_to_forward, + const std::vector &packed_leg_to_reverse, + const EdgeWeight new_total_weight_to_forward, + const EdgeWeight new_total_weight_to_reverse) +{ + leg_connections connections; + for (const auto i : util::irange(0UL, source_candidates.size())) + { + const auto &candidate = source_candidates[i]; - std::vector total_packed_path_to_forward; - std::vector packed_leg_to_forward_begin; - std::vector total_packed_path_to_reverse; - std::vector packed_leg_to_reverse_begin; + if ((new_total_weight_to_forward != INVALID_EDGE_WEIGHT) && + candidate.IsValidForwardSource() && + packed_leg_to_forward.front() == candidate.forward_segment_id.id) + { + BOOST_ASSERT(!connections.forward_to_forward && !connections.reverse_to_forward); + connections.forward_to_forward = i; + } + else if ((new_total_weight_to_forward != INVALID_EDGE_WEIGHT) && + candidate.IsValidReverseSource() && + packed_leg_to_forward.front() == candidate.reverse_segment_id.id) + { + BOOST_ASSERT(!connections.forward_to_forward && !connections.reverse_to_forward); + connections.reverse_to_forward = i; + } - std::size_t current_leg = 0; - // this implements a dynamic program that finds the shortest route through - // a list of vias - for (const auto &phantom_node_pair : phantom_nodes_vector) + if ((new_total_weight_to_reverse != INVALID_EDGE_WEIGHT) && + candidate.IsValidForwardSource() && + packed_leg_to_reverse.front() == candidate.forward_segment_id.id) + { + BOOST_ASSERT(!connections.forward_to_reverse && !connections.reverse_to_reverse); + connections.forward_to_reverse = i; + } + else if ((new_total_weight_to_reverse != INVALID_EDGE_WEIGHT) && + candidate.IsValidReverseSource() && + packed_leg_to_reverse.front() == candidate.reverse_segment_id.id) + { + BOOST_ASSERT(!connections.forward_to_reverse && !connections.reverse_to_reverse); + connections.reverse_to_reverse = i; + } + } + return connections; +} + +struct leg_state +{ + // routability to target + std::vector reached_forward_node_target; + std::vector reached_reverse_node_target; + // total weight from route start up to and including this leg + std::vector total_weight_to_forward; + std::vector total_weight_to_reverse; + // total nodes from route start up to and including this leg + std::vector total_nodes_to_forward; + std::vector total_nodes_to_reverse; + + void reset() { - int new_total_weight_to_forward = INVALID_EDGE_WEIGHT; - int new_total_weight_to_reverse = INVALID_EDGE_WEIGHT; + reached_forward_node_target.clear(); + reached_reverse_node_target.clear(); + total_weight_to_forward.clear(); + total_weight_to_reverse.clear(); + total_nodes_to_forward.clear(); + total_nodes_to_reverse.clear(); + } +}; - std::vector packed_leg_to_forward; - std::vector packed_leg_to_reverse; +struct route_state +{ + /** + * To avoid many small vectors or lots of copying, we use a single vector to track all + * possible route leg paths and combine ths with offset and back link vectors to + * reconstruct the full shortest path once the search is complete. + * path_0 -> path_1 ->...-> path_m-1 -> path_m + * \ + * > path_2 + * --path_0-- --path_1-- --path_2-- ....... -path_m-1- --path_m-- + * | | | | | | | + * total_packed_path [n0,n1,.....,na,na+1,..,nb,nb+1,..,.......,nx,.......,ny......nz] + * + * packed_leg_path_begin [0,a,b,...,x,y,z+1] + * + * previous_leg_in_route [-1,0,0,...,..,m-1] + */ + std::vector total_packed_paths; + std::vector packed_leg_begin; + std::vector previous_leg_in_route; + + leg_state last; + leg_state current; + + size_t current_leg; + + /** + * Given the current state after leg n: + * | leg_0_paths | leg_1_paths | ... | leg_n-1_paths | leg_n_paths | + * packed_leg_begin [...............................................................] + * previous_leg_in_route [...............................................................] + * ^ + * previous_leg_path_offset + * + * We want to link back to leg_n paths from leg n+1 paths to represent the routes taken. + * + * We know that the target candidates of leg n are the source candidates of leg n+1, so as long + * as we store the path vector data in the same order as the waypoint candidates, + * we can combine an offset into the path vectors and the candidate index to link back to a + * path from a previous leg. + **/ + size_t previous_leg_path_offset; + + route_state(const PhantomNodeCandidates &init_candidates) + : current_leg(0), previous_leg_path_offset(0) + { + last.total_weight_to_forward.resize(init_candidates.size(), 0); + last.total_weight_to_reverse.resize(init_candidates.size(), 0); + // Initialize routability from source validity. + std::transform( + init_candidates.begin(), + init_candidates.end(), + std::back_inserter(last.reached_forward_node_target), + [](const PhantomNode &phantom_node) { return phantom_node.IsValidForwardSource(); }); + std::transform( + init_candidates.begin(), + init_candidates.end(), + std::back_inserter(last.reached_reverse_node_target), + [](const PhantomNode &phantom_node) { return phantom_node.IsValidReverseSource(); }); + } - const auto &source_phantom = phantom_node_pair.source_phantom; - const auto &target_phantom = phantom_node_pair.target_phantom; + bool completeLeg() + { + std::swap(current, last); + // Reset current state + current.reset(); + + current_leg++; + previous_leg_path_offset = + previous_leg_in_route.size() - 2 * last.total_weight_to_forward.size(); + + auto can_reach_leg_forward = std::any_of(last.total_weight_to_forward.begin(), + last.total_weight_to_forward.end(), + [](auto v) { return v != INVALID_EDGE_WEIGHT; }); + auto can_reach_leg_reverse = std::any_of(last.total_weight_to_reverse.begin(), + last.total_weight_to_reverse.end(), + [](auto v) { return v != INVALID_EDGE_WEIGHT; }); + + return can_reach_leg_forward || can_reach_leg_reverse; + } - bool search_to_forward_node = target_phantom.IsValidForwardTarget(); - bool search_to_reverse_node = target_phantom.IsValidReverseTarget(); + void completeSearch() + { + // insert sentinel + packed_leg_begin.push_back(total_packed_paths.size()); + BOOST_ASSERT(packed_leg_begin.size() == previous_leg_in_route.size() + 1); + } - BOOST_ASSERT(!search_from_forward_node || source_phantom.IsValidForwardSource()); - BOOST_ASSERT(!search_from_reverse_node || source_phantom.IsValidReverseSource()); + size_t previousForwardPath(size_t previous_leg) const + { + return previous_leg_path_offset + 2 * previous_leg; + } - if (search_to_reverse_node || search_to_forward_node) + size_t previousReversePath(size_t previous_leg) const + { + return previous_leg_path_offset + 2 * previous_leg + 1; + } + + void addSearchResult(const PhantomCandidatesToTarget &candidates, + const std::vector &packed_leg_to_forward, + const std::vector &packed_leg_to_reverse, + EdgeWeight new_total_weight_to_forward, + EdgeWeight new_total_weight_to_reverse) + { + + // we need to figure out how the new legs connect to the previous ones + if (current_leg > 0) { - if (allow_uturn_at_waypoint) + const auto leg_connections = getLegConnections(candidates.source_phantoms, + packed_leg_to_forward, + packed_leg_to_reverse, + new_total_weight_to_forward, + new_total_weight_to_reverse); + + // Make the back link connections between the current and previous legs. + if (leg_connections.forward_to_forward) + { + auto new_total = last.total_nodes_to_forward[*leg_connections.forward_to_forward] + + packed_leg_to_forward.size(); + current.total_nodes_to_forward.push_back(new_total); + previous_leg_in_route.push_back( + previousForwardPath(*leg_connections.forward_to_forward)); + } + else if (leg_connections.reverse_to_forward) { - searchWithUTurn(engine_working_data, - facade, - forward_heap, - reverse_heap, - search_from_forward_node, - search_from_reverse_node, - search_to_forward_node, - search_to_reverse_node, - source_phantom, - target_phantom, - total_weight_to_forward, - total_weight_to_reverse, - new_total_weight_to_forward, - packed_leg_to_forward); - // if only the reverse node is valid (e.g. when using the match plugin) we - // actually need to move - if (!target_phantom.IsValidForwardTarget()) - { - BOOST_ASSERT(target_phantom.IsValidReverseTarget()); - new_total_weight_to_reverse = new_total_weight_to_forward; - // NOLINTNEXTLINE(bugprone-use-after-move) - packed_leg_to_reverse = std::move(packed_leg_to_forward); - new_total_weight_to_forward = INVALID_EDGE_WEIGHT; - - // (*) - // - // Below we have to check if new_total_weight_to_forward is invalid. - // This prevents use-after-move on packed_leg_to_forward. - } - else if (target_phantom.IsValidReverseTarget()) - { - new_total_weight_to_reverse = new_total_weight_to_forward; - packed_leg_to_reverse = packed_leg_to_forward; - } + auto new_total = last.total_nodes_to_reverse[*leg_connections.reverse_to_forward] + + packed_leg_to_forward.size(); + current.total_nodes_to_forward.push_back(new_total); + previous_leg_in_route.push_back( + previousReversePath(*leg_connections.reverse_to_forward)); } else { - search(engine_working_data, - facade, - forward_heap, - reverse_heap, - search_from_forward_node, - search_from_reverse_node, - search_to_forward_node, - search_to_reverse_node, - source_phantom, - target_phantom, - total_weight_to_forward, - total_weight_to_reverse, - new_total_weight_to_forward, - new_total_weight_to_reverse, - packed_leg_to_forward, - packed_leg_to_reverse); + BOOST_ASSERT(new_total_weight_to_forward == INVALID_EDGE_WEIGHT); + current.total_nodes_to_forward.push_back(0); + previous_leg_in_route.push_back(INVALID_LEG_INDEX); } - } - - // Note: To make sure we do not access the moved-from packed_leg_to_forward - // we guard its access by a check for invalid edge weight. See (*) above. - - // No path found for both target nodes? - if ((INVALID_EDGE_WEIGHT == new_total_weight_to_forward) && - (INVALID_EDGE_WEIGHT == new_total_weight_to_reverse)) - { - return raw_route_data; - } - // we need to figure out how the new legs connect to the previous ones - if (current_leg > 0) - { - bool forward_to_forward = - (new_total_weight_to_forward != INVALID_EDGE_WEIGHT) && - // NOLINTNEXTLINE(bugprone-use-after-move) - packed_leg_to_forward.front() == source_phantom.forward_segment_id.id; - bool reverse_to_forward = - (new_total_weight_to_forward != INVALID_EDGE_WEIGHT) && - packed_leg_to_forward.front() == source_phantom.reverse_segment_id.id; - bool forward_to_reverse = - (new_total_weight_to_reverse != INVALID_EDGE_WEIGHT) && - packed_leg_to_reverse.front() == source_phantom.forward_segment_id.id; - bool reverse_to_reverse = - (new_total_weight_to_reverse != INVALID_EDGE_WEIGHT) && - packed_leg_to_reverse.front() == source_phantom.reverse_segment_id.id; - - BOOST_ASSERT(!forward_to_forward || !reverse_to_forward); - BOOST_ASSERT(!forward_to_reverse || !reverse_to_reverse); - - // in this case we always need to copy - if (forward_to_forward && forward_to_reverse) + if (leg_connections.forward_to_reverse) { - // in this case we copy the path leading to the source forward node - // and change the case - total_packed_path_to_reverse = total_packed_path_to_forward; - packed_leg_to_reverse_begin = packed_leg_to_forward_begin; - forward_to_reverse = false; - reverse_to_reverse = true; + auto new_total = last.total_nodes_to_forward[*leg_connections.forward_to_reverse] + + packed_leg_to_reverse.size(); + current.total_nodes_to_reverse.push_back(new_total); + previous_leg_in_route.push_back( + previousForwardPath(*leg_connections.forward_to_reverse)); } - else if (reverse_to_forward && reverse_to_reverse) + else if (leg_connections.reverse_to_reverse) { - total_packed_path_to_forward = total_packed_path_to_reverse; - packed_leg_to_forward_begin = packed_leg_to_reverse_begin; - reverse_to_forward = false; - forward_to_forward = true; + auto new_total = last.total_nodes_to_reverse[*leg_connections.reverse_to_reverse] + + packed_leg_to_reverse.size(); + current.total_nodes_to_reverse.push_back(new_total); + previous_leg_in_route.push_back( + previousReversePath(*leg_connections.reverse_to_reverse)); } - BOOST_ASSERT(!forward_to_forward || !forward_to_reverse); - BOOST_ASSERT(!reverse_to_forward || !reverse_to_reverse); - - // in this case we just need to swap to regain the correct mapping - if (reverse_to_forward || forward_to_reverse) + else { - total_packed_path_to_forward.swap(total_packed_path_to_reverse); - packed_leg_to_forward_begin.swap(packed_leg_to_reverse_begin); + current.total_nodes_to_reverse.push_back(0); + previous_leg_in_route.push_back(INVALID_LEG_INDEX); } } - - if (new_total_weight_to_forward != INVALID_EDGE_WEIGHT) - { - BOOST_ASSERT(target_phantom.IsValidForwardTarget()); - - packed_leg_to_forward_begin.push_back(total_packed_path_to_forward.size()); - total_packed_path_to_forward.insert(total_packed_path_to_forward.end(), - packed_leg_to_forward.begin(), - packed_leg_to_forward.end()); - search_from_forward_node = true; - } else { - total_packed_path_to_forward.clear(); - packed_leg_to_forward_begin.clear(); - search_from_forward_node = false; + previous_leg_in_route.push_back(INVALID_LEG_INDEX); + current.total_nodes_to_forward.push_back(packed_leg_to_forward.size()); + + previous_leg_in_route.push_back(INVALID_LEG_INDEX); + current.total_nodes_to_reverse.push_back(packed_leg_to_reverse.size()); } - if (new_total_weight_to_reverse != INVALID_EDGE_WEIGHT) - { - BOOST_ASSERT(target_phantom.IsValidReverseTarget()); + // Update route paths, weights and reachability + BOOST_ASSERT(new_total_weight_to_forward == INVALID_EDGE_WEIGHT || + candidates.target_phantom.IsValidForwardTarget()); + current.total_weight_to_forward.push_back(new_total_weight_to_forward); + current.reached_forward_node_target.push_back(new_total_weight_to_forward != + INVALID_EDGE_WEIGHT); + + BOOST_ASSERT(new_total_weight_to_reverse == INVALID_EDGE_WEIGHT || + candidates.target_phantom.IsValidReverseTarget()); + current.total_weight_to_reverse.push_back(new_total_weight_to_reverse); + current.reached_reverse_node_target.push_back(new_total_weight_to_reverse != + INVALID_EDGE_WEIGHT); + + packed_leg_begin.push_back(total_packed_paths.size()); + total_packed_paths.insert( + total_packed_paths.end(), packed_leg_to_forward.begin(), packed_leg_to_forward.end()); + packed_leg_begin.push_back(total_packed_paths.size()); + total_packed_paths.insert( + total_packed_paths.end(), packed_leg_to_reverse.begin(), packed_leg_to_reverse.end()); + } - packed_leg_to_reverse_begin.push_back(total_packed_path_to_reverse.size()); - total_packed_path_to_reverse.insert(total_packed_path_to_reverse.end(), - packed_leg_to_reverse.begin(), - packed_leg_to_reverse.end()); - search_from_reverse_node = true; + // Find the final target with the shortest route and backtrack through the legs to find the + // paths that create this route. + std::pair, EdgeWeight> getMinRoute() + { + // Find the segment from final leg with the shortest path + auto forward_range = util::irange(0UL, last.total_weight_to_forward.size()); + auto forward_min = + std::min_element(forward_range.begin(), forward_range.end(), [&](size_t a, size_t b) { + return (last.total_weight_to_forward[a] < last.total_weight_to_forward[b] || + (last.total_weight_to_forward[a] == last.total_weight_to_forward[b] && + last.total_nodes_to_forward[a] < last.total_nodes_to_forward[b])); + }); + auto reverse_range = util::irange(0UL, last.total_weight_to_reverse.size()); + auto reverse_min = + std::min_element(reverse_range.begin(), reverse_range.end(), [&](size_t a, size_t b) { + return (last.total_weight_to_reverse[a] < last.total_weight_to_reverse[b] || + (last.total_weight_to_reverse[a] == last.total_weight_to_reverse[b] && + last.total_nodes_to_reverse[a] < last.total_nodes_to_reverse[b])); + }); + + auto min_weight = INVALID_EDGE_WEIGHT; + std::vector path_indices; + if (last.total_weight_to_forward[*forward_min] < + last.total_weight_to_reverse[*reverse_min] || + (last.total_weight_to_forward[*forward_min] == + last.total_weight_to_reverse[*reverse_min] && + last.total_nodes_to_forward[*forward_min] < last.total_nodes_to_reverse[*reverse_min])) + { + // Get path indices for forward + auto current_path_index = previousForwardPath(*forward_min); + path_indices.push_back(current_path_index); + while (previous_leg_in_route[current_path_index] != INVALID_LEG_INDEX) + { + current_path_index = previous_leg_in_route[current_path_index]; + path_indices.push_back(current_path_index); + } + min_weight = last.total_weight_to_forward[*forward_min]; } else { - total_packed_path_to_reverse.clear(); - packed_leg_to_reverse_begin.clear(); - search_from_reverse_node = false; + // Get path indices for reverse + auto current_path_index = previousReversePath(*reverse_min); + path_indices.push_back(current_path_index); + while (previous_leg_in_route[current_path_index] != INVALID_LEG_INDEX) + { + current_path_index = previous_leg_in_route[current_path_index]; + path_indices.push_back(current_path_index); + } + min_weight = last.total_weight_to_reverse[*reverse_min]; } - prev_packed_leg_to_forward = std::move(packed_leg_to_forward); - prev_packed_leg_to_reverse = std::move(packed_leg_to_reverse); + std::reverse(path_indices.begin(), path_indices.end()); + return std::make_pair(std::move(path_indices), min_weight); + } +}; + +// Requires segment continuation at a waypoint. +// In this case we need to track paths to each of the waypoint candidate forward/reverse segments, +// as each of them could be a leg in route shortest path. +template +InternalRouteResult +shortestPathWithWaypointContinuation(SearchEngineData &engine_working_data, + const DataFacade &facade, + const std::vector &waypoint_candidates) +{ - total_weight_to_forward = new_total_weight_to_forward; - total_weight_to_reverse = new_total_weight_to_reverse; + route_state route(waypoint_candidates.front()); - ++current_leg; - } + initializeHeap(engine_working_data, facade); + auto &forward_heap = *engine_working_data.forward_heap_1; + auto &reverse_heap = *engine_working_data.reverse_heap_1; + + // this implements a dynamic program that finds the shortest route through + // a list of leg endpoints. + for (const auto i : util::irange(0UL, waypoint_candidates.size() - 1)) + { + const auto &source_candidates = waypoint_candidates[i]; + const auto &target_candidates = waypoint_candidates[i + 1]; + // We assume each source candidate for this leg was a target candidate from the previous + // leg, and in the same order. + BOOST_ASSERT(source_candidates.size() == route.last.reached_forward_node_target.size()); + BOOST_ASSERT(source_candidates.size() == route.last.reached_reverse_node_target.size()); + + // We only perform the search for this leg if we reached this waypoint. + // Note that the waypoint can be a valid target, but still not a valid source + // (e.g. if an edge weight is set to be invalid after the phantom node on the way), + // so this doesn't guarantee that it can be used as a source for this leg. + BOOST_ASSERT(i == 0 || + std::any_of(route.last.reached_forward_node_target.begin(), + route.last.reached_forward_node_target.end(), + [](auto v) { return v; }) || + std::any_of(route.last.reached_reverse_node_target.begin(), + route.last.reached_reverse_node_target.end(), + [](auto v) { return v; })); + + for (const auto &target_phantom : target_candidates) + { + PhantomCandidatesToTarget search_candidates{source_candidates, target_phantom}; + EdgeWeight new_total_weight_to_forward = INVALID_EDGE_WEIGHT; + EdgeWeight new_total_weight_to_reverse = INVALID_EDGE_WEIGHT; + + std::vector packed_leg_to_forward; + std::vector packed_leg_to_reverse; + + if (target_phantom.IsValidForwardTarget() || target_phantom.IsValidReverseTarget()) + { + search(engine_working_data, + facade, + forward_heap, + reverse_heap, + route.last.reached_forward_node_target, + route.last.reached_reverse_node_target, + search_candidates, + route.last.total_weight_to_forward, + route.last.total_weight_to_reverse, + new_total_weight_to_forward, + new_total_weight_to_reverse, + packed_leg_to_forward, + packed_leg_to_reverse); + } + + route.addSearchResult(search_candidates, + packed_leg_to_forward, + packed_leg_to_reverse, + new_total_weight_to_forward, + new_total_weight_to_reverse); + } - BOOST_ASSERT(total_weight_to_forward != INVALID_EDGE_WEIGHT || - total_weight_to_reverse != INVALID_EDGE_WEIGHT); + auto has_valid_path = route.completeLeg(); + // No path found for both target nodes? + if (!has_valid_path) + return {}; + }; + + BOOST_ASSERT(std::any_of(route.last.total_weight_to_forward.begin(), + route.last.total_weight_to_forward.end(), + [](const auto weight) { return weight != INVALID_EDGE_WEIGHT; }) || + std::any_of(route.last.total_weight_to_reverse.begin(), + route.last.total_weight_to_reverse.end(), + [](const auto weight) { return weight != INVALID_EDGE_WEIGHT; })); + + route.completeSearch(); + std::vector min_path_indices; + EdgeWeight min_weight; + std::tie(min_path_indices, min_weight) = route.getMinRoute(); + BOOST_ASSERT(min_path_indices.size() + 1 == waypoint_candidates.size()); + + return constructRouteResult(facade, + waypoint_candidates, + min_path_indices, + route.total_packed_paths, + route.packed_leg_begin, + min_weight); +} +} // namespace - // We make sure the fastest route is always in packed_legs_to_forward - if (total_weight_to_forward < total_weight_to_reverse || - (total_weight_to_forward == total_weight_to_reverse && - total_packed_path_to_forward.size() < total_packed_path_to_reverse.size())) +template +InternalRouteResult +shortestPathSearch(SearchEngineData &engine_working_data, + const DataFacade &facade, + const std::vector &waypoint_candidates, + const boost::optional continue_straight_at_waypoint) +{ + const bool allow_uturn_at_waypoint = + !(continue_straight_at_waypoint ? *continue_straight_at_waypoint + : facade.GetContinueStraightDefault()); + + if (allow_uturn_at_waypoint) { - // insert sentinel - packed_leg_to_forward_begin.push_back(total_packed_path_to_forward.size()); - BOOST_ASSERT(packed_leg_to_forward_begin.size() == phantom_nodes_vector.size() + 1); - - unpackLegs(facade, - phantom_nodes_vector, - total_packed_path_to_forward, - packed_leg_to_forward_begin, - total_weight_to_forward, - raw_route_data); + return shortestPathWithWaypointUTurns(engine_working_data, facade, waypoint_candidates); } else { - // insert sentinel - packed_leg_to_reverse_begin.push_back(total_packed_path_to_reverse.size()); - BOOST_ASSERT(packed_leg_to_reverse_begin.size() == phantom_nodes_vector.size() + 1); - - unpackLegs(facade, - phantom_nodes_vector, - total_packed_path_to_reverse, - packed_leg_to_reverse_begin, - total_weight_to_reverse, - raw_route_data); + return shortestPathWithWaypointContinuation( + engine_working_data, facade, waypoint_candidates); } - - return raw_route_data; } } // namespace routing_algorithms diff --git a/include/server/api/base_parameters_grammar.hpp b/include/server/api/base_parameters_grammar.hpp index 7d24f909b7b..827855acf0b 100644 --- a/include/server/api/base_parameters_grammar.hpp +++ b/include/server/api/base_parameters_grammar.hpp @@ -78,10 +78,17 @@ struct BaseParametersGrammar : boost::spirit::qi::grammar : BaseParametersGrammar::base_type(root_rule) { const auto add_hint = [](engine::api::BaseParameters &base_parameters, - const boost::optional &hint_string) { - if (hint_string) + const std::vector &hint_strings) { + if (!hint_strings.empty()) { - base_parameters.hints.emplace_back(engine::Hint::FromBase64(hint_string.get())); + std::vector location_hints(hint_strings.size()); + std::transform(hint_strings.begin(), + hint_strings.end(), + location_hints.begin(), + [](const auto &hint_string) { + return engine::SegmentHint::FromBase64(hint_string); + }); + base_parameters.hints.push_back(engine::Hint{std::move(location_hints)}); } else { @@ -145,10 +152,11 @@ struct BaseParametersGrammar : boost::spirit::qi::grammar (-(qi::double_ | unlimited_rule) % ';')[ph::bind(&engine::api::BaseParameters::radiuses, qi::_r1) = qi::_1]; - hints_rule = qi::lit("hints=") > - (-qi::as_string[qi::repeat(engine::ENCODED_HINT_SIZE)[base64_char]])[ph::bind( - add_hint, qi::_r1, qi::_1)] % - ';'; + hints_rule = + qi::lit("hints=") > + (*qi::as_string[qi::repeat(engine::ENCODED_SEGMENT_HINT_SIZE)[base64_char]])[ph::bind( + add_hint, qi::_r1, qi::_1)] % + ';'; generate_hints_rule = qi::lit("generate_hints=") > diff --git a/include/util/static_rtree.hpp b/include/util/static_rtree.hpp index 333855974f1..1e8cfe2bcd8 100644 --- a/include/util/static_rtree.hpp +++ b/include/util/static_rtree.hpp @@ -68,7 +68,7 @@ write(storage::tar::FileWriter &writer, /*** * Static RTree for serving nearest neighbour queries - * // All coordinates are pojected first to Web Mercator before the bounding boxes + * // All coordinates are projected first to Web Mercator before the bounding boxes * // are computed, this means the internal distance metric doesn not represent meters! */ @@ -556,8 +556,8 @@ class StaticRTree } // Override filter and terminator for the desired behaviour. - std::vector Nearest(const Coordinate input_coordinate, - const std::size_t max_results) const + std::vector Nearest(const Coordinate input_coordinate, + const std::size_t max_results) const { return Nearest( input_coordinate, @@ -567,13 +567,13 @@ class StaticRTree }); } - // Override filter and terminator for the desired behaviour. + // Return edges in distance order with the coordinate of the closest point on the edge. template - std::vector Nearest(const Coordinate input_coordinate, - const FilterT filter, - const TerminationT terminate) const + std::vector Nearest(const Coordinate input_coordinate, + const FilterT filter, + const TerminationT terminate) const { - std::vector results; + std::vector results; auto projected_coordinate = web_mercator::fromWGS84(input_coordinate); Coordinate fixed_projected_coordinate{projected_coordinate}; // initialize queue with root element @@ -603,10 +603,10 @@ class StaticRTree } else { // current candidate is an actual road segment - // We deliberatly make a copy here, we mutate the value below - auto edge_data = m_objects[current_query_node.segment_index]; - const auto ¤t_candidate = - CandidateSegment{current_query_node.fixed_projected_coordinate, edge_data}; + const auto &edge_data = m_objects[current_query_node.segment_index]; + // We deliberately make an edge data copy here, we mutate the value below + CandidateSegment current_candidate{current_query_node.fixed_projected_coordinate, + edge_data}; // to allow returns of no-results if too restrictive filtering, this needs to be // done here even though performance would indicate that we want to stop after @@ -621,11 +621,11 @@ class StaticRTree { continue; } - edge_data.forward_segment_id.enabled &= use_segment.first; - edge_data.reverse_segment_id.enabled &= use_segment.second; + current_candidate.data.forward_segment_id.enabled &= use_segment.first; + current_candidate.data.reverse_segment_id.enabled &= use_segment.second; // store phantom node in result vector - results.push_back(std::move(edge_data)); + results.push_back(std::move(current_candidate)); } } @@ -676,7 +676,7 @@ class StaticRTree * Iterates over all the children of a TreeNode and inserts them into the search * priority queue using their distance from the search coordinate as the * priority metric. - * The closests distance to a box from our point is also the closest distance + * The closest distance to a box from our point is also the closest distance * to the closest line in that box (assuming the boxes hug their contents). */ template diff --git a/src/engine/api/json_factory.cpp b/src/engine/api/json_factory.cpp index a0f71e50ec8..c2bcbb84150 100644 --- a/src/engine/api/json_factory.cpp +++ b/src/engine/api/json_factory.cpp @@ -239,10 +239,10 @@ makeWaypoint(const util::Coordinate &location, const double &distance, std::stri util::json::Object makeWaypoint(const util::Coordinate &location, const double &distance, std::string name, - const Hint &hint) + const Hint &location_hints) { auto waypoint = makeWaypoint(location, distance, std::move(name)); - waypoint.values["hint"] = hint.ToBase64(); + waypoint.values["hint"] = location_hints.ToBase64(); return waypoint; } diff --git a/src/engine/hint.cpp b/src/engine/hint.cpp index 6bc246c9d3c..f7cad73a1a7 100644 --- a/src/engine/hint.cpp +++ b/src/engine/hint.cpp @@ -3,10 +3,10 @@ #include "engine/datafacade/datafacade_base.hpp" #include +#include #include #include -#include #include namespace osrm @@ -14,8 +14,8 @@ namespace osrm namespace engine { -bool Hint::IsValid(const util::Coordinate new_input_coordinates, - const datafacade::BaseDataFacade &facade) const +bool SegmentHint::IsValid(const util::Coordinate new_input_coordinates, + const datafacade::BaseDataFacade &facade) const { auto is_same_input_coordinate = new_input_coordinates.lon == phantom.input_location.lon && new_input_coordinates.lat == phantom.input_location.lat; @@ -25,7 +25,7 @@ bool Hint::IsValid(const util::Coordinate new_input_coordinates, return is_same_input_coordinate && phantom.IsValid() && facade.GetCheckSum() == data_checksum; } -std::string Hint::ToBase64() const +std::string SegmentHint::ToBase64() const { auto base64 = encodeBase64Bytewise(*this); @@ -36,9 +36,9 @@ std::string Hint::ToBase64() const return base64; } -Hint Hint::FromBase64(const std::string &base64Hint) +SegmentHint SegmentHint::FromBase64(const std::string &base64Hint) { - BOOST_ASSERT_MSG(base64Hint.size() == ENCODED_HINT_SIZE, "Hint has invalid size"); + BOOST_ASSERT_MSG(base64Hint.size() == ENCODED_SEGMENT_HINT_SIZE, "Hint has invalid size"); // We need mutability but don't want to change the API auto encoded = base64Hint; @@ -47,15 +47,82 @@ Hint Hint::FromBase64(const std::string &base64Hint) std::replace(begin(encoded), end(encoded), '-', '+'); std::replace(begin(encoded), end(encoded), '_', '/'); - return decodeBase64Bytewise(encoded); + return decodeBase64Bytewise(encoded); } -bool operator==(const Hint &lhs, const Hint &rhs) +bool operator==(const SegmentHint &lhs, const SegmentHint &rhs) { return std::tie(lhs.phantom, lhs.data_checksum) == std::tie(rhs.phantom, rhs.data_checksum); } -std::ostream &operator<<(std::ostream &out, const Hint &hint) { return out << hint.ToBase64(); } +bool operator!=(const SegmentHint &lhs, const SegmentHint &rhs) { return !(lhs == rhs); } + +std::ostream &operator<<(std::ostream &out, const SegmentHint &hint) +{ + return out << hint.ToBase64(); +} + +std::string Hint::ToBase64() const +{ + std::string res; + for (const auto &hint : segment_hints) + { + res += hint.ToBase64(); + } + return res; +} + +Hint Hint::FromBase64(const std::string &base64Hint) +{ + + BOOST_ASSERT_MSG(base64Hint.size() % ENCODED_SEGMENT_HINT_SIZE == 0, + "SegmentHint has invalid size"); + + auto num_hints = base64Hint.size() / ENCODED_SEGMENT_HINT_SIZE; + std::vector res(num_hints); + + for (const auto i : util::irange(0UL, num_hints)) + { + auto start_offset = i * ENCODED_SEGMENT_HINT_SIZE; + auto end_offset = start_offset + ENCODED_SEGMENT_HINT_SIZE; + res[i] = SegmentHint::FromBase64( + std::string(base64Hint.begin() + start_offset, base64Hint.begin() + end_offset)); + } + + return {std::move(res)}; +} + +bool Hint::IsValid(const util::Coordinate new_input_coordinates, + const datafacade::BaseDataFacade &facade) const +{ + const auto all_valid = + std::all_of(segment_hints.begin(), segment_hints.end(), [&](const auto &seg_hint) { + return seg_hint.IsValid(new_input_coordinates, facade); + }); + if (!all_valid) + { + return false; + } + + // Check hints do not contain duplicate segment pairs + // We can't allow duplicates as search heaps do not support it. + boost::unordered_set forward_segments; + boost::unordered_set reverse_segments; + for (const auto &seg_hint : segment_hints) + { + const auto forward_res = forward_segments.insert(seg_hint.phantom.forward_segment_id.id); + if (!forward_res.second) + { + return false; + } + const auto backward_res = reverse_segments.insert(seg_hint.phantom.reverse_segment_id.id); + if (!backward_res.second) + { + return false; + } + } + return true; +} } // namespace engine } // namespace osrm diff --git a/src/engine/plugins/match.cpp b/src/engine/plugins/match.cpp index 59d7fccdae3..0400bb6f565 100644 --- a/src/engine/plugins/match.cpp +++ b/src/engine/plugins/match.cpp @@ -4,21 +4,16 @@ #include "engine/api/match_api.hpp" #include "engine/api/match_parameters.hpp" #include "engine/api/match_parameters_tidy.hpp" -#include "engine/map_matching/bayes_classifier.hpp" #include "engine/map_matching/sub_matching.hpp" #include "util/coordinate_calculation.hpp" #include "util/integer_range.hpp" -#include "util/json_util.hpp" -#include "util/string_util.hpp" #include #include #include -#include #include #include -#include #include namespace osrm @@ -28,7 +23,7 @@ namespace engine namespace plugins { -// Filters PhantomNodes to obtain a set of viable candiates +// Filters PhantomNodes to obtain a set of viable candidates void filterCandidates(const std::vector &coordinates, MatchPlugin::CandidateLists &candidates_lists) { @@ -272,20 +267,26 @@ Status MatchPlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms, // FIXME we only run this to obtain the geometry // The clean way would be to get this directly from the map matching plugin - PhantomNodes current_phantom_node_pair; for (unsigned i = 0; i < sub_matchings[index].nodes.size() - 1; ++i) { - current_phantom_node_pair.source_phantom = sub_matchings[index].nodes[i]; - current_phantom_node_pair.target_phantom = sub_matchings[index].nodes[i + 1]; - BOOST_ASSERT(current_phantom_node_pair.source_phantom.IsValid()); - BOOST_ASSERT(current_phantom_node_pair.target_phantom.IsValid()); - sub_routes[index].segment_end_coordinates.emplace_back(current_phantom_node_pair); + PhantomEndpoints current_endpoints{sub_matchings[index].nodes[i], + sub_matchings[index].nodes[i + 1]}; + BOOST_ASSERT(current_endpoints.source_phantom.IsValid()); + BOOST_ASSERT(current_endpoints.target_phantom.IsValid()); + sub_routes[index].leg_endpoints.push_back(current_endpoints); } + + std::vector waypoint_candidates; + waypoint_candidates.reserve(sub_matchings[index].nodes.size()); + std::transform(sub_matchings[index].nodes.begin(), + sub_matchings[index].nodes.end(), + std::back_inserter(waypoint_candidates), + [](const auto &phantom) { return PhantomNodeCandidates{phantom}; }); + // force uturns to be on // we split the phantom nodes anyway and only have bi-directional phantom nodes for // possible uturns - sub_routes[index] = - algorithms.ShortestPathSearch(sub_routes[index].segment_end_coordinates, {false}); + sub_routes[index] = algorithms.ShortestPathSearch(waypoint_candidates, {false}); BOOST_ASSERT(sub_routes[index].shortest_path_weight != INVALID_EDGE_WEIGHT); if (collapse_legs) { diff --git a/src/engine/plugins/nearest.cpp b/src/engine/plugins/nearest.cpp index b01d66dc87a..96a67b13ddb 100644 --- a/src/engine/plugins/nearest.cpp +++ b/src/engine/plugins/nearest.cpp @@ -1,10 +1,7 @@ #include "engine/plugins/nearest.hpp" #include "engine/api/nearest_api.hpp" #include "engine/api/nearest_parameters.hpp" -#include "engine/phantom_node.hpp" -#include "util/integer_range.hpp" -#include #include #include diff --git a/src/engine/plugins/table.cpp b/src/engine/plugins/table.cpp index 77760f71db8..89239a3ee50 100644 --- a/src/engine/plugins/table.cpp +++ b/src/engine/plugins/table.cpp @@ -2,17 +2,11 @@ #include "engine/api/table_api.hpp" #include "engine/api/table_parameters.hpp" -#include "engine/routing_algorithms/many_to_many.hpp" -#include "engine/search_engine_data.hpp" #include "util/coordinate_calculation.hpp" -#include "util/json_container.hpp" #include "util/string_util.hpp" #include -#include -#include -#include #include #include @@ -47,7 +41,7 @@ Status TablePlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms, return Error("InvalidOptions", "Coordinates are invalid", result); } - if (params.bearings.size() > 0 && params.coordinates.size() != params.bearings.size()) + if (!params.bearings.empty() && params.coordinates.size() != params.bearings.size()) { return Error( "InvalidOptions", "Number of bearings does not match number of coordinates", result); @@ -79,7 +73,7 @@ Status TablePlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms, "NoSegment", MissingPhantomErrorMessage(phantom_nodes, params.coordinates), result); } - auto snapped_phantoms = SnapPhantomNodes(phantom_nodes); + auto snapped_phantoms = SnapPhantomNodes(std::move(phantom_nodes)); bool request_distance = params.annotations & api::TableParameters::AnnotationsType::Distance; bool request_duration = params.annotations & api::TableParameters::AnnotationsType::Duration; @@ -117,9 +111,11 @@ Status TablePlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms, params.fallback_coordinate_type == api::TableParameters::FallbackCoordinateType::Input ? util::coordinate_calculation::greatCircleDistance( - source.input_location, destination.input_location) + candidatesInputLocation(source), + candidatesInputLocation(destination)) : util::coordinate_calculation::greatCircleDistance( - source.location, destination.location); + candidatesSnappedLocation(source), + candidatesSnappedLocation(destination)); result_tables_pair.first[table_index] = distance_estimate / (double)params.fallback_speed; diff --git a/src/engine/plugins/tile.cpp b/src/engine/plugins/tile.cpp index 5a899edd447..65f35af57f5 100644 --- a/src/engine/plugins/tile.cpp +++ b/src/engine/plugins/tile.cpp @@ -20,7 +20,6 @@ #include #include #include -#include #include #include diff --git a/src/engine/plugins/trip.cpp b/src/engine/plugins/trip.cpp index 20daf95d917..63f65e0f0a3 100644 --- a/src/engine/plugins/trip.cpp +++ b/src/engine/plugins/trip.cpp @@ -4,18 +4,12 @@ #include "engine/api/trip_parameters.hpp" #include "engine/trip/trip_brute_force.hpp" #include "engine/trip/trip_farthest_insertion.hpp" -#include "engine/trip/trip_nearest_neighbour.hpp" #include "util/dist_table_wrapper.hpp" // to access the dist table more easily -#include "util/json_container.hpp" #include #include -#include -#include #include -#include -#include #include #include @@ -41,40 +35,33 @@ bool IsSupportedParameterCombination(const bool fixed_start, // given the node order in which to visit, compute the actual route (with geometry, travel time and // so on) and return the result -InternalRouteResult TripPlugin::ComputeRoute(const RoutingAlgorithmsInterface &algorithms, - const std::vector &snapped_phantoms, - const std::vector &trip, - const bool roundtrip) const +InternalRouteResult +TripPlugin::ComputeRoute(const RoutingAlgorithmsInterface &algorithms, + const std::vector &waypoint_candidates, + const std::vector &trip, + const bool roundtrip) const { - InternalRouteResult min_route; - // given the final trip, compute total duration and return the route and location permutation - PhantomNodes viapoint; - - // computes a roundtrip from the nodes in trip - for (auto node = trip.begin(); node < trip.end() - 1; ++node) - { - const auto from_node = *node; - const auto to_node = *std::next(node); - - viapoint = PhantomNodes{snapped_phantoms[from_node], snapped_phantoms[to_node]}; - min_route.segment_end_coordinates.emplace_back(viapoint); - } + // TODO make a more efficient solution that doesn't require copying all the waypoints vectors. + std::vector trip_candidates; + std::transform(trip.begin(), + trip.end(), + std::back_inserter(trip_candidates), + [&](const auto &node) { return waypoint_candidates[node]; }); // return back to the first node if it is a round trip if (roundtrip) { - viapoint = PhantomNodes{snapped_phantoms[trip.back()], snapped_phantoms[trip.front()]}; - min_route.segment_end_coordinates.emplace_back(viapoint); + trip_candidates.push_back(waypoint_candidates[trip.front()]); // trip comes out to be something like 0 1 4 3 2 0 - BOOST_ASSERT(min_route.segment_end_coordinates.size() == trip.size()); + BOOST_ASSERT(trip_candidates.size() == trip.size() + 1); } else { - // trip comes out to be something like 0 1 4 3 2, so the sizes don't match - BOOST_ASSERT(min_route.segment_end_coordinates.size() == trip.size() - 1); + // trip comes out to be something like 0 1 4 3 2 + BOOST_ASSERT(trip_candidates.size() == trip.size()); } - min_route = algorithms.ShortestPathSearch(min_route.segment_end_coordinates, {false}); + auto min_route = algorithms.ShortestPathSearch(trip_candidates, {false}); BOOST_ASSERT_MSG(min_route.shortest_path_weight < INVALID_EDGE_WEIGHT, "unroutable route"); return min_route; } @@ -226,7 +213,7 @@ Status TripPlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms, return Error("InvalidValue", "Invalid source or destination value.", result); } - auto snapped_phantoms = SnapPhantomNodes(phantom_node_pairs); + auto snapped_phantoms = SnapPhantomNodes(std::move(phantom_node_pairs)); BOOST_ASSERT(snapped_phantoms.size() == number_of_locations); diff --git a/src/engine/plugins/viaroute.cpp b/src/engine/plugins/viaroute.cpp index a95d1781082..bc71adceb71 100644 --- a/src/engine/plugins/viaroute.cpp +++ b/src/engine/plugins/viaroute.cpp @@ -5,12 +5,10 @@ #include "util/for_each_pair.hpp" #include "util/integer_range.hpp" -#include "util/json_container.hpp" #include #include -#include #include #include @@ -95,19 +93,10 @@ Status ViaRoutePlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithm } BOOST_ASSERT(phantom_node_pairs.size() == route_parameters.coordinates.size()); - auto snapped_phantoms = SnapPhantomNodes(phantom_node_pairs); - - std::vector start_end_nodes; - auto build_phantom_pairs = [&start_end_nodes](const PhantomNode &first_node, - const PhantomNode &second_node) { - start_end_nodes.push_back(PhantomNodes{first_node, second_node}); - }; - util::for_each_pair(snapped_phantoms, build_phantom_pairs); + auto snapped_phantoms = SnapPhantomNodes(std::move(phantom_node_pairs)); api::RouteAPI route_api{facade, route_parameters}; - InternalManyRoutesResult routes; - // TODO: in v6 we should remove the boolean and only keep the number parameter. // For now just force them to be in sync. and keep backwards compatibility. const auto wants_alternatives = @@ -115,20 +104,23 @@ Status ViaRoutePlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithm (route_parameters.alternatives || route_parameters.number_of_alternatives > 0); const auto number_of_alternatives = std::max(1u, route_parameters.number_of_alternatives); + InternalManyRoutesResult routes; // Alternatives do not support vias, only direct s,t queries supported // See the implementation notes and high-level outline. // https://github.com/Project-OSRM/osrm-backend/issues/3905 - if (1 == start_end_nodes.size() && algorithms.HasAlternativePathSearch() && wants_alternatives) + if (2 == snapped_phantoms.size() && algorithms.HasAlternativePathSearch() && wants_alternatives) { - routes = algorithms.AlternativePathSearch(start_end_nodes.front(), number_of_alternatives); + routes = algorithms.AlternativePathSearch({snapped_phantoms[0], snapped_phantoms[1]}, + number_of_alternatives); } - else if (1 == start_end_nodes.size() && algorithms.HasDirectShortestPathSearch()) + else if (2 == snapped_phantoms.size() && algorithms.HasDirectShortestPathSearch()) { - routes = algorithms.DirectShortestPathSearch(start_end_nodes.front()); + routes = algorithms.DirectShortestPathSearch({snapped_phantoms[0], snapped_phantoms[1]}); } else { - routes = algorithms.ShortestPathSearch(start_end_nodes, route_parameters.continue_straight); + routes = + algorithms.ShortestPathSearch(snapped_phantoms, route_parameters.continue_straight); } // The post condition for all path searches is we have at least one route in our result. @@ -160,18 +152,29 @@ Status ViaRoutePlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithm } } - route_api.MakeResponse(routes, start_end_nodes, result); + route_api.MakeResponse(routes, snapped_phantoms, result); } else { - auto first_component_id = snapped_phantoms.front().component.id; - auto not_in_same_component = std::any_of(snapped_phantoms.begin(), - snapped_phantoms.end(), - [first_component_id](const PhantomNode &node) { - return node.component.id != first_component_id; - }); - - if (not_in_same_component) + const auto all_in_same_component = + [](const std::vector &waypoint_candidates) { + return std::any_of(waypoint_candidates.front().begin(), + waypoint_candidates.front().end(), + // For each of the first possible phantoms, check if all other + // positions in the list have a phantom from the same component. + [&](const PhantomNode &phantom) { + const auto component_id = phantom.component.id; + return std::all_of( + std::next(waypoint_candidates.begin()), + std::end(waypoint_candidates), + [component_id](const PhantomNodeCandidates &candidates) { + return candidatesHaveComponent(candidates, + component_id); + }); + }); + }; + + if (!all_in_same_component(snapped_phantoms)) { return Error("NoRoute", "Impossible route between points", result); } diff --git a/src/engine/routing_algorithms/alternative_path_ch.cpp b/src/engine/routing_algorithms/alternative_path_ch.cpp index 00be0794dce..d81ebabccc4 100644 --- a/src/engine/routing_algorithms/alternative_path_ch.cpp +++ b/src/engine/routing_algorithms/alternative_path_ch.cpp @@ -190,8 +190,8 @@ void computeWeightAndSharingOfViaPath(SearchEngineData &engine_workin s_v_middle, upper_bound_s_v_path_weight, min_edge_offset, - DO_NOT_FORCE_LOOPS, - DO_NOT_FORCE_LOOPS); + {}, + {}); } // compute path by reusing backward search from node t NodeID v_t_middle = SPECIAL_NODEID; @@ -205,8 +205,8 @@ void computeWeightAndSharingOfViaPath(SearchEngineData &engine_workin v_t_middle, upper_bound_of_v_t_path_weight, min_edge_offset, - DO_NOT_FORCE_LOOPS, - DO_NOT_FORCE_LOOPS); + {}, + {}); } *real_weight_of_via_path = upper_bound_s_v_path_weight + upper_bound_of_v_t_path_weight; @@ -351,8 +351,8 @@ bool viaNodeCandidatePassesTTest(SearchEngineData &engine_working_dat *s_v_middle, upper_bound_s_v_path_weight, min_edge_offset, - DO_NOT_FORCE_LOOPS, - DO_NOT_FORCE_LOOPS); + {}, + {}); } if (INVALID_EDGE_WEIGHT == upper_bound_s_v_path_weight) @@ -372,8 +372,8 @@ bool viaNodeCandidatePassesTTest(SearchEngineData &engine_working_dat *v_t_middle, upper_bound_of_v_t_path_weight, min_edge_offset, - DO_NOT_FORCE_LOOPS, - DO_NOT_FORCE_LOOPS); + {}, + {}); } if (INVALID_EDGE_WEIGHT == upper_bound_of_v_t_path_weight) @@ -539,25 +539,13 @@ bool viaNodeCandidatePassesTTest(SearchEngineData &engine_working_dat { if (!forward_heap3.Empty()) { - routingStep(facade, - forward_heap3, - reverse_heap3, - middle, - upper_bound, - min_edge_offset, - DO_NOT_FORCE_LOOPS, - DO_NOT_FORCE_LOOPS); + routingStep( + facade, forward_heap3, reverse_heap3, middle, upper_bound, min_edge_offset, {}, {}); } if (!reverse_heap3.Empty()) { - routingStep(facade, - reverse_heap3, - forward_heap3, - middle, - upper_bound, - min_edge_offset, - DO_NOT_FORCE_LOOPS, - DO_NOT_FORCE_LOOPS); + routingStep( + facade, reverse_heap3, forward_heap3, middle, upper_bound, min_edge_offset, {}, {}); } } return (upper_bound <= t_test_path_weight); @@ -566,15 +554,12 @@ bool viaNodeCandidatePassesTTest(SearchEngineData &engine_working_dat InternalManyRoutesResult alternativePathSearch(SearchEngineData &engine_working_data, const DataFacade &facade, - const PhantomNodes &phantom_node_pair, + const PhantomEndpointCandidates &endpoint_candidates, unsigned /*number_of_alternatives*/) { InternalRouteResult primary_route; InternalRouteResult secondary_route; - primary_route.segment_end_coordinates = {phantom_node_pair}; - secondary_route.segment_end_coordinates = {phantom_node_pair}; - std::vector alternative_path; std::vector via_node_candidate_list; std::vector forward_search_space; @@ -592,15 +577,13 @@ InternalManyRoutesResult alternativePathSearch(SearchEngineData &engi EdgeWeight upper_bound_to_shortest_path_weight = INVALID_EDGE_WEIGHT; NodeID middle_node = SPECIAL_NODEID; - const EdgeWeight min_edge_offset = - std::min(phantom_node_pair.source_phantom.forward_segment_id.enabled - ? -phantom_node_pair.source_phantom.GetForwardWeightPlusOffset() - : 0, - phantom_node_pair.source_phantom.reverse_segment_id.enabled - ? -phantom_node_pair.source_phantom.GetReverseWeightPlusOffset() - : 0); - insertNodesInHeaps(forward_heap1, reverse_heap1, phantom_node_pair); + insertNodesInHeaps(forward_heap1, reverse_heap1, endpoint_candidates); + // get offset to account for offsets on phantom nodes on compressed edges + EdgeWeight min_edge_offset = forward_heap1.Empty() ? 0 : std::min(0, forward_heap1.MinKey()); + BOOST_ASSERT(min_edge_offset <= 0); + // we only every insert negative offsets for nodes in the forward heap + BOOST_ASSERT(reverse_heap1.Empty() || reverse_heap1.MinKey() >= 0); // search from s and t till new_min/(1+epsilon) > weight_of_shortest_path while (0 < (forward_heap1.Size() + reverse_heap1.Size())) @@ -790,7 +773,7 @@ InternalManyRoutesResult alternativePathSearch(SearchEngineData &engi &v_t_middle, min_edge_offset)) { - // select first admissable + // select first admissible selected_via_node = candidate.node; break; } @@ -799,20 +782,23 @@ InternalManyRoutesResult alternativePathSearch(SearchEngineData &engi // Unpack shortest path and alternative, if they exist if (INVALID_EDGE_WEIGHT != upper_bound_to_shortest_path_weight) { + auto phantom_endpoints = endpointsFromCandidates(endpoint_candidates, packed_shortest_path); + primary_route.leg_endpoints = {phantom_endpoints}; + BOOST_ASSERT(!packed_shortest_path.empty()); primary_route.unpacked_path_segments.resize(1); primary_route.source_traversed_in_reverse.push_back( (packed_shortest_path.front() != - phantom_node_pair.source_phantom.forward_segment_id.id)); + phantom_endpoints.source_phantom.forward_segment_id.id)); primary_route.target_traversed_in_reverse.push_back(( - packed_shortest_path.back() != phantom_node_pair.target_phantom.forward_segment_id.id)); + packed_shortest_path.back() != phantom_endpoints.target_phantom.forward_segment_id.id)); unpackPath(facade, // -- packed input packed_shortest_path.begin(), packed_shortest_path.end(), // -- start of route - phantom_node_pair, + phantom_endpoints, // -- unpacked output primary_route.unpacked_path_segments.front()); primary_route.shortest_path_weight = upper_bound_to_shortest_path_weight; @@ -830,19 +816,23 @@ InternalManyRoutesResult alternativePathSearch(SearchEngineData &engi v_t_middle, packed_alternate_path); + auto phantom_endpoints = + endpointsFromCandidates(endpoint_candidates, packed_alternate_path); + secondary_route.leg_endpoints = {phantom_endpoints}; + secondary_route.unpacked_path_segments.resize(1); secondary_route.source_traversed_in_reverse.push_back( (packed_alternate_path.front() != - phantom_node_pair.source_phantom.forward_segment_id.id)); + phantom_endpoints.source_phantom.forward_segment_id.id)); secondary_route.target_traversed_in_reverse.push_back( (packed_alternate_path.back() != - phantom_node_pair.target_phantom.forward_segment_id.id)); + phantom_endpoints.target_phantom.forward_segment_id.id)); // unpack the alternate path unpackPath(facade, packed_alternate_path.begin(), packed_alternate_path.end(), - phantom_node_pair, + phantom_endpoints, secondary_route.unpacked_path_segments.front()); secondary_route.shortest_path_weight = weight_of_via_path; diff --git a/src/engine/routing_algorithms/alternative_path_mld.cpp b/src/engine/routing_algorithms/alternative_path_mld.cpp index c22419c67fd..7020e8a8a6a 100644 --- a/src/engine/routing_algorithms/alternative_path_mld.cpp +++ b/src/engine/routing_algorithms/alternative_path_mld.cpp @@ -133,12 +133,13 @@ double getLongerByFactorBasedOnDuration(const EdgeWeight duration) return a + b / (duration - d) + c / std::pow(duration - d, 3); } -Parameters parametersFromRequest(const PhantomNodes &phantom_node_pair) +Parameters parametersFromRequest(const PhantomEndpointCandidates &endpoint_candidates) { Parameters parameters; const auto distance = util::coordinate_calculation::greatCircleDistance( - phantom_node_pair.source_phantom.location, phantom_node_pair.target_phantom.location); + candidatesSnappedLocation(endpoint_candidates.source_phantoms), + candidatesSnappedLocation(endpoint_candidates.target_phantoms)); // 10km if (distance < 10000.) @@ -547,7 +548,7 @@ void unpackPackedPaths(InputIt first, OutIt out, SearchEngineData &search_engine_data, const Facade &facade, - const PhantomNodes &phantom_node_pair) + const PhantomEndpointCandidates &endpoint_candidates) { util::static_assert_iter_category(); util::static_assert_iter_category(); @@ -600,7 +601,7 @@ void unpackPackedPaths(InputIt first, } else { // an overlay graph edge - LevelID level = getNodeQueryLevel(partition, source, phantom_node_pair); // XXX + LevelID level = getNodeQueryLevel(partition, source, endpoint_candidates); // XXX CellID parent_cell_id = partition.GetCell(level, source); BOOST_ASSERT(parent_cell_id == partition.GetCell(level, target)); @@ -624,8 +625,8 @@ void unpackPackedPaths(InputIt first, facade, forward_heap, reverse_heap, - DO_NOT_FORCE_LOOPS, - DO_NOT_FORCE_LOOPS, + {}, + {}, INVALID_EDGE_WEIGHT, sublevel, parent_cell_id); @@ -656,13 +657,13 @@ void unpackPackedPaths(InputIt first, inline std::vector makeCandidateVias(SearchEngineData &search_engine_data, const Facade &facade, - const PhantomNodes &phantom_node_pair, + const PhantomEndpointCandidates &endpoint_candidates, const Parameters ¶meters) { Heap &forward_heap = *search_engine_data.forward_heap_1; Heap &reverse_heap = *search_engine_data.reverse_heap_1; - insertNodesInHeaps(forward_heap, reverse_heap, phantom_node_pair); + insertNodesInHeaps(forward_heap, reverse_heap, endpoint_candidates); if (forward_heap.Empty() || reverse_heap.Empty()) { return {}; @@ -712,9 +713,9 @@ makeCandidateVias(SearchEngineData &search_engine_data, reverse_heap, overlap_via, overlap_weight, - DO_NOT_FORCE_LOOPS, - DO_NOT_FORCE_LOOPS, - phantom_node_pair); + {}, + {}, + endpoint_candidates); if (!forward_heap.Empty()) forward_heap_min = forward_heap.MinKey(); @@ -738,9 +739,9 @@ makeCandidateVias(SearchEngineData &search_engine_data, forward_heap, overlap_via, overlap_weight, - DO_NOT_FORCE_LOOPS, - DO_NOT_FORCE_LOOPS, - phantom_node_pair); + {}, + {}, + endpoint_candidates); if (!reverse_heap.Empty()) reverse_heap_min = reverse_heap.MinKey(); @@ -776,10 +777,10 @@ makeCandidateVias(SearchEngineData &search_engine_data, // https://github.com/Project-OSRM/osrm-backend/issues/3905 InternalManyRoutesResult alternativePathSearch(SearchEngineData &search_engine_data, const Facade &facade, - const PhantomNodes &phantom_node_pair, + const PhantomEndpointCandidates &endpoint_candidates, unsigned number_of_alternatives) { - Parameters parameters = parametersFromRequest(phantom_node_pair); + Parameters parameters = parametersFromRequest(endpoint_candidates); const auto max_number_of_alternatives = number_of_alternatives; const auto max_number_of_alternatives_to_unpack = @@ -798,7 +799,7 @@ InternalManyRoutesResult alternativePathSearch(SearchEngineData &sear // Do forward and backward search, save search space overlap as via candidates. auto candidate_vias = - makeCandidateVias(search_engine_data, facade, phantom_node_pair, parameters); + makeCandidateVias(search_engine_data, facade, endpoint_candidates, parameters); const auto by_weight = [](const auto &lhs, const auto &rhs) { return lhs.weight < rhs.weight; }; auto shortest_path_via_it = @@ -813,8 +814,6 @@ InternalManyRoutesResult alternativePathSearch(SearchEngineData &sear if (!has_shortest_path) { InternalRouteResult invalid; - invalid.shortest_path_weight = INVALID_EDGE_WEIGHT; - invalid.segment_end_coordinates = {phantom_node_pair}; return invalid; } @@ -900,7 +899,7 @@ InternalManyRoutesResult alternativePathSearch(SearchEngineData &sear std::back_inserter(unpacked_paths), search_engine_data, facade, - phantom_node_pair); + endpoint_candidates); // // Filter and rank a second time. This time instead of being fast and doing @@ -927,7 +926,7 @@ InternalManyRoutesResult alternativePathSearch(SearchEngineData &sear routes.reserve(number_of_unpacked_paths); const auto unpacked_path_to_route = [&](const WeightedViaNodeUnpackedPath &path) { - return extractRoute(facade, path.via.weight, phantom_node_pair, path.nodes, path.edges); + return extractRoute(facade, path.via.weight, endpoint_candidates, path.nodes, path.edges); }; std::transform(unpacked_paths_first, diff --git a/src/engine/routing_algorithms/direct_shortest_path.cpp b/src/engine/routing_algorithms/direct_shortest_path.cpp index 08ca3249e24..267ecf2f47e 100644 --- a/src/engine/routing_algorithms/direct_shortest_path.cpp +++ b/src/engine/routing_algorithms/direct_shortest_path.cpp @@ -19,7 +19,7 @@ namespace routing_algorithms template <> InternalRouteResult directShortestPathSearch(SearchEngineData &engine_working_data, const DataFacade &facade, - const PhantomNodes &phantom_nodes) + const PhantomEndpointCandidates &endpoint_candidates) { engine_working_data.InitializeOrClearFirstThreadLocalStorage(facade.GetNumberOfNodes()); auto &forward_heap = *engine_working_data.forward_heap_1; @@ -29,7 +29,7 @@ InternalRouteResult directShortestPathSearch(SearchEngineData &en EdgeWeight weight = INVALID_EDGE_WEIGHT; std::vector packed_leg; - insertNodesInHeaps(forward_heap, reverse_heap, phantom_nodes); + insertNodesInHeaps(forward_heap, reverse_heap, endpoint_candidates); search(engine_working_data, facade, @@ -37,9 +37,9 @@ InternalRouteResult directShortestPathSearch(SearchEngineData &en reverse_heap, weight, packed_leg, - DO_NOT_FORCE_LOOPS, - DO_NOT_FORCE_LOOPS, - phantom_nodes); + {}, + {}, + endpoint_candidates); std::vector unpacked_nodes; std::vector unpacked_edges; @@ -60,19 +60,19 @@ InternalRouteResult directShortestPathSearch(SearchEngineData &en }); } - return extractRoute(facade, weight, phantom_nodes, unpacked_nodes, unpacked_edges); + return extractRoute(facade, weight, endpoint_candidates, unpacked_nodes, unpacked_edges); } template <> InternalRouteResult directShortestPathSearch(SearchEngineData &engine_working_data, const DataFacade &facade, - const PhantomNodes &phantom_nodes) + const PhantomEndpointCandidates &endpoint_candidates) { engine_working_data.InitializeOrClearFirstThreadLocalStorage(facade.GetNumberOfNodes(), facade.GetMaxBorderNodeID() + 1); auto &forward_heap = *engine_working_data.forward_heap_1; auto &reverse_heap = *engine_working_data.reverse_heap_1; - insertNodesInHeaps(forward_heap, reverse_heap, phantom_nodes); + insertNodesInHeaps(forward_heap, reverse_heap, endpoint_candidates); // TODO: when structured bindings will be allowed change to // auto [weight, source_node, target_node, unpacked_edges] = ... @@ -83,12 +83,12 @@ InternalRouteResult directShortestPathSearch(SearchEngineData &e facade, forward_heap, reverse_heap, - DO_NOT_FORCE_LOOPS, - DO_NOT_FORCE_LOOPS, + {}, + {}, INVALID_EDGE_WEIGHT, - phantom_nodes); + endpoint_candidates); - return extractRoute(facade, weight, phantom_nodes, unpacked_nodes, unpacked_edges); + return extractRoute(facade, weight, endpoint_candidates, unpacked_nodes, unpacked_edges); } } // namespace routing_algorithms diff --git a/src/engine/routing_algorithms/many_to_many_ch.cpp b/src/engine/routing_algorithms/many_to_many_ch.cpp index 2d2108cb295..f203ebc1d86 100644 --- a/src/engine/routing_algorithms/many_to_many_ch.cpp +++ b/src/engine/routing_algorithms/many_to_many_ch.cpp @@ -49,7 +49,7 @@ void relaxOutgoingEdges( const DataFacade &facade, const typename SearchEngineData::ManyToManyQueryHeap::HeapNode &heapNode, typename SearchEngineData::ManyToManyQueryHeap &query_heap, - const PhantomNode &) + const PhantomNodeCandidates &) { if (stallAtNode(facade, heapNode, query_heap)) { @@ -99,7 +99,7 @@ void forwardRoutingStep(const DataFacade &facade, std::vector &durations_table, std::vector &distances_table, std::vector &middle_nodes_table, - const PhantomNode &phantom_node) + const PhantomNodeCandidates &candidates) { // Take a copy of the extracted node because otherwise could be modified later if toHeapNode is // the same @@ -151,14 +151,14 @@ void forwardRoutingStep(const DataFacade &facade, } } - relaxOutgoingEdges(facade, heapNode, query_heap, phantom_node); + relaxOutgoingEdges(facade, heapNode, query_heap, candidates); } void backwardRoutingStep(const DataFacade &facade, const unsigned column_index, typename SearchEngineData::ManyToManyQueryHeap &query_heap, std::vector &search_space_with_buckets, - const PhantomNode &phantom_node) + const PhantomNodeCandidates &candidates) { // Take a copy (no ref &) of the extracted node because otherwise could be modified later if // toHeapNode is the same @@ -172,7 +172,7 @@ void backwardRoutingStep(const DataFacade &facade, heapNode.data.duration, heapNode.data.distance); - relaxOutgoingEdges(facade, heapNode, query_heap, phantom_node); + relaxOutgoingEdges(facade, heapNode, query_heap, candidates); } } // namespace ch @@ -181,7 +181,7 @@ template <> std::pair, std::vector> manyToManySearch(SearchEngineData &engine_working_data, const DataFacade &facade, - const std::vector &phantom_nodes, + const std::vector &candidates_list, const std::vector &source_indices, const std::vector &target_indices, const bool calculate_distance) @@ -202,18 +202,18 @@ manyToManySearch(SearchEngineData &engine_working_data, for (std::uint32_t column_index = 0; column_index < target_indices.size(); ++column_index) { const auto index = target_indices[column_index]; - const auto &phantom = phantom_nodes[index]; + const auto &target_candidates = candidates_list[index]; engine_working_data.InitializeOrClearManyToManyThreadLocalStorage( facade.GetNumberOfNodes()); auto &query_heap = *(engine_working_data.many_to_many_heap); - insertTargetInHeap(query_heap, phantom); + insertTargetInHeap(query_heap, target_candidates); // Explore search space while (!query_heap.Empty()) { backwardRoutingStep( - facade, column_index, query_heap, search_space_with_buckets, phantom); + facade, column_index, query_heap, search_space_with_buckets, target_candidates); } } @@ -224,13 +224,13 @@ manyToManySearch(SearchEngineData &engine_working_data, for (std::uint32_t row_index = 0; row_index < source_indices.size(); ++row_index) { const auto source_index = source_indices[row_index]; - const auto &source_phantom = phantom_nodes[source_index]; + const auto &source_candidates = candidates_list[source_index]; // Clear heap and insert source nodes engine_working_data.InitializeOrClearManyToManyThreadLocalStorage( facade.GetNumberOfNodes()); auto &query_heap = *(engine_working_data.many_to_many_heap); - insertSourceInHeap(query_heap, source_phantom); + insertSourceInHeap(query_heap, source_candidates); // Explore search space while (!query_heap.Empty()) @@ -244,7 +244,7 @@ manyToManySearch(SearchEngineData &engine_working_data, durations_table, distances_table, middle_nodes_table, - source_phantom); + source_candidates); } } diff --git a/src/engine/routing_algorithms/many_to_many_mld.cpp b/src/engine/routing_algorithms/many_to_many_mld.cpp index 9607ccf094d..2404e9ddd30 100644 --- a/src/engine/routing_algorithms/many_to_many_mld.cpp +++ b/src/engine/routing_algorithms/many_to_many_mld.cpp @@ -25,7 +25,7 @@ using PackedPath = std::vector; template inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition, const NodeID node, - const PhantomNode &phantom_node, + const PhantomNodeCandidates &phantom_node, const LevelID maximal_level) { const auto node_level = getNodeQueryLevel(partition, node, phantom_node); @@ -96,7 +96,7 @@ void relaxOutgoingEdges( const DataFacade &facade, const typename SearchEngineData::ManyToManyQueryHeap::HeapNode &heapNode, typename SearchEngineData::ManyToManyQueryHeap &query_heap, - Args... args) + const Args &... args) { BOOST_ASSERT(!facade.ExcludeNode(heapNode.node)); @@ -214,59 +214,63 @@ template std::pair, std::vector> oneToManySearch(SearchEngineData &engine_working_data, const DataFacade &facade, - const std::vector &phantom_nodes, - std::size_t phantom_index, - const std::vector &phantom_indices, + const std::vector &candidates_list, + std::size_t source_index, + const std::vector &target_indices, const bool calculate_distance) { - std::vector weights_table(phantom_indices.size(), INVALID_EDGE_WEIGHT); - std::vector durations_table(phantom_indices.size(), MAXIMAL_EDGE_DURATION); - std::vector distances_table(calculate_distance ? phantom_indices.size() : 0, + std::vector weights_table(target_indices.size(), INVALID_EDGE_WEIGHT); + std::vector durations_table(target_indices.size(), MAXIMAL_EDGE_DURATION); + std::vector distances_table(calculate_distance ? target_indices.size() : 0, MAXIMAL_EDGE_DISTANCE); - std::vector middle_nodes_table(phantom_indices.size(), SPECIAL_NODEID); + std::vector middle_nodes_table(target_indices.size(), SPECIAL_NODEID); // Collect destination (source) nodes into a map std::unordered_multimap> target_nodes_index; - target_nodes_index.reserve(phantom_indices.size()); - for (std::size_t index = 0; index < phantom_indices.size(); ++index) + target_nodes_index.reserve(target_indices.size()); + for (std::size_t index = 0; index < target_indices.size(); ++index) { - const auto &phantom_index = phantom_indices[index]; - const auto &phantom_node = phantom_nodes[phantom_index]; + const auto &target_candidates = candidates_list[target_indices[index]]; - if (DIRECTION == FORWARD_DIRECTION) - { - if (phantom_node.IsValidForwardTarget()) - target_nodes_index.insert( - {phantom_node.forward_segment_id.id, - std::make_tuple(index, - phantom_node.GetForwardWeightPlusOffset(), - phantom_node.GetForwardDuration(), - phantom_node.GetForwardDistance())}); - if (phantom_node.IsValidReverseTarget()) - target_nodes_index.insert( - {phantom_node.reverse_segment_id.id, - std::make_tuple(index, - phantom_node.GetReverseWeightPlusOffset(), - phantom_node.GetReverseDuration(), - phantom_node.GetReverseDistance())}); - } - else if (DIRECTION == REVERSE_DIRECTION) + for (const auto &phantom_node : target_candidates) { - if (phantom_node.IsValidForwardSource()) - target_nodes_index.insert( - {phantom_node.forward_segment_id.id, - std::make_tuple(index, - -phantom_node.GetForwardWeightPlusOffset(), - -phantom_node.GetForwardDuration(), - -phantom_node.GetForwardDistance())}); - if (phantom_node.IsValidReverseSource()) - target_nodes_index.insert( - {phantom_node.reverse_segment_id.id, - std::make_tuple(index, - -phantom_node.GetReverseWeightPlusOffset(), - -phantom_node.GetReverseDuration(), - -phantom_node.GetReverseDistance())}); + if (DIRECTION == FORWARD_DIRECTION) + { + if (phantom_node.IsValidForwardTarget()) + target_nodes_index.insert( + {phantom_node.forward_segment_id.id, + std::make_tuple(index, + phantom_node.GetForwardWeightPlusOffset(), + phantom_node.GetForwardDuration(), + phantom_node.GetForwardDistance())}); + + if (phantom_node.IsValidReverseTarget()) + target_nodes_index.insert( + {phantom_node.reverse_segment_id.id, + std::make_tuple(index, + phantom_node.GetReverseWeightPlusOffset(), + phantom_node.GetReverseDuration(), + phantom_node.GetReverseDistance())}); + } + else if (DIRECTION == REVERSE_DIRECTION) + { + if (phantom_node.IsValidForwardSource()) + target_nodes_index.insert( + {phantom_node.forward_segment_id.id, + std::make_tuple(index, + -phantom_node.GetForwardWeightPlusOffset(), + -phantom_node.GetForwardDuration(), + -phantom_node.GetForwardDistance())}); + + if (phantom_node.IsValidReverseSource()) + target_nodes_index.insert( + {phantom_node.reverse_segment_id.id, + std::make_tuple(index, + -phantom_node.GetReverseWeightPlusOffset(), + -phantom_node.GetReverseDuration(), + -phantom_node.GetReverseDistance())}); + } } } @@ -337,42 +341,45 @@ oneToManySearch(SearchEngineData &engine_working_data, }; { // Place source (destination) adjacent nodes into the heap - const auto &phantom_node = phantom_nodes[phantom_index]; + const auto &source_candidates = candidates_list[source_index]; - if (DIRECTION == FORWARD_DIRECTION) + for (const auto &phantom_node : source_candidates) { - if (phantom_node.IsValidForwardSource()) + if (DIRECTION == FORWARD_DIRECTION) { - insert_node(phantom_node.forward_segment_id.id, - -phantom_node.GetForwardWeightPlusOffset(), - -phantom_node.GetForwardDuration(), - -phantom_node.GetForwardDistance()); - } + if (phantom_node.IsValidForwardSource()) + { + insert_node(phantom_node.forward_segment_id.id, + -phantom_node.GetForwardWeightPlusOffset(), + -phantom_node.GetForwardDuration(), + -phantom_node.GetForwardDistance()); + } - if (phantom_node.IsValidReverseSource()) - { - insert_node(phantom_node.reverse_segment_id.id, - -phantom_node.GetReverseWeightPlusOffset(), - -phantom_node.GetReverseDuration(), - -phantom_node.GetReverseDistance()); + if (phantom_node.IsValidReverseSource()) + { + insert_node(phantom_node.reverse_segment_id.id, + -phantom_node.GetReverseWeightPlusOffset(), + -phantom_node.GetReverseDuration(), + -phantom_node.GetReverseDistance()); + } } - } - else if (DIRECTION == REVERSE_DIRECTION) - { - if (phantom_node.IsValidForwardTarget()) + else if (DIRECTION == REVERSE_DIRECTION) { - insert_node(phantom_node.forward_segment_id.id, - phantom_node.GetForwardWeightPlusOffset(), - phantom_node.GetForwardDuration(), - phantom_node.GetForwardDistance()); - } + if (phantom_node.IsValidForwardTarget()) + { + insert_node(phantom_node.forward_segment_id.id, + phantom_node.GetForwardWeightPlusOffset(), + phantom_node.GetForwardDuration(), + phantom_node.GetForwardDistance()); + } - if (phantom_node.IsValidReverseTarget()) - { - insert_node(phantom_node.reverse_segment_id.id, - phantom_node.GetReverseWeightPlusOffset(), - phantom_node.GetReverseDuration(), - phantom_node.GetReverseDistance()); + if (phantom_node.IsValidReverseTarget()) + { + insert_node(phantom_node.reverse_segment_id.id, + phantom_node.GetReverseWeightPlusOffset(), + phantom_node.GetReverseDuration(), + phantom_node.GetReverseDistance()); + } } } } @@ -389,7 +396,7 @@ oneToManySearch(SearchEngineData &engine_working_data, // Relax outgoing edges relaxOutgoingEdges( - facade, heapNode, query_heap, phantom_nodes, phantom_index, phantom_indices); + facade, heapNode, query_heap, candidates_list, source_index, target_indices); } return std::make_pair(std::move(durations_table), std::move(distances_table)); @@ -409,7 +416,7 @@ void forwardRoutingStep(const DataFacade &facade, std::vector &durations_table, std::vector &distances_table, std::vector &middle_nodes_table, - const PhantomNode &phantom_node) + const PhantomNodeCandidates &candidates) { // Take a copy of the extracted node because otherwise could be modified later if toHeapNode is // the same @@ -455,7 +462,7 @@ void forwardRoutingStep(const DataFacade &facade, } } - relaxOutgoingEdges(facade, heapNode, query_heap, phantom_node); + relaxOutgoingEdges(facade, heapNode, query_heap, candidates); } template @@ -463,7 +470,7 @@ void backwardRoutingStep(const DataFacade &facade, const unsigned column_idx, typename SearchEngineData::ManyToManyQueryHeap &query_heap, std::vector &search_space_with_buckets, - const PhantomNode &phantom_node) + const PhantomNodeCandidates &candidates) { // Take a copy of the extracted node because otherwise could be modified later if toHeapNode is // the same @@ -481,7 +488,7 @@ void backwardRoutingStep(const DataFacade &facade, const auto &partition = facade.GetMultiLevelPartition(); const auto maximal_level = partition.GetNumberOfLevels() - 1; - relaxOutgoingEdges(facade, heapNode, query_heap, phantom_node, maximal_level); + relaxOutgoingEdges(facade, heapNode, query_heap, candidates, maximal_level); } template @@ -524,7 +531,7 @@ template std::pair, std::vector> manyToManySearch(SearchEngineData &engine_working_data, const DataFacade &facade, - const std::vector &phantom_nodes, + const std::vector &candidates_list, const std::vector &source_indices, const std::vector &target_indices, const bool calculate_distance) @@ -545,22 +552,22 @@ manyToManySearch(SearchEngineData &engine_working_data, for (std::uint32_t column_idx = 0; column_idx < target_indices.size(); ++column_idx) { const auto index = target_indices[column_idx]; - const auto &target_phantom = phantom_nodes[index]; + const auto &target_candidates = candidates_list[index]; engine_working_data.InitializeOrClearManyToManyThreadLocalStorage( facade.GetNumberOfNodes(), facade.GetMaxBorderNodeID() + 1); auto &query_heap = *(engine_working_data.many_to_many_heap); if (DIRECTION == FORWARD_DIRECTION) - insertTargetInHeap(query_heap, target_phantom); + insertTargetInHeap(query_heap, target_candidates); else - insertSourceInHeap(query_heap, target_phantom); + insertSourceInHeap(query_heap, target_candidates); // explore search space while (!query_heap.Empty()) { backwardRoutingStep( - facade, column_idx, query_heap, search_space_with_buckets, target_phantom); + facade, column_idx, query_heap, search_space_with_buckets, target_candidates); } } @@ -571,7 +578,7 @@ manyToManySearch(SearchEngineData &engine_working_data, for (std::uint32_t row_idx = 0; row_idx < source_indices.size(); ++row_idx) { const auto source_index = source_indices[row_idx]; - const auto &source_phantom = phantom_nodes[source_index]; + const auto &source_candidates = candidates_list[source_index]; // Clear heap and insert source nodes engine_working_data.InitializeOrClearManyToManyThreadLocalStorage( @@ -580,9 +587,9 @@ manyToManySearch(SearchEngineData &engine_working_data, auto &query_heap = *(engine_working_data.many_to_many_heap); if (DIRECTION == FORWARD_DIRECTION) - insertSourceInHeap(query_heap, source_phantom); + insertSourceInHeap(query_heap, source_candidates); else - insertTargetInHeap(query_heap, source_phantom); + insertTargetInHeap(query_heap, source_candidates); // Explore search space while (!query_heap.Empty()) @@ -597,7 +604,7 @@ manyToManySearch(SearchEngineData &engine_working_data, durations_table, distances_table, middle_nodes_table, - source_phantom); + source_candidates); } } @@ -622,7 +629,7 @@ template <> std::pair, std::vector> manyToManySearch(SearchEngineData &engine_working_data, const DataFacade &facade, - const std::vector &phantom_nodes, + const std::vector &candidates_list, const std::vector &source_indices, const std::vector &target_indices, const bool calculate_distance) @@ -631,7 +638,7 @@ manyToManySearch(SearchEngineData &engine_working_data, { // TODO: check if target_indices.size() == 1 and do a bi-directional search return mld::oneToManySearch(engine_working_data, facade, - phantom_nodes, + candidates_list, source_indices.front(), target_indices, calculate_distance); @@ -641,7 +648,7 @@ manyToManySearch(SearchEngineData &engine_working_data, { return mld::oneToManySearch(engine_working_data, facade, - phantom_nodes, + candidates_list, target_indices.front(), source_indices, calculate_distance); @@ -651,7 +658,7 @@ manyToManySearch(SearchEngineData &engine_working_data, { return mld::manyToManySearch(engine_working_data, facade, - phantom_nodes, + candidates_list, target_indices, source_indices, calculate_distance); @@ -659,7 +666,7 @@ manyToManySearch(SearchEngineData &engine_working_data, return mld::manyToManySearch(engine_working_data, facade, - phantom_nodes, + candidates_list, source_indices, target_indices, calculate_distance); diff --git a/src/engine/routing_algorithms/routing_base.cpp b/src/engine/routing_algorithms/routing_base.cpp index fb01472bbaa..ee8a99ba3cc 100644 --- a/src/engine/routing_algorithms/routing_base.cpp +++ b/src/engine/routing_algorithms/routing_base.cpp @@ -7,30 +7,104 @@ namespace engine namespace routing_algorithms { -bool needsLoopForward(const PhantomNode &source_phantom, const PhantomNode &target_phantom) +bool requiresForwardLoop(const PhantomNode &source, const PhantomNode &target) { - return source_phantom.IsValidForwardSource() && target_phantom.IsValidForwardTarget() && - source_phantom.forward_segment_id.id == target_phantom.forward_segment_id.id && - source_phantom.GetForwardWeightPlusOffset() > - target_phantom.GetForwardWeightPlusOffset(); + return source.IsValidForwardSource() && target.IsValidForwardTarget() && + source.forward_segment_id.id == target.forward_segment_id.id && + source.GetForwardWeightPlusOffset() > target.GetForwardWeightPlusOffset(); } -bool needsLoopBackwards(const PhantomNode &source_phantom, const PhantomNode &target_phantom) +bool requiresBackwardLoop(const PhantomNode &source, const PhantomNode &target) { - return source_phantom.IsValidReverseSource() && target_phantom.IsValidReverseTarget() && - source_phantom.reverse_segment_id.id == target_phantom.reverse_segment_id.id && - source_phantom.GetReverseWeightPlusOffset() > - target_phantom.GetReverseWeightPlusOffset(); + return source.IsValidReverseSource() && target.IsValidReverseTarget() && + source.reverse_segment_id.id == target.reverse_segment_id.id && + source.GetReverseWeightPlusOffset() > target.GetReverseWeightPlusOffset(); } -bool needsLoopForward(const PhantomNodes &phantoms) +std::vector getForwardLoopNodes(const PhantomEndpointCandidates &endpoint_candidates) { - return needsLoopForward(phantoms.source_phantom, phantoms.target_phantom); + std::vector res; + for (const auto &source_phantom : endpoint_candidates.source_phantoms) + { + auto requires_loop = + std::any_of(endpoint_candidates.target_phantoms.begin(), + endpoint_candidates.target_phantoms.end(), + [&](const auto &target_phantom) { + return requiresForwardLoop(source_phantom, target_phantom); + }); + if (requires_loop) + { + res.push_back(source_phantom.forward_segment_id.id); + } + } + return res; } -bool needsLoopBackwards(const PhantomNodes &phantoms) +std::vector getForwardLoopNodes(const PhantomCandidatesToTarget &endpoint_candidates) { - return needsLoopBackwards(phantoms.source_phantom, phantoms.target_phantom); + std::vector res; + for (const auto &source_phantom : endpoint_candidates.source_phantoms) + { + if (requiresForwardLoop(source_phantom, endpoint_candidates.target_phantom)) + { + res.push_back(source_phantom.forward_segment_id.id); + } + } + return res; +} + +std::vector getBackwardLoopNodes(const PhantomEndpointCandidates &endpoint_candidates) +{ + std::vector res; + for (const auto &source_phantom : endpoint_candidates.source_phantoms) + { + auto requires_loop = + std::any_of(endpoint_candidates.target_phantoms.begin(), + endpoint_candidates.target_phantoms.end(), + [&](const auto &target_phantom) { + return requiresBackwardLoop(source_phantom, target_phantom); + }); + if (requires_loop) + { + res.push_back(source_phantom.reverse_segment_id.id); + } + } + return res; +} + +std::vector getBackwardLoopNodes(const PhantomCandidatesToTarget &endpoint_candidates) +{ + std::vector res; + for (const auto &source_phantom : endpoint_candidates.source_phantoms) + { + if (requiresBackwardLoop(source_phantom, endpoint_candidates.target_phantom)) + { + res.push_back(source_phantom.reverse_segment_id.id); + } + } + return res; +} + +PhantomEndpoints endpointsFromCandidates(const PhantomEndpointCandidates &candidates, + const std::vector &path) +{ + auto source_it = std::find_if(candidates.source_phantoms.begin(), + candidates.source_phantoms.end(), + [&path](const auto &source_phantom) { + return path.front() == source_phantom.forward_segment_id.id || + path.front() == source_phantom.reverse_segment_id.id; + }); + BOOST_ASSERT(source_it != candidates.source_phantoms.end()); + + auto target_it = std::find_if(candidates.target_phantoms.begin(), + candidates.target_phantoms.end(), + [&path](const auto &target_phantom) { + return path.back() == target_phantom.forward_segment_id.id || + path.back() == target_phantom.reverse_segment_id.id; + }); + BOOST_ASSERT(target_it != candidates.target_phantoms.end()); + + return PhantomEndpoints{*source_it, *target_it}; } } // namespace routing_algorithms diff --git a/src/engine/routing_algorithms/routing_base_ch.cpp b/src/engine/routing_algorithms/routing_base_ch.cpp index 695ebe9234a..e64278b7232 100644 --- a/src/engine/routing_algorithms/routing_base_ch.cpp +++ b/src/engine/routing_algorithms/routing_base_ch.cpp @@ -95,9 +95,8 @@ void search(SearchEngineData & /*engine_working_data*/, SearchEngineData::QueryHeap &reverse_heap, EdgeWeight &weight, std::vector &packed_leg, - const bool force_loop_forward, - const bool force_loop_reverse, - const PhantomNodes & /*phantom_nodes*/, + const std::vector &force_loop_forward_nodes, + const std::vector &force_loop_reverse_nodes, const EdgeWeight weight_upper_bound) { if (forward_heap.Empty() || reverse_heap.Empty()) @@ -126,8 +125,8 @@ void search(SearchEngineData & /*engine_working_data*/, middle, weight, min_edge_offset, - force_loop_forward, - force_loop_reverse); + force_loop_forward_nodes, + force_loop_reverse_nodes); } if (!reverse_heap.Empty()) { @@ -137,8 +136,8 @@ void search(SearchEngineData & /*engine_working_data*/, middle, weight, min_edge_offset, - force_loop_reverse, - force_loop_forward); + force_loop_reverse_nodes, + force_loop_forward_nodes); } } @@ -179,7 +178,8 @@ double getNetworkDistance(SearchEngineData &engine_working_data, forward_heap.Clear(); reverse_heap.Clear(); - insertNodesInHeaps(forward_heap, reverse_heap, {source_phantom, target_phantom}); + PhantomEndpoints endpoints{source_phantom, target_phantom}; + insertNodesInHeaps(forward_heap, reverse_heap, endpoints); EdgeWeight weight = INVALID_EDGE_WEIGHT; std::vector packed_path; @@ -189,9 +189,9 @@ double getNetworkDistance(SearchEngineData &engine_working_data, reverse_heap, weight, packed_path, - DO_NOT_FORCE_LOOPS, - DO_NOT_FORCE_LOOPS, - {source_phantom, target_phantom}, + {}, + {}, + endpoints, weight_upper_bound); if (weight == INVALID_EDGE_WEIGHT) diff --git a/src/engine/routing_algorithms/shortest_path.cpp b/src/engine/routing_algorithms/shortest_path.cpp index 8fadf6491ac..e850de7777d 100644 --- a/src/engine/routing_algorithms/shortest_path.cpp +++ b/src/engine/routing_algorithms/shortest_path.cpp @@ -12,13 +12,13 @@ namespace routing_algorithms template InternalRouteResult shortestPathSearch(SearchEngineData &engine_working_data, const DataFacade &facade, - const std::vector &phantom_nodes_vector, + const std::vector &waypoint_candidates, const boost::optional continue_straight_at_waypoint); template InternalRouteResult shortestPathSearch(SearchEngineData &engine_working_data, const DataFacade &facade, - const std::vector &phantom_nodes_vector, + const std::vector &waypoint_candidates, const boost::optional continue_straight_at_waypoint); } // namespace routing_algorithms diff --git a/unit_tests/engine/base64.cpp b/unit_tests/engine/base64.cpp index de5343a2f76..3adb7db3e92 100644 --- a/unit_tests/engine/base64.cpp +++ b/unit_tests/engine/base64.cpp @@ -44,16 +44,16 @@ BOOST_AUTO_TEST_CASE(hint_encoding_decoding_roundtrip) const PhantomNode phantom; const osrm::test::MockDataFacade facade{}; - const Hint hint{phantom, facade.GetCheckSum()}; + const SegmentHint seg_hint{phantom, facade.GetCheckSum()}; - const auto base64 = hint.ToBase64(); + const auto base64 = seg_hint.ToBase64(); BOOST_CHECK(0 == std::count(begin(base64), end(base64), '+')); BOOST_CHECK(0 == std::count(begin(base64), end(base64), '/')); - const auto decoded = Hint::FromBase64(base64); + const auto decoded = SegmentHint::FromBase64(base64); - BOOST_CHECK_EQUAL(hint, decoded); + BOOST_CHECK_EQUAL(seg_hint, decoded); } BOOST_AUTO_TEST_CASE(hint_encoding_decoding_roundtrip_bytewise) @@ -65,12 +65,12 @@ BOOST_AUTO_TEST_CASE(hint_encoding_decoding_roundtrip_bytewise) const PhantomNode phantom; const osrm::test::MockDataFacade facade{}; - const Hint hint{phantom, facade.GetCheckSum()}; + const SegmentHint seg_hint{phantom, facade.GetCheckSum()}; - const auto decoded = Hint::FromBase64(hint.ToBase64()); + const auto decoded = SegmentHint::FromBase64(seg_hint.ToBase64()); - BOOST_CHECK(std::equal(reinterpret_cast(&hint), - reinterpret_cast(&hint) + sizeof(Hint), + BOOST_CHECK(std::equal(reinterpret_cast(&seg_hint), + reinterpret_cast(&seg_hint) + sizeof(Hint), reinterpret_cast(&decoded))); } diff --git a/unit_tests/engine/collapse_internal_route_result.cpp b/unit_tests/engine/collapse_internal_route_result.cpp index c14fa3967c8..445bb36f61d 100644 --- a/unit_tests/engine/collapse_internal_route_result.cpp +++ b/unit_tests/engine/collapse_internal_route_result.cpp @@ -23,7 +23,7 @@ BOOST_AUTO_TEST_CASE(unchanged_collapse_route_result) PathData kathy{0, 1, 1, 2, 3, 4, 1, boost::none}; InternalRouteResult one_leg_result; one_leg_result.unpacked_path_segments = {{pathy, kathy}}; - one_leg_result.segment_end_coordinates = {PhantomNodes{source, target}}; + one_leg_result.leg_endpoints = {PhantomEndpoints{source, target}}; one_leg_result.source_traversed_in_reverse = {true}; one_leg_result.target_traversed_in_reverse = {true}; one_leg_result.shortest_path_weight = 50; @@ -50,18 +50,17 @@ BOOST_AUTO_TEST_CASE(two_legs_to_one_leg) node_3.forward_segment_id = {12, true}; InternalRouteResult two_leg_result; two_leg_result.unpacked_path_segments = {{pathy, kathy}, {kathy, cathy}}; - two_leg_result.segment_end_coordinates = {PhantomNodes{node_1, node_2}, - PhantomNodes{node_2, node_3}}; + two_leg_result.leg_endpoints = {PhantomEndpoints{node_1, node_2}, + PhantomEndpoints{node_2, node_3}}; two_leg_result.source_traversed_in_reverse = {true, false}; two_leg_result.target_traversed_in_reverse = {true, false}; two_leg_result.shortest_path_weight = 80; auto collapsed = CollapseInternalRouteResult(two_leg_result, {true, false, true, true}); BOOST_CHECK_EQUAL(collapsed.unpacked_path_segments.size(), 1); - BOOST_CHECK_EQUAL(collapsed.segment_end_coordinates.size(), 1); - BOOST_CHECK_EQUAL(collapsed.segment_end_coordinates[0].target_phantom.forward_segment_id.id, - 12); - BOOST_CHECK_EQUAL(collapsed.segment_end_coordinates[0].source_phantom.forward_segment_id.id, 1); + BOOST_CHECK_EQUAL(collapsed.leg_endpoints.size(), 1); + BOOST_CHECK_EQUAL(collapsed.leg_endpoints[0].target_phantom.forward_segment_id.id, 12); + BOOST_CHECK_EQUAL(collapsed.leg_endpoints[0].source_phantom.forward_segment_id.id, 1); BOOST_CHECK_EQUAL(collapsed.unpacked_path_segments[0].size(), 4); BOOST_CHECK_EQUAL(collapsed.unpacked_path_segments[0][0].turn_via_node, 2); BOOST_CHECK_EQUAL(collapsed.unpacked_path_segments[0][1].turn_via_node, 1); @@ -88,20 +87,20 @@ BOOST_AUTO_TEST_CASE(three_legs_to_two_legs) three_leg_result.unpacked_path_segments = {std::vector{pathy, kathy}, std::vector{kathy, qathy, cathy}, std::vector{cathy, mathy}}; - three_leg_result.segment_end_coordinates = { - PhantomNodes{node_1, node_2}, PhantomNodes{node_2, node_3}, PhantomNodes{node_3, node_4}}; + three_leg_result.leg_endpoints = {PhantomEndpoints{node_1, node_2}, + PhantomEndpoints{node_2, node_3}, + PhantomEndpoints{node_3, node_4}}; three_leg_result.source_traversed_in_reverse = {true, false, true}, three_leg_result.target_traversed_in_reverse = {true, false, true}, three_leg_result.shortest_path_weight = 140; auto collapsed = CollapseInternalRouteResult(three_leg_result, {true, true, false, true}); BOOST_CHECK_EQUAL(collapsed.unpacked_path_segments.size(), 2); - BOOST_CHECK_EQUAL(collapsed.segment_end_coordinates.size(), 2); - BOOST_CHECK_EQUAL(collapsed.segment_end_coordinates[0].source_phantom.forward_segment_id.id, 1); - BOOST_CHECK_EQUAL(collapsed.segment_end_coordinates[0].target_phantom.forward_segment_id.id, 6); - BOOST_CHECK_EQUAL(collapsed.segment_end_coordinates[1].source_phantom.forward_segment_id.id, 6); - BOOST_CHECK_EQUAL(collapsed.segment_end_coordinates[1].target_phantom.forward_segment_id.id, - 18); + BOOST_CHECK_EQUAL(collapsed.leg_endpoints.size(), 2); + BOOST_CHECK_EQUAL(collapsed.leg_endpoints[0].source_phantom.forward_segment_id.id, 1); + BOOST_CHECK_EQUAL(collapsed.leg_endpoints[0].target_phantom.forward_segment_id.id, 6); + BOOST_CHECK_EQUAL(collapsed.leg_endpoints[1].source_phantom.forward_segment_id.id, 6); + BOOST_CHECK_EQUAL(collapsed.leg_endpoints[1].target_phantom.forward_segment_id.id, 18); BOOST_CHECK_EQUAL(collapsed.unpacked_path_segments[0].size(), 2); BOOST_CHECK_EQUAL(collapsed.unpacked_path_segments[1].size(), 5); BOOST_CHECK_EQUAL(collapsed.unpacked_path_segments[0][0].turn_via_node, 2); @@ -126,20 +125,19 @@ BOOST_AUTO_TEST_CASE(two_legs_to_two_legs) node_3.forward_segment_id = {12, true}; InternalRouteResult two_leg_result; two_leg_result.unpacked_path_segments = {{pathy, kathy}, {kathy, cathy}}; - two_leg_result.segment_end_coordinates = {PhantomNodes{node_1, node_2}, - PhantomNodes{node_2, node_3}}; + two_leg_result.leg_endpoints = {PhantomEndpoints{node_1, node_2}, + PhantomEndpoints{node_2, node_3}}; two_leg_result.source_traversed_in_reverse = {true, false}; two_leg_result.target_traversed_in_reverse = {true, false}; two_leg_result.shortest_path_weight = 80; auto collapsed = CollapseInternalRouteResult(two_leg_result, {true, true, true}); BOOST_CHECK_EQUAL(collapsed.unpacked_path_segments.size(), 2); - BOOST_CHECK_EQUAL(collapsed.segment_end_coordinates.size(), 2); - BOOST_CHECK_EQUAL(collapsed.segment_end_coordinates[0].source_phantom.forward_segment_id.id, 1); - BOOST_CHECK_EQUAL(collapsed.segment_end_coordinates[0].target_phantom.forward_segment_id.id, 6); - BOOST_CHECK_EQUAL(collapsed.segment_end_coordinates[1].source_phantom.forward_segment_id.id, 6); - BOOST_CHECK_EQUAL(collapsed.segment_end_coordinates[1].target_phantom.forward_segment_id.id, - 12); + BOOST_CHECK_EQUAL(collapsed.leg_endpoints.size(), 2); + BOOST_CHECK_EQUAL(collapsed.leg_endpoints[0].source_phantom.forward_segment_id.id, 1); + BOOST_CHECK_EQUAL(collapsed.leg_endpoints[0].target_phantom.forward_segment_id.id, 6); + BOOST_CHECK_EQUAL(collapsed.leg_endpoints[1].source_phantom.forward_segment_id.id, 6); + BOOST_CHECK_EQUAL(collapsed.leg_endpoints[1].target_phantom.forward_segment_id.id, 12); BOOST_CHECK_EQUAL(collapsed.unpacked_path_segments[0].size(), 2); BOOST_CHECK_EQUAL(collapsed.unpacked_path_segments[1].size(), 2); BOOST_CHECK_EQUAL(collapsed.unpacked_path_segments[0][0].turn_via_node, 2); diff --git a/unit_tests/engine/offline_facade.cpp b/unit_tests/engine/offline_facade.cpp index 7c264e17695..1c5dfa37362 100644 --- a/unit_tests/engine/offline_facade.cpp +++ b/unit_tests/engine/offline_facade.cpp @@ -224,101 +224,35 @@ class ContiguousInternalMemoryDataFacade return {}; } - std::vector + std::vector NearestPhantomNodesInRange(const util::Coordinate /*input_coordinate*/, - const float /*max_distance*/, - const int /*bearing*/, - const int /*bearing_range*/, - const Approach /*approach*/, + const double /*max_distance*/, + const boost::optional /*bearing*/, + const engine::Approach /*approach*/, const bool /*use_all_edges*/) const override { return {}; - } - - std::vector - NearestPhantomNodesInRange(const util::Coordinate /*input_coordinate*/, - const float /*max_distance*/, - const Approach /*approach*/, - const bool /*use_all_edges*/) const override - { - return {}; - } - - std::vector - NearestPhantomNodes(const util::Coordinate /*input_coordinate*/, - const unsigned /*max_results*/, - const double /*max_distance*/, - const int /*bearing*/, - const int /*bearing_range*/, - const Approach /*approach*/) const override - { - return {}; - } - - std::vector - NearestPhantomNodes(const util::Coordinate /*input_coordinate*/, - const unsigned /*max_results*/, - const int /*bearing*/, - const int /*bearing_range*/, - const Approach /*approach*/) const override - { - return {}; - } - - std::vector - NearestPhantomNodes(const util::Coordinate /*input_coordinate*/, - const unsigned /*max_results*/, - const Approach /*approach*/) const override - { - return {}; - } + }; - std::vector + std::vector NearestPhantomNodes(const util::Coordinate /*input_coordinate*/, - const unsigned /*max_results*/, - const double /*max_distance*/, - const Approach /*approach*/) const override - { - return {}; - } - - std::pair - NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate /*input_coordinate*/, - const Approach /*approach*/, - const bool /* use_all_edges */) const override + const size_t /*max_results*/, + const boost::optional /*max_distance*/, + const boost::optional /*bearing*/, + const engine::Approach /*approach*/) const override { return {}; - } - - std::pair - NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate /*input_coordinate*/, - const double /*max_distance*/, - const Approach /*approach*/, - const bool /* use_all_edges */) const override - { - return {}; - } - - std::pair - NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate /*input_coordinate*/, - const double /*max_distance*/, - const int /*bearing*/, - const int /*bearing_range*/, - const Approach /*approach*/, - const bool /* use_all_edges */) const override - { - return {}; - } + }; - std::pair - NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate /*input_coordinate*/, - const int /*bearing*/, - const int /*bearing_range*/, - const Approach /*approach*/, - const bool /* use_all_edges */) const override + engine::PhantomCandidateAlternatives NearestCandidatesWithAlternativeFromBigComponent( + const util::Coordinate /*input_coordinate*/, + const boost::optional /*max_distance*/, + const boost::optional /*bearing*/, + const engine::Approach /*approach*/, + const bool /*use_all_edges*/) const override { return {}; - } + }; util::guidance::LaneTupleIdPair GetLaneData(const EdgeID /*id*/) const override { @@ -394,15 +328,16 @@ namespace routing_algorithms namespace offline { +template inline void search(SearchEngineData &engine_working_data, const datafacade::ContiguousInternalMemoryDataFacade &facade, typename SearchEngineData::QueryHeap &forward_heap, typename SearchEngineData::QueryHeap &reverse_heap, EdgeWeight &weight, std::vector &packed_leg, - const bool force_loop_forward, - const bool force_loop_reverse, - const PhantomNodes &phantom_nodes, + const std::vector &forward_loop_nodes, + const std::vector &reverse_loop_nodes, + const PhantomT &endpoints, const EdgeWeight weight_upper_bound = INVALID_EDGE_WEIGHT) { mld::search(engine_working_data, @@ -411,9 +346,9 @@ inline void search(SearchEngineData &engine_working_data, reverse_heap, weight, packed_leg, - force_loop_forward, - force_loop_reverse, - phantom_nodes, + forward_loop_nodes, + reverse_loop_nodes, + endpoints, weight_upper_bound); } @@ -421,10 +356,10 @@ template void unpackPath(const FacadeT &facade, RandomIter packed_path_begin, RandomIter packed_path_end, - const PhantomNodes &phantom_nodes, + const PhantomEndpoints &endpoints, std::vector &unpacked_path) { - mld::unpackPath(facade, packed_path_begin, packed_path_end, phantom_nodes, unpacked_path); + mld::unpackPath(facade, packed_path_begin, packed_path_end, endpoints, unpacked_path); } } // namespace offline @@ -442,11 +377,12 @@ BOOST_AUTO_TEST_CASE(shortest_path) osrm::engine::SearchEngineData heaps; osrm::engine::datafacade::ContiguousInternalMemoryDataFacade facade; - std::vector phantom_nodes; - phantom_nodes.push_back({osrm::engine::PhantomNode{}, osrm::engine::PhantomNode{}}); + std::vector waypoints; + waypoints.push_back({osrm::engine::PhantomNode{}}); + waypoints.push_back({osrm::engine::PhantomNode{}}); auto route = - osrm::engine::routing_algorithms::shortestPathSearch(heaps, facade, phantom_nodes, false); + osrm::engine::routing_algorithms::shortestPathSearch(heaps, facade, waypoints, false); BOOST_CHECK_EQUAL(route.shortest_path_weight, INVALID_EDGE_WEIGHT); } diff --git a/unit_tests/mocks/mock_datafacade.hpp b/unit_tests/mocks/mock_datafacade.hpp index 4edcb97356e..6e7022e9a93 100644 --- a/unit_tests/mocks/mock_datafacade.hpp +++ b/unit_tests/mocks/mock_datafacade.hpp @@ -110,99 +110,33 @@ class MockBaseDataFacade : public engine::datafacade::BaseDataFacade std::vector NearestPhantomNodesInRange(const util::Coordinate /*input_coordinate*/, - const float /*max_distance*/, - const int /*bearing*/, - const int /*bearing_range*/, + const double /*max_distance*/, + const boost::optional /*bearing*/, const engine::Approach /*approach*/, const bool /*use_all_edges*/) const override { return {}; - } - - std::vector - NearestPhantomNodesInRange(const util::Coordinate /*input_coordinate*/, - const float /*max_distance*/, - const engine::Approach /*approach*/, - const bool /*use_all_edges*/) const override - { - return {}; - } - - std::vector - NearestPhantomNodes(const util::Coordinate /*input_coordinate*/, - const unsigned /*max_results*/, - const double /*max_distance*/, - const int /*bearing*/, - const int /*bearing_range*/, - const engine::Approach /*approach*/) const override - { - return {}; - } - - std::vector - NearestPhantomNodes(const util::Coordinate /*input_coordinate*/, - const unsigned /*max_results*/, - const int /*bearing*/, - const int /*bearing_range*/, - const engine::Approach /*approach*/) const override - { - return {}; - } + }; std::vector NearestPhantomNodes(const util::Coordinate /*input_coordinate*/, - const unsigned /*max_results*/, + const size_t /*max_results*/, + const boost::optional /*max_distance*/, + const boost::optional /*bearing*/, const engine::Approach /*approach*/) const override { return {}; - } - - std::vector - NearestPhantomNodes(const util::Coordinate /*input_coordinate*/, - const unsigned /*max_results*/, - const double /*max_distance*/, - const engine::Approach /*approach*/) const override - { - return {}; - } - - std::pair - NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate /*input_coordinate*/, - const engine::Approach /*approach*/, - const bool /* use_all_edges */) const override - { - return {}; - } - - std::pair - NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate /*input_coordinate*/, - const double /*max_distance*/, - const engine::Approach /*approach*/, - const bool /* use_all_edges */) const override - { - return {}; - } + }; - std::pair - NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate /*input_coordinate*/, - const double /*max_distance*/, - const int /*bearing*/, - const int /*bearing_range*/, - const engine::Approach /*approach*/, - const bool /* use_all_edges */) const override + engine::PhantomCandidateAlternatives NearestCandidatesWithAlternativeFromBigComponent( + const util::Coordinate /*input_coordinate*/, + const boost::optional /*max_distance*/, + const boost::optional /*bearing*/, + const engine::Approach /*approach*/, + const bool /*use_all_edges*/) const override { return {}; - } - - std::pair - NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate /*input_coordinate*/, - const int /*bearing*/, - const int /*bearing_range*/, - const engine::Approach /*approach*/, - const bool /* use_all_edges */) const override - { - return {}; - } + }; std::uint32_t GetCheckSum() const override { return 0; } diff --git a/unit_tests/server/parameters_parser.cpp b/unit_tests/server/parameters_parser.cpp index 42b5a60b974..c28825a6801 100644 --- a/unit_tests/server/parameters_parser.cpp +++ b/unit_tests/server/parameters_parser.cpp @@ -19,6 +19,20 @@ #define CHECK_EQUAL_RANGE(R1, R2) \ BOOST_CHECK_EQUAL_COLLECTIONS((R1).begin(), (R1).end(), (R2).begin(), (R2).end()); +#define CHECK_EQUAL_RANGE_OF_HINTS(R1, R2) \ + BOOST_REQUIRE_EQUAL((R1).size(), (R2).size()); \ + for (const auto i : util::irange(0UL, (R1).size())) \ + { \ + BOOST_REQUIRE(((R1)[i] && (R2)[i]) || !((R1)[i] || (R2)[i])); \ + if ((R1)[i]) \ + { \ + BOOST_CHECK_EQUAL_COLLECTIONS((R1)[i]->segment_hints.begin(), \ + (R1)[i]->segment_hints.end(), \ + (R2)[i]->segment_hints.begin(), \ + (R2)[i]->segment_hints.end()); \ + } \ + } + BOOST_AUTO_TEST_SUITE(api_parameters_parser) using namespace osrm; @@ -117,15 +131,16 @@ BOOST_AUTO_TEST_CASE(invalid_table_urls) // BOOST_CHECK_EQUAL(testInvalidOptions("1,2;3,4?destinations=2"), 7UL); } -BOOST_AUTO_TEST_CASE(valid_route_hint) +BOOST_AUTO_TEST_CASE(valid_route_segment_hint) { engine::PhantomNode reference_node; reference_node.input_location = util::Coordinate(util::FloatLongitude{7.432251}, util::FloatLatitude{43.745995}); - engine::Hint reference_hint{reference_node, 0x1337}; - auto encoded_hint = reference_hint.ToBase64(); - auto hint = engine::Hint::FromBase64(encoded_hint); - BOOST_CHECK_EQUAL(hint.phantom.input_location, reference_hint.phantom.input_location); + engine::SegmentHint reference_segment_hint{reference_node, 0x1337}; + auto encoded_hint = reference_segment_hint.ToBase64(); + auto seg_hint = engine::SegmentHint::FromBase64(encoded_hint); + BOOST_CHECK_EQUAL(seg_hint.phantom.input_location, + reference_segment_hint.phantom.input_location); } BOOST_AUTO_TEST_CASE(valid_route_urls) @@ -147,7 +162,7 @@ BOOST_AUTO_TEST_CASE(valid_route_urls) CHECK_EQUAL_RANGE(reference_1.radiuses, result_1->radiuses); CHECK_EQUAL_RANGE(reference_1.approaches, result_1->approaches); CHECK_EQUAL_RANGE(reference_1.coordinates, result_1->coordinates); - CHECK_EQUAL_RANGE(reference_1.hints, result_1->hints); + CHECK_EQUAL_RANGE_OF_HINTS(reference_1.hints, result_1->hints); RouteParameters reference_2{}; reference_2.alternatives = true; @@ -170,7 +185,7 @@ BOOST_AUTO_TEST_CASE(valid_route_urls) CHECK_EQUAL_RANGE(reference_2.radiuses, result_2->radiuses); CHECK_EQUAL_RANGE(reference_2.approaches, result_2->approaches); CHECK_EQUAL_RANGE(reference_2.coordinates, result_2->coordinates); - CHECK_EQUAL_RANGE(reference_2.hints, result_2->hints); + CHECK_EQUAL_RANGE_OF_HINTS(reference_2.hints, result_2->hints); BOOST_CHECK_EQUAL(result_2->annotations_type == RouteParameters::AnnotationsType::All, true); RouteParameters reference_3{false, @@ -195,14 +210,15 @@ BOOST_AUTO_TEST_CASE(valid_route_urls) CHECK_EQUAL_RANGE(reference_3.radiuses, result_3->radiuses); CHECK_EQUAL_RANGE(reference_3.approaches, result_3->approaches); CHECK_EQUAL_RANGE(reference_3.coordinates, result_3->coordinates); - CHECK_EQUAL_RANGE(reference_3.hints, result_3->hints); + CHECK_EQUAL_RANGE_OF_HINTS(reference_3.hints, result_3->hints); engine::PhantomNode phantom_1; phantom_1.input_location = coords_1[0]; engine::PhantomNode phantom_2; phantom_2.input_location = coords_1[1]; - std::vector> hints_4 = {engine::Hint{phantom_1, 0x1337}, - engine::Hint{phantom_2, 0x1337}}; + std::vector> hints_4 = { + engine::Hint{{engine::SegmentHint{phantom_1, 0x1337}}}, + engine::Hint{{engine::SegmentHint{phantom_2, 0x1337}}}}; RouteParameters reference_4{false, false, false, @@ -226,7 +242,7 @@ BOOST_AUTO_TEST_CASE(valid_route_urls) CHECK_EQUAL_RANGE(reference_4.radiuses, result_4->radiuses); CHECK_EQUAL_RANGE(reference_4.approaches, result_4->approaches); CHECK_EQUAL_RANGE(reference_4.coordinates, result_4->coordinates); - CHECK_EQUAL_RANGE(reference_4.hints, result_4->hints); + CHECK_EQUAL_RANGE_OF_HINTS(reference_4.hints, result_4->hints); std::vector> bearings_4 = { boost::none, @@ -255,7 +271,7 @@ BOOST_AUTO_TEST_CASE(valid_route_urls) CHECK_EQUAL_RANGE(reference_5.radiuses, result_5->radiuses); CHECK_EQUAL_RANGE(reference_5.approaches, result_5->approaches); CHECK_EQUAL_RANGE(reference_5.coordinates, result_5->coordinates); - CHECK_EQUAL_RANGE(reference_5.hints, result_5->hints); + CHECK_EQUAL_RANGE_OF_HINTS(reference_5.hints, result_5->hints); std::vector coords_2 = {{util::FloatLongitude{0}, util::FloatLatitude{1}}, {util::FloatLongitude{2}, util::FloatLatitude{3}}, @@ -275,7 +291,7 @@ BOOST_AUTO_TEST_CASE(valid_route_urls) CHECK_EQUAL_RANGE(reference_6.radiuses, result_6->radiuses); CHECK_EQUAL_RANGE(reference_6.approaches, result_6->approaches); CHECK_EQUAL_RANGE(reference_6.coordinates, result_6->coordinates); - CHECK_EQUAL_RANGE(reference_6.hints, result_6->hints); + CHECK_EQUAL_RANGE_OF_HINTS(reference_6.hints, result_6->hints); auto result_7 = parseParameters("1,2;3,4?radiuses=;unlimited"); RouteParameters reference_7{}; @@ -293,7 +309,7 @@ BOOST_AUTO_TEST_CASE(valid_route_urls) CHECK_EQUAL_RANGE(reference_7.radiuses, result_7->radiuses); CHECK_EQUAL_RANGE(reference_7.approaches, result_7->approaches); CHECK_EQUAL_RANGE(reference_7.coordinates, result_7->coordinates); - CHECK_EQUAL_RANGE(reference_7.hints, result_7->hints); + CHECK_EQUAL_RANGE_OF_HINTS(reference_7.hints, result_7->hints); auto result_8 = parseParameters("1,2;3,4?radiuses=;"); RouteParameters reference_8{}; @@ -320,7 +336,10 @@ BOOST_AUTO_TEST_CASE(valid_route_urls) engine::PhantomNode phantom_4; phantom_4.input_location = coords_3[2]; std::vector> hints_10 = { - engine::Hint{phantom_3, 0x1337}, boost::none, engine::Hint{phantom_4, 0x1337}, boost::none}; + engine::Hint{{engine::SegmentHint{phantom_3, 0x1337}}}, + {}, + engine::Hint{{engine::SegmentHint{phantom_4, 0x1337}}}, + {}}; RouteParameters reference_10{false, false, @@ -346,7 +365,7 @@ BOOST_AUTO_TEST_CASE(valid_route_urls) CHECK_EQUAL_RANGE(reference_10.radiuses, result_10->radiuses); CHECK_EQUAL_RANGE(reference_10.approaches, result_10->approaches); CHECK_EQUAL_RANGE(reference_10.coordinates, result_10->coordinates); - CHECK_EQUAL_RANGE(reference_10.hints, result_10->hints); + CHECK_EQUAL_RANGE_OF_HINTS(reference_10.hints, result_10->hints); // Do not generate Hints when they are explicitly disabled auto result_11 = parseParameters("1,2;3,4?generate_hints=false"); @@ -459,7 +478,7 @@ BOOST_AUTO_TEST_CASE(valid_route_urls) CHECK_EQUAL_RANGE(reference_18.radiuses, result_18->radiuses); CHECK_EQUAL_RANGE(reference_18.approaches, result_18->approaches); CHECK_EQUAL_RANGE(reference_18.coordinates, result_18->coordinates); - CHECK_EQUAL_RANGE(reference_18.hints, result_18->hints); + CHECK_EQUAL_RANGE_OF_HINTS(reference_18.hints, result_18->hints); RouteParameters reference_19{}; reference_19.alternatives = true; @@ -478,7 +497,7 @@ BOOST_AUTO_TEST_CASE(valid_route_urls) CHECK_EQUAL_RANGE(reference_19.radiuses, result_19->radiuses); CHECK_EQUAL_RANGE(reference_19.approaches, result_19->approaches); CHECK_EQUAL_RANGE(reference_19.coordinates, result_19->coordinates); - CHECK_EQUAL_RANGE(reference_19.hints, result_19->hints); + CHECK_EQUAL_RANGE_OF_HINTS(reference_19.hints, result_19->hints); RouteParameters reference_20{}; reference_20.alternatives = false; @@ -497,7 +516,7 @@ BOOST_AUTO_TEST_CASE(valid_route_urls) CHECK_EQUAL_RANGE(reference_20.radiuses, result_20->radiuses); CHECK_EQUAL_RANGE(reference_20.approaches, result_20->approaches); CHECK_EQUAL_RANGE(reference_20.coordinates, result_20->coordinates); - CHECK_EQUAL_RANGE(reference_20.hints, result_20->hints); + CHECK_EQUAL_RANGE_OF_HINTS(reference_20.hints, result_20->hints); // exclude flags RouteParameters reference_21{}; @@ -516,7 +535,7 @@ BOOST_AUTO_TEST_CASE(valid_route_urls) CHECK_EQUAL_RANGE(reference_21.radiuses, result_21->radiuses); CHECK_EQUAL_RANGE(reference_21.approaches, result_21->approaches); CHECK_EQUAL_RANGE(reference_21.coordinates, result_21->coordinates); - CHECK_EQUAL_RANGE(reference_21.hints, result_21->hints); + CHECK_EQUAL_RANGE_OF_HINTS(reference_21.hints, result_21->hints); CHECK_EQUAL_RANGE(reference_21.exclude, result_21->exclude); } diff --git a/unit_tests/util/static_rtree.cpp b/unit_tests/util/static_rtree.cpp index 34eb18994b8..ddb376eb5f6 100644 --- a/unit_tests/util/static_rtree.cpp +++ b/unit_tests/util/static_rtree.cpp @@ -200,8 +200,8 @@ void simple_verify_rtree(RTreeT &rtree, auto result_u = rtree.Nearest(pu, 1); auto result_v = rtree.Nearest(pv, 1); BOOST_CHECK(result_u.size() == 1 && result_v.size() == 1); - BOOST_CHECK(result_u.front().u == e.u || result_u.front().v == e.u); - BOOST_CHECK(result_v.front().u == e.v || result_v.front().v == e.v); + BOOST_CHECK(result_u.front().data.u == e.u || result_u.front().data.v == e.u); + BOOST_CHECK(result_v.front().data.u == e.v || result_v.front().data.v == e.v); } } @@ -226,8 +226,8 @@ void sampling_verify_rtree(RTreeT &rtree, auto result_lsnn = lsnn.Nearest(q, 1); BOOST_CHECK(result_rtree.size() == 1); BOOST_CHECK(result_lsnn.size() == 1); - auto rtree_u = result_rtree.back().u; - auto rtree_v = result_rtree.back().v; + auto rtree_u = result_rtree.back().data.u; + auto rtree_v = result_rtree.back().data.v; auto lsnn_u = result_lsnn.back().u; auto lsnn_v = result_lsnn.back().v; @@ -322,8 +322,8 @@ BOOST_AUTO_TEST_CASE(regression_test) BOOST_CHECK(result_rtree.size() == 1); BOOST_CHECK(result_ls.size() == 1); - BOOST_CHECK_EQUAL(result_ls.front().u, result_rtree.front().u); - BOOST_CHECK_EQUAL(result_ls.front().v, result_rtree.front().v); + BOOST_CHECK_EQUAL(result_ls.front().u, result_rtree.front().data.u); + BOOST_CHECK_EQUAL(result_ls.front().v, result_rtree.front().data.v); } // Bug: If you querry a point with a narrow radius, no result should be returned @@ -347,8 +347,8 @@ BOOST_AUTO_TEST_CASE(radius_regression_test) Coordinate input(FloatLongitude{5.2}, FloatLatitude{5.0}); { - auto results = query.NearestPhantomNodesInRange( - input, 0.01, osrm::engine::Approach::UNRESTRICTED, true); + auto results = query.NearestPhantomNodes( + input, osrm::engine::Approach::UNRESTRICTED, boost::none, 0.01, boost::none, true); BOOST_CHECK_EQUAL(results.size(), 0); } } @@ -373,14 +373,14 @@ BOOST_AUTO_TEST_CASE(permissive_edge_snapping) Coordinate input(FloatLongitude{0.0005}, FloatLatitude{0.0005}); { - auto results = query.NearestPhantomNodesInRange( - input, 1000, osrm::engine::Approach::UNRESTRICTED, false); + auto results = query.NearestPhantomNodes( + input, osrm::engine::Approach::UNRESTRICTED, boost::none, 1000, boost::none, false); BOOST_CHECK_EQUAL(results.size(), 1); } { - auto results = query.NearestPhantomNodesInRange( - input, 1000, osrm::engine::Approach::UNRESTRICTED, true); + auto results = query.NearestPhantomNodes( + input, osrm::engine::Approach::UNRESTRICTED, boost::none, 1000, boost::none, true); BOOST_CHECK_EQUAL(results.size(), 2); } } @@ -405,21 +405,30 @@ BOOST_AUTO_TEST_CASE(bearing_tests) Coordinate input(FloatLongitude{5.1}, FloatLatitude{5.0}); { - auto results = query.NearestPhantomNodes(input, 5, osrm::engine::Approach::UNRESTRICTED); + auto results = query.NearestPhantomNodes( + input, osrm::engine::Approach::UNRESTRICTED, 5, boost::none, boost::none, false); BOOST_CHECK_EQUAL(results.size(), 2); BOOST_CHECK_EQUAL(results.back().phantom_node.forward_segment_id.id, 0); BOOST_CHECK_EQUAL(results.back().phantom_node.reverse_segment_id.id, 1); } { - auto results = - query.NearestPhantomNodes(input, 5, 270, 10, osrm::engine::Approach::UNRESTRICTED); + auto results = query.NearestPhantomNodes(input, + osrm::engine::Approach::UNRESTRICTED, + 5, + boost::none, + engine::Bearing{270, 10}, + false); BOOST_CHECK_EQUAL(results.size(), 0); } { - auto results = - query.NearestPhantomNodes(input, 5, 45, 10, osrm::engine::Approach::UNRESTRICTED); + auto results = query.NearestPhantomNodes(input, + osrm::engine::Approach::UNRESTRICTED, + 5, + boost::none, + engine::Bearing{45, 10}, + false); BOOST_CHECK_EQUAL(results.size(), 2); BOOST_CHECK(results[0].phantom_node.forward_segment_id.enabled); @@ -432,20 +441,28 @@ BOOST_AUTO_TEST_CASE(bearing_tests) } { - auto results = query.NearestPhantomNodesInRange( - input, 11000, osrm::engine::Approach::UNRESTRICTED, true); + auto results = query.NearestPhantomNodes( + input, osrm::engine::Approach::UNRESTRICTED, boost::none, 11000, boost::none, true); BOOST_CHECK_EQUAL(results.size(), 2); } { - auto results = query.NearestPhantomNodesInRange( - input, 11000, 270, 10, osrm::engine::Approach::UNRESTRICTED, true); + auto results = query.NearestPhantomNodes(input, + osrm::engine::Approach::UNRESTRICTED, + boost::none, + 11000, + engine::Bearing{270, 10}, + true); BOOST_CHECK_EQUAL(results.size(), 0); } { - auto results = query.NearestPhantomNodesInRange( - input, 11000, 45, 10, osrm::engine::Approach::UNRESTRICTED, true); + auto results = query.NearestPhantomNodes(input, + osrm::engine::Approach::UNRESTRICTED, + boost::none, + 11000, + engine::Bearing{45, 10}, + true); BOOST_CHECK_EQUAL(results.size(), 2); BOOST_CHECK(results[0].phantom_node.forward_segment_id.enabled);