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);