Support snapping to multiple ways at an input location (#5953)

This PR improves routing results by adding support for snapping to
multiple ways at input locations.

This means all edges at the snapped location can act as source/target
candidates for routing search, ensuring we always find the best route,
and not the one dependent on the edge selected.
This commit is contained in:
Michael Bell 2022-08-27 11:36:20 +01:00 committed by GitHub
parent bb18a2b428
commit d74e7b66bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
59 changed files with 2820 additions and 1964 deletions

View File

@ -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

View File

@ -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 |

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 |

View File

@ -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

View File

@ -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 |

View File

@ -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 |

View File

@ -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 |

View File

@ -9,9 +9,12 @@
#include "engine/hint.hpp"
#include "util/coordinate_calculation.hpp"
#include <boost/algorithm/string/join.hpp>
#include <boost/assert.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include <boost/range/algorithm/transform.hpp>
#include <boost/range/adaptor/filtered.hpp>
#include <memory>
#include <vector>
@ -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<PhantomNodes> &segment_end_coordinates) const
util::json::Array
MakeWaypoints(const std::vector<PhantomNodeCandidates> &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<SegmentHint> 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<flatbuffers::Vector<flatbuffers::Offset<fbresult::Waypoint>>>
MakeWaypoints(flatbuffers::FlatBufferBuilder *builder,
const std::vector<PhantomNodes> &segment_end_coordinates) const
const std::vector<PhantomNodeCandidates> &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<flatbuffers::Offset<fbresult::Waypoint>> 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<fbresult::WaypointBuilder> MakeWaypoint(flatbuffers::FlatBufferBuilder *builder,
const PhantomNode &phantom) const
std::unique_ptr<fbresult::WaypointBuilder>
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<double>(util::toFloating(phantom.location.lon)),
static_cast<double>(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<double>(util::toFloating(snapped_location.lon)),
static_cast<double>(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<flatbuffers::String> hint_string;
if (parameters.generate_hints)
{
hint_string = builder->CreateString(Hint{phantom, facade.GetCheckSum()}.ToBase64());
std::vector<SegmentHint> 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<fbresult::WaypointBuilder>(*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)
{

View File

@ -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

View File

@ -76,7 +76,7 @@ class MatchAPI final : public RouteAPI
routes.values.reserve(number_of_routes);
for (auto index : util::irange<std::size_t>(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"] =

View File

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

View File

@ -47,8 +47,8 @@ class RouteAPI : public BaseAPI
void
MakeResponse(const InternalManyRoutesResult &raw_routes,
const std::vector<PhantomNodes>
&all_start_end_points, // all used coordinates, ignoring waypoints= parameter
const std::vector<PhantomNodeCandidates>
&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<flatbuffers::FlatBufferBuilder>())
{
auto &fb_result = response.get<flatbuffers::FlatBufferBuilder>();
MakeResponse(raw_routes, all_start_end_points, fb_result);
MakeResponse(raw_routes, waypoint_candidates, fb_result);
}
else
{
auto &json_result = response.get<util::json::Object>();
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<PhantomNodes>
&all_start_end_points, // all used coordinates, ignoring waypoints= parameter
const std::vector<PhantomNodeCandidates>
&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<PhantomNodes>
&all_start_end_points, // all used coordinates, ignoring waypoints= parameter
const std::vector<PhantomNodeCandidates>
&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<fbresult::RouteObject>
MakeRoute(flatbuffers::FlatBufferBuilder &fb_result,
const std::vector<PhantomNodes> &segment_end_coordinates,
const std::vector<PhantomEndpoints> &leg_endpoints,
const std::vector<std::vector<PathData>> &unpacked_path_segments,
const std::vector<bool> &source_traversed_in_reverse,
const std::vector<bool> &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<PhantomNodes> &segment_end_coordinates,
util::json::Object MakeRoute(const std::vector<PhantomEndpoints> &leg_endpoints,
const std::vector<std::vector<PathData>> &unpacked_path_segments,
const std::vector<bool> &source_traversed_in_reverse,
const std::vector<bool> &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 &parameters;
std::pair<std::vector<guidance::RouteLeg>, std::vector<guidance::LegGeometry>>
MakeLegs(const std::vector<PhantomNodes> &segment_end_coordinates,
MakeLegs(const std::vector<PhantomEndpoints> &leg_endpoints,
const std::vector<std::vector<PathData>> &unpacked_path_segments,
const std::vector<bool> &source_traversed_in_reverse,
const std::vector<bool> &target_traversed_in_reverse) const
@ -877,13 +877,13 @@ class RouteAPI : public BaseAPI
std::make_pair(std::vector<guidance::RouteLeg>(), std::vector<guidance::LegGeometry>());
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<std::size_t>(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];

View File

@ -48,25 +48,25 @@ class TableAPI final : public BaseAPI
virtual void
MakeResponse(const std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>> &tables,
const std::vector<PhantomNode> &phantoms,
const std::vector<PhantomNodeCandidates> &candidates,
const std::vector<TableCellRef> &fallback_speed_cells,
osrm::engine::api::ResultT &response) const
{
if (response.is<flatbuffers::FlatBufferBuilder>())
{
auto &fb_result = response.get<flatbuffers::FlatBufferBuilder>();
MakeResponse(tables, phantoms, fallback_speed_cells, fb_result);
MakeResponse(tables, candidates, fallback_speed_cells, fb_result);
}
else
{
auto &json_result = response.get<util::json::Object>();
MakeResponse(tables, phantoms, fallback_speed_cells, json_result);
MakeResponse(tables, candidates, fallback_speed_cells, json_result);
}
}
virtual void
MakeResponse(const std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>> &tables,
const std::vector<PhantomNode> &phantoms,
const std::vector<PhantomNodeCandidates> &candidates,
const std::vector<TableCellRef> &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<EdgeDuration>, std::vector<EdgeDistance>> &tables,
const std::vector<PhantomNode> &phantoms,
const std::vector<PhantomNodeCandidates> &candidates,
const std::vector<TableCellRef> &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<flatbuffers::Vector<flatbuffers::Offset<fbresult::Waypoint>>>
MakeWaypoints(flatbuffers::FlatBufferBuilder &builder,
const std::vector<PhantomNode> &phantoms) const
const std::vector<PhantomNodeCandidates> &candidates) const
{
std::vector<flatbuffers::Offset<fbresult::Waypoint>> 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<flatbuffers::Vector<flatbuffers::Offset<fbresult::Waypoint>>>
MakeWaypoints(flatbuffers::FlatBufferBuilder &builder,
const std::vector<PhantomNode> &phantoms,
const std::vector<PhantomNodeCandidates> &candidates,
const std::vector<std::size_t> &indices) const
{
std::vector<flatbuffers::Offset<fbresult::Waypoint>> 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<PhantomNode> &phantoms) const
virtual util::json::Array
MakeWaypoints(const std::vector<PhantomNodeCandidates> &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<PhantomNode> &phantoms,
virtual util::json::Array MakeWaypoints(const std::vector<PhantomNodeCandidates> &candidates,
const std::vector<std::size_t> &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;
}

View File

@ -26,7 +26,7 @@ class TripAPI final : public RouteAPI
}
void MakeResponse(const std::vector<std::vector<NodeID>> &sub_trips,
const std::vector<InternalRouteResult> &sub_routes,
const std::vector<PhantomNode> &phantoms,
const std::vector<PhantomNodeCandidates> &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<flatbuffers::FlatBufferBuilder>())
{
auto &fb_result = response.get<flatbuffers::FlatBufferBuilder>();
MakeResponse(sub_trips, sub_routes, phantoms, fb_result);
MakeResponse(sub_trips, sub_routes, candidates, fb_result);
}
else
{
auto &json_result = response.get<util::json::Object>();
MakeResponse(sub_trips, sub_routes, phantoms, json_result);
MakeResponse(sub_trips, sub_routes, candidates, json_result);
}
}
void MakeResponse(const std::vector<std::vector<NodeID>> &sub_trips,
const std::vector<InternalRouteResult> &sub_routes,
const std::vector<PhantomNode> &phantoms,
const std::vector<PhantomNodeCandidates> &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<std::vector<NodeID>> &sub_trips,
const std::vector<InternalRouteResult> &sub_routes,
const std::vector<PhantomNode> &phantoms,
const std::vector<PhantomNodeCandidates> &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<std::size_t>(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<flatbuffers::Vector<flatbuffers::Offset<fbresult::Waypoint>>>
MakeWaypoints(flatbuffers::FlatBufferBuilder &fb_result,
const std::vector<std::vector<NodeID>> &sub_trips,
const std::vector<PhantomNode> &phantoms) const
const std::vector<PhantomNodeCandidates> &candidates) const
{
std::vector<flatbuffers::Offset<fbresult::Waypoint>> 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<std::vector<NodeID>> &sub_trips,
const std::vector<PhantomNode> &phantoms) const
const std::vector<PhantomNodeCandidates> &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));

View File

@ -323,127 +323,41 @@ class ContiguousInternalMemoryDataFacadeBase : public BaseDataFacade
std::vector<PhantomNodeWithDistance>
NearestPhantomNodesInRange(const util::Coordinate input_coordinate,
const float max_distance,
const double max_distance,
const boost::optional<Bearing> 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<PhantomNodeWithDistance>
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);
return m_geospatial_query->NearestPhantomNodes(
input_coordinate, approach, boost::none, max_distance, bearing, use_all_edges);
}
std::vector<PhantomNodeWithDistance>
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<PhantomNodeWithDistance>
NearestPhantomNodes(const util::Coordinate input_coordinate,
const unsigned max_results,
const double max_distance,
const size_t max_results,
const boost::optional<double> max_distance,
const boost::optional<Bearing> bearing,
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, max_results, max_distance, bearing, boost::none);
}
std::vector<PhantomNodeWithDistance>
NearestPhantomNodes(const util::Coordinate input_coordinate,
const unsigned max_results,
const int bearing,
const int bearing_range,
const Approach approach) const override final
PhantomCandidateAlternatives
NearestCandidatesWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
const boost::optional<double> max_distance,
const boost::optional<Bearing> bearing,
const Approach approach,
const bool use_all_edges) const override final
{
BOOST_ASSERT(m_geospatial_query.get());
return m_geospatial_query->NearestPhantomNodes(
input_coordinate, max_results, bearing, bearing_range, approach);
}
std::vector<PhantomNodeWithDistance>
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<PhantomNode, PhantomNode>
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<PhantomNode, PhantomNode>
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<PhantomNode, PhantomNode>
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);
}
std::pair<PhantomNode, PhantomNode>
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
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, 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; }

View File

@ -35,6 +35,7 @@
#include <boost/range/any_range.hpp>
#include <cstddef>
#include <engine/bearing.hpp>
#include <string>
#include <utility>
#include <vector>
@ -130,62 +131,24 @@ class BaseDataFacade
virtual std::vector<PhantomNodeWithDistance>
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<PhantomNodeWithDistance>
NearestPhantomNodesInRange(const util::Coordinate input_coordinate,
const float max_distance,
const double max_distance,
const boost::optional<Bearing> bearing,
const Approach approach,
const bool use_all_edges) const = 0;
virtual std::vector<PhantomNodeWithDistance>
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<PhantomNodeWithDistance>
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<PhantomNodeWithDistance>
NearestPhantomNodes(const util::Coordinate input_coordinate,
const unsigned max_results,
const Approach approach) const = 0;
virtual std::vector<PhantomNodeWithDistance>
NearestPhantomNodes(const util::Coordinate input_coordinate,
const unsigned max_results,
const double max_distance,
const size_t max_results,
const boost::optional<double> max_distance,
const boost::optional<Bearing> bearing,
const Approach approach) const = 0;
virtual std::pair<PhantomNode, PhantomNode>
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
const Approach approach,
const bool use_all_edges) const = 0;
virtual std::pair<PhantomNode, PhantomNode>
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
const double max_distance,
const Approach approach,
const bool use_all_edges) const = 0;
virtual std::pair<PhantomNode, PhantomNode>
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<PhantomNode, PhantomNode>
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<double> max_distance,
const boost::optional<Bearing> 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;

View File

@ -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<bool, bool> boolPairAnd(const std::pair<bool, bool> &A,
const std::pair<bool, bool> &B)
inline std::pair<bool, bool> operator&&(const std::pair<bool, bool> &a,
const std::pair<bool, bool> &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 <typename RTreeT, typename DataFacadeT> class GeospatialQuery
return rtree.SearchInBox(bbox);
}
// Returns nearest PhantomNodes in the given bearing range within max_distance.
// Returns max_results nearest PhantomNodes that are valid within the provided parameters.
// Does not filter by small/big component!
std::vector<PhantomNodeWithDistance>
NearestPhantomNodesInRange(const util::Coordinate input_coordinate,
const double max_distance,
const Approach approach,
const bool use_all_edges) const
NearestPhantomNodes(const util::Coordinate input_coordinate,
const Approach approach,
const boost::optional<size_t> max_results,
const boost::optional<double> max_distance,
const boost::optional<Bearing> bearing_with_range,
const boost::optional<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<PhantomNodeWithDistance>
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](
[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, use_all_edges),
CheckSegmentExclude(segment)));
use_direction =
boolPairAnd(use_direction, CheckApproach(input_coordinate, segment, approach));
return use_direction;
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, input_coordinate](const std::size_t,
const CandidateSegment &segment) {
return 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 in the given bearing range.
// Does not filter by small/big component!
std::vector<PhantomNodeWithDistance>
NearestPhantomNodes(const util::Coordinate input_coordinate,
const unsigned max_results,
const int bearing,
const int bearing_range,
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<double> max_distance,
const boost::optional<Bearing> bearing_with_range,
const boost::optional<bool> use_all_edges) 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.
// Does not filter by small/big component!
std::vector<PhantomNodeWithDistance>
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
{
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));
},
[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 max_results nearest PhantomNodes.
// Does not filter by small/big component!
std::vector<PhantomNodeWithDistance>
NearestPhantomNodes(const util::Coordinate input_coordinate,
const unsigned max_results,
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));
},
[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<PhantomNodeWithDistance>
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<PhantomNode, PhantomNode>
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<double>::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));
&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;
if (use_directions.first || use_directions.second)
/**
*
* 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))
{
has_big_component = has_big_component || !IsTinyComponent(segment);
has_small_component = has_small_component || IsTinyComponent(segment);
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));
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() == 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<PhantomNode, PhantomNode>
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
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,
&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)
if (use_candidate.first || use_candidate.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{});
}
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<PhantomNode, PhantomNode>
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)
{
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 (!has_nearest)
{
has_big_component = has_big_component || !IsTinyComponent(segment);
has_small_component = has_small_component || IsTinyComponent(segment);
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;
},
[&has_big_component](const std::size_t num_results, const CandidateSegment &) {
return num_results > 0 && has_big_component;
[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() > 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<PhantomNode, PhantomNode>
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)
{
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)
{
has_big_component = has_big_component || !IsTinyComponent(segment);
has_small_component = has_small_component || IsTinyComponent(segment);
}
}
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);
return MakeAlternativeBigCandidates(input_coordinate, nearest_coord, results);
}
private:
PhantomCandidateAlternatives
MakeAlternativeBigCandidates(const util::Coordinate input_coordinate,
const Coordinate nearest_coord,
const std::vector<CandidateSegment> &results) const
{
if (results.size() == 0)
{
return std::make_pair(PhantomNodeCandidates{}, PhantomNodeCandidates{});
}
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)
{
if (data.forward_segment_id.enabled && data.reverse_segment_id.enabled &&
!(candidate_it->forward_segment_id.enabled &&
candidate_it->reverse_segment_id.enabled))
{
*candidate_it = MakePhantomNode(input_coordinate, data).phantom_node;
}
}
else
{
if (!candidate_it->forward_segment_id.enabled ||
!candidate_it->reverse_segment_id.enabled ||
(data.forward_segment_id.enabled && data.reverse_segment_id.enabled))
{
*candidate_it = MakePhantomNode(input_coordinate, data).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));
}
std::vector<PhantomNodeWithDistance>
MakePhantomNodes(const util::Coordinate input_coordinate,
const std::vector<EdgeData> &results) const
const std::vector<CandidateSegment> &results) const
{
std::vector<PhantomNodeWithDistance> 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 <typename RTreeT, typename DataFacadeT> 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 <typename RTreeT, typename DataFacadeT> 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<bool, bool> CheckSegmentExclude(const CandidateSegment &segment) const
@ -616,8 +474,7 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
}
std::pair<bool, bool> 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 <typename RTreeT, typename DataFacadeT> 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 <typename RTreeT, typename DataFacadeT> 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<bool, bool> HasValidEdge(const CandidateSegment &segment,
@ -682,7 +539,7 @@ template <typename RTreeT, typename DataFacadeT> 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;
}

View File

@ -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<SegmentHint> 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

View File

@ -50,7 +50,7 @@ struct PathData
struct InternalRouteResult
{
std::vector<std::vector<PathData>> unpacked_path_segments;
std::vector<PhantomNodes> segment_end_coordinates;
std::vector<PhantomEndpoints> leg_endpoints;
std::vector<bool> source_traversed_in_reverse;
std::vector<bool> 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

View File

@ -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 <vector>
#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<PhantomNode, PhantomNode>;
using PhantomNodeCandidates = std::vector<PhantomNode>;
using PhantomCandidateAlternatives = std::pair<PhantomNodeCandidates, PhantomNodeCandidates>;
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

View File

@ -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<PhantomNode>
SnapPhantomNodes(const std::vector<PhantomNodePair> &phantom_node_pair_list) const
// Decides whether to use the phantom candidates from big or small components if both are found.
std::vector<PhantomNodeCandidates>
SnapPhantomNodes(std::vector<PhantomCandidateAlternatives> alternatives_list) const
{
const auto check_component_id_is_tiny =
[](const std::pair<PhantomNode, PhantomNode> &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<std::pair<PhantomNode, PhantomNode>> &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 all_in_same_tiny_component =
[](const std::vector<PhantomCandidateAlternatives> &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 fallback_to_big_component =
[](const std::pair<PhantomNode, PhantomNode> &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;
};
// 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 auto use_closed_phantom =
[](const std::pair<PhantomNode, PhantomNode> &phantom_pair) {
return phantom_pair.first;
};
// Move the alternative into the final list
const auto use_closed_phantom = [](PhantomCandidateAlternatives &alternatives) {
return std::move(alternatives.first);
};
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);
std::vector<PhantomNode> 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<PhantomNodeCandidates> 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<std::size_t>(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<std::vector<PhantomNodeWithDistance>>
GetPhantomNodes(const datafacade::BaseDataFacade &facade,
const api::BaseParameters &parameters,
unsigned number_of_results) const
size_t number_of_results) const
{
std::vector<std::vector<PhantomNodeWithDistance>> phantom_nodes(
parameters.coordinates.size());
@ -231,56 +225,26 @@ class BasePlugin
BOOST_ASSERT(parameters.IsValid());
for (const auto i : util::irange<std::size_t>(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])
{
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
{
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] = 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<PhantomNodePair> GetPhantomNodes(const datafacade::BaseDataFacade &facade,
const api::BaseParameters &parameters) const
std::vector<PhantomCandidateAlternatives>
GetPhantomNodes(const datafacade::BaseDataFacade &facade,
const api::BaseParameters &parameters) const
{
std::vector<PhantomNodePair> phantom_node_pairs(parameters.coordinates.size());
std::vector<PhantomCandidateAlternatives> 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<std::size_t>(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<PhantomNodePair> &phantom_nodes,
const std::vector<util::Coordinate> &coordinates) const
std::string
MissingPhantomErrorMessage(const std::vector<PhantomCandidateAlternatives> &alternatives,
const std::vector<util::Coordinate> &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);
}

View File

@ -31,7 +31,7 @@ class TripPlugin final : public BasePlugin
const int max_locations_trip;
InternalRouteResult ComputeRoute(const RoutingAlgorithmsInterface &algorithms,
const std::vector<PhantomNode> &phantom_node_list,
const std::vector<PhantomNodeCandidates> &candidates_list,
const std::vector<NodeID> &trip,
const bool roundtrip) const;

View File

@ -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<PhantomNodes> &phantom_node_pair,
ShortestPathSearch(const std::vector<PhantomNodeCandidates> &waypoint_candidates,
const boost::optional<bool> 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<EdgeDuration>, std::vector<EdgeDistance>>
ManyToManySearch(const std::vector<PhantomNode> &phantom_nodes,
ManyToManySearch(const std::vector<PhantomNodeCandidates> &candidates_list,
const std::vector<std::size_t> &source_indices,
const std::vector<std::size_t> &target_indices,
const bool calculate_distance) const = 0;
@ -73,18 +73,18 @@ template <typename Algorithm> 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<PhantomNodes> &phantom_node_pair,
const std::vector<PhantomNodeCandidates> &waypoint_candidates,
const boost::optional<bool> 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<EdgeDuration>, std::vector<EdgeDistance>>
ManyToManySearch(const std::vector<PhantomNode> &phantom_nodes,
std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
ManyToManySearch(const std::vector<PhantomNodeCandidates> &candidates_list,
const std::vector<std::size_t> &source_indices,
const std::vector<std::size_t> &target_indices,
const bool calculate_distance) const final override;
@ -150,28 +150,27 @@ template <typename Algorithm> class RoutingAlgorithms final : public RoutingAlgo
};
template <typename Algorithm>
InternalManyRoutesResult
RoutingAlgorithms<Algorithm>::AlternativePathSearch(const PhantomNodes &phantom_node_pair,
unsigned number_of_alternatives) const
InternalManyRoutesResult RoutingAlgorithms<Algorithm>::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 <typename Algorithm>
InternalRouteResult RoutingAlgorithms<Algorithm>::ShortestPathSearch(
const std::vector<PhantomNodes> &phantom_node_pair,
const std::vector<PhantomNodeCandidates> &waypoint_candidates,
const boost::optional<bool> 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 <typename Algorithm>
InternalRouteResult
RoutingAlgorithms<Algorithm>::DirectShortestPathSearch(const PhantomNodes &phantom_nodes) const
InternalRouteResult RoutingAlgorithms<Algorithm>::DirectShortestPathSearch(
const PhantomEndpointCandidates &endpoint_candidates) const
{
return routing_algorithms::directShortestPathSearch(heaps, *facade, phantom_nodes);
return routing_algorithms::directShortestPathSearch(heaps, *facade, endpoint_candidates);
}
template <typename Algorithm>
@ -193,30 +192,31 @@ inline routing_algorithms::SubMatchingList RoutingAlgorithms<Algorithm>::MapMatc
template <typename Algorithm>
std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
RoutingAlgorithms<Algorithm>::ManyToManySearch(const std::vector<PhantomNode> &phantom_nodes,
const std::vector<std::size_t> &_source_indices,
const std::vector<std::size_t> &_target_indices,
const bool calculate_distance) const
RoutingAlgorithms<Algorithm>::ManyToManySearch(
const std::vector<PhantomNodeCandidates> &candidates_list,
const std::vector<std::size_t> &_source_indices,
const std::vector<std::size_t> &_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);

View File

@ -18,12 +18,12 @@ namespace routing_algorithms
InternalManyRoutesResult alternativePathSearch(SearchEngineData<ch::Algorithm> &search_engine_data,
const DataFacade<ch::Algorithm> &facade,
const PhantomNodes &phantom_node_pair,
const PhantomEndpointCandidates &endpoint_candidates,
unsigned number_of_alternatives);
InternalManyRoutesResult alternativePathSearch(SearchEngineData<mld::Algorithm> &search_engine_data,
const DataFacade<mld::Algorithm> &facade,
const PhantomNodes &phantom_node_pair,
const PhantomEndpointCandidates &endpoint_candidates,
unsigned number_of_alternatives);
} // namespace routing_algorithms

View File

@ -24,7 +24,7 @@ namespace routing_algorithms
template <typename Algorithm>
InternalRouteResult directShortestPathSearch(SearchEngineData<Algorithm> &engine_working_data,
const DataFacade<Algorithm> &facade,
const PhantomNodes &phantom_nodes);
const PhantomEndpointCandidates &endpoint_candidates);
} // namespace routing_algorithms
} // namespace engine

View File

@ -94,7 +94,7 @@ template <typename Algorithm>
std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
manyToManySearch(SearchEngineData<Algorithm> &engine_working_data,
const DataFacade<Algorithm> &facade,
const std::vector<PhantomNode> &phantom_nodes,
const std::vector<PhantomNodeCandidates> &candidates_list,
const std::vector<std::size_t> &source_indices,
const std::vector<std::size_t> &target_indices,
const bool calculate_distance);

View File

@ -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);
template <typename Heap>
void insertNodesInHeaps(Heap &forward_heap, Heap &reverse_heap, const PhantomNodes &nodes)
namespace details
{
template <typename Heap>
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 <typename Heap>
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;
template <typename ManyToManyQueryHeap>
void insertSourceInHeap(ManyToManyQueryHeap &heap, const PhantomNode &phantom_node)
// 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<NodeID> getForwardLoopNodes(const PhantomEndpointCandidates &candidates);
std::vector<NodeID> getForwardLoopNodes(const PhantomCandidatesToTarget &candidates);
std::vector<NodeID> getBackwardLoopNodes(const PhantomEndpointCandidates &candidates);
std::vector<NodeID> 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<NodeID> &path);
template <typename HeapNodeT>
inline bool force_loop(const std::vector<NodeID> &force_nodes, const HeapNodeT &heap_node)
{
if (phantom_node.IsValidForwardSource())
// 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 <typename Heap>
void insertNodesInHeaps(Heap &forward_heap, Heap &reverse_heap, const PhantomEndpoints &endpoints)
{
details::insertSourceInForwardHeap(forward_heap, endpoints.source_phantom);
details::insertTargetInReverseHeap(reverse_heap, endpoints.target_phantom);
}
template <typename Heap>
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 <typename ManyToManyQueryHeap>
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 <typename ManyToManyQueryHeap>
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 <typename FacadeT>
void annotatePath(const FacadeT &facade,
const PhantomNodes &phantom_node_pair,
const PhantomEndpoints &endpoints,
const std::vector<NodeID> &unpacked_nodes,
const std::vector<EdgeID> &unpacked_edges,
std::vector<PathData> &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<NodeID> 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<std::size_t>(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<Algorithm> &facade,
template <typename AlgorithmT>
InternalRouteResult extractRoute(const DataFacade<AlgorithmT> &facade,
const EdgeWeight weight,
const PhantomNodes &phantom_nodes,
const PhantomEndpointCandidates &endpoint_candidates,
const std::vector<NodeID> &unpacked_nodes,
const std::vector<EdgeID> &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<AlgorithmT> &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());

View File

@ -120,8 +120,8 @@ void routingStep(const DataFacade<Algorithm> &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<NodeID> &force_loop_forward_nodes,
const std::vector<NodeID> &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<Algorithm> &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 <typename RandomIter, typename FacadeT>
void unpackPath(const FacadeT &facade,
RandomIter packed_path_begin,
RandomIter packed_path_end,
const PhantomNodes &phantom_nodes,
const PhantomEndpoints &route_endpoints,
std::vector<PathData> &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<Algorithm> &engine_working_data,
const DataFacade<Algorithm> &facade,
SearchEngineData<Algorithm>::QueryHeap &forward_heap,
SearchEngineData<Algorithm>::QueryHeap &reverse_heap,
std::int32_t &weight,
EdgeWeight &weight,
std::vector<NodeID> &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<NodeID> &force_loop_forward_node,
const std::vector<NodeID> &force_loop_reverse_node,
const EdgeWeight duration_upper_bound = INVALID_EDGE_WEIGHT);
template <typename PhantomEndpointT>
void search(SearchEngineData<Algorithm> &engine_working_data,
const DataFacade<Algorithm> &facade,
SearchEngineData<Algorithm>::QueryHeap &forward_heap,
SearchEngineData<Algorithm>::QueryHeap &reverse_heap,
EdgeWeight &weight,
std::vector<NodeID> &packed_leg,
const std::vector<NodeID> &force_loop_forward_node,
const std::vector<NodeID> &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,

View File

@ -33,24 +33,75 @@ namespace
template <typename MultiLevelPartition>
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)));
}
inline bool checkParentCellRestriction(CellID, const PhantomNodes &) { return true; }
template <typename MultiLevelPartition>
inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition,
NodeID node,
const PhantomEndpoints &endpoints)
{
return getNodeQueryLevel(partition, node, endpoints.source_phantom, endpoints.target_phantom);
}
template <typename MultiLevelPartition>
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 <typename MultiLevelPartition>
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;
}
template <typename PhantomCandidateT>
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 <typename MultiLevelPartition>
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 <typename MultiLevelPartition>
inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition,
NodeID node,
const std::vector<PhantomNode> &phantom_nodes,
const std::vector<PhantomNodeCandidates> &candidates_list,
const std::size_t phantom_index,
const std::vector<std::size_t> &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 <bool DIRECTION, typename Algorithm, typename... Args>
void relaxOutgoingEdges(const DataFacade<Algorithm> &facade,
typename SearchEngineData<Algorithm>::QueryHeap &forward_heap,
const typename SearchEngineData<Algorithm>::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<Algorithm> &facade,
typename SearchEngineData<Algorithm>::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<NodeID> &force_loop_forward_nodes,
const std::vector<NodeID> &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<Algorithm> &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<Algorithm> &engine_working_data,
const DataFacade<Algorithm> &facade,
typename SearchEngineData<Algorithm>::QueryHeap &forward_heap,
typename SearchEngineData<Algorithm>::QueryHeap &reverse_heap,
const bool force_loop_forward,
const bool force_loop_reverse,
const std::vector<NodeID> &force_loop_forward_nodes,
const std::vector<NodeID> &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<Algorithm> &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<Algorithm> &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<Algorithm> &engine_working_data,
EdgeWeight subpath_weight;
std::vector<NodeID> subpath_nodes;
std::vector<EdgeID> 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<Algorithm> &engine_working_data,
}
// Alias to be compatible with the CH-based search
template <typename Algorithm>
template <typename Algorithm, typename PhantomEndpointT>
inline void search(SearchEngineData<Algorithm> &engine_working_data,
const DataFacade<Algorithm> &facade,
typename SearchEngineData<Algorithm>::QueryHeap &forward_heap,
typename SearchEngineData<Algorithm>::QueryHeap &reverse_heap,
EdgeWeight &weight,
std::vector<NodeID> &unpacked_nodes,
const bool force_loop_forward,
const bool force_loop_reverse,
const PhantomNodes &phantom_nodes,
const std::vector<NodeID> &force_loop_forward_node,
const std::vector<NodeID> &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<Algorithm> &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 <typename RandomIter, typename FacadeT>
void unpackPath(const FacadeT &facade,
RandomIter packed_path_begin,
RandomIter packed_path_end,
const PhantomNodes &phantom_nodes,
const PhantomEndpoints &route_endpoints,
std::vector<PathData> &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 <typename Algorithm>
@ -583,8 +627,8 @@ double getNetworkDistance(SearchEngineData<Algorithm> &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<NodeID> unpacked_nodes;
@ -593,10 +637,10 @@ double getNetworkDistance(SearchEngineData<Algorithm> &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<Algorithm> &engine_working_data,
std::vector<PathData> 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);
}

View File

@ -14,10 +14,11 @@ namespace routing_algorithms
{
template <typename Algorithm>
InternalRouteResult shortestPathSearch(SearchEngineData<Algorithm> &engine_working_data,
const DataFacade<Algorithm> &facade,
const std::vector<PhantomNodes> &phantom_nodes_vector,
const boost::optional<bool> continue_straight_at_waypoint);
InternalRouteResult
shortestPathSearch(SearchEngineData<Algorithm> &engine_working_data,
const DataFacade<Algorithm> &facade,
const std::vector<PhantomNodeCandidates> &waypoint_candidates,
const boost::optional<bool> continue_straight_at_waypoint);
} // namespace routing_algorithms
} // namespace engine

File diff suppressed because it is too large Load Diff

View File

@ -78,10 +78,17 @@ struct BaseParametersGrammar : boost::spirit::qi::grammar<Iterator, Signature>
: BaseParametersGrammar::base_type(root_rule)
{
const auto add_hint = [](engine::api::BaseParameters &base_parameters,
const boost::optional<std::string> &hint_string) {
if (hint_string)
const std::vector<std::string> &hint_strings) {
if (!hint_strings.empty())
{
base_parameters.hints.emplace_back(engine::Hint::FromBase64(hint_string.get()));
std::vector<engine::SegmentHint> 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<Iterator, Signature>
(-(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=") >

View File

@ -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<EdgeDataT> Nearest(const Coordinate input_coordinate,
const std::size_t max_results) const
std::vector<CandidateSegment> 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 <typename FilterT, typename TerminationT>
std::vector<EdgeDataT> Nearest(const Coordinate input_coordinate,
const FilterT filter,
const TerminationT terminate) const
std::vector<CandidateSegment> Nearest(const Coordinate input_coordinate,
const FilterT filter,
const TerminationT terminate) const
{
std::vector<EdgeDataT> results;
std::vector<CandidateSegment> 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 &current_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 <class QueueT>

View File

@ -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;
}

View File

@ -3,10 +3,10 @@
#include "engine/datafacade/datafacade_base.hpp"
#include <boost/assert.hpp>
#include <boost/unordered_set.hpp>
#include <algorithm>
#include <iterator>
#include <ostream>
#include <tuple>
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<Hint>(encoded);
return decodeBase64Bytewise<SegmentHint>(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<SegmentHint> res(num_hints);
for (const auto i : util::irange<std::size_t>(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<NodeID> forward_segments;
boost::unordered_set<NodeID> 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

View File

@ -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 <cstdlib>
#include <algorithm>
#include <functional>
#include <iterator>
#include <memory>
#include <set>
#include <string>
#include <vector>
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<util::Coordinate> &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<PhantomNodeCandidates> 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)
{

View File

@ -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 <cstddef>
#include <string>
#include <boost/assert.hpp>

View File

@ -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 <cstdlib>
#include <algorithm>
#include <memory>
#include <string>
#include <vector>
#include <boost/assert.hpp>
@ -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;

View File

@ -20,7 +20,6 @@
#include <algorithm>
#include <numeric>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>

View File

@ -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 <boost/assert.hpp>
#include <algorithm>
#include <cstdlib>
#include <iterator>
#include <limits>
#include <memory>
#include <string>
#include <utility>
#include <vector>
@ -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<PhantomNode> &snapped_phantoms,
const std::vector<NodeID> &trip,
const bool roundtrip) const
InternalRouteResult
TripPlugin::ComputeRoute(const RoutingAlgorithmsInterface &algorithms,
const std::vector<PhantomNodeCandidates> &waypoint_candidates,
const std::vector<NodeID> &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<PhantomNodeCandidates> 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);

View File

@ -5,12 +5,10 @@
#include "util/for_each_pair.hpp"
#include "util/integer_range.hpp"
#include "util/json_container.hpp"
#include <cstdlib>
#include <algorithm>
#include <memory>
#include <string>
#include <vector>
@ -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<PhantomNodes> 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;
});
const auto all_in_same_component =
[](const std::vector<PhantomNodeCandidates> &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 (not_in_same_component)
if (!all_in_same_component(snapped_phantoms))
{
return Error("NoRoute", "Impossible route between points", result);
}

View File

@ -190,8 +190,8 @@ void computeWeightAndSharingOfViaPath(SearchEngineData<Algorithm> &engine_workin
s_v_middle,
upper_bound_s_v_path_weight,
min_edge_offset,
DO_NOT_FORCE_LOOPS,
DO_NOT_FORCE_LOOPS);
{},
{});
}
// compute path <v,..,t> by reusing backward search from node t
NodeID v_t_middle = SPECIAL_NODEID;
@ -205,8 +205,8 @@ void computeWeightAndSharingOfViaPath(SearchEngineData<Algorithm> &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<Algorithm> &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<Algorithm> &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<Algorithm> &engine_working_dat
{
if (!forward_heap3.Empty())
{
routingStep<FORWARD_DIRECTION>(facade,
forward_heap3,
reverse_heap3,
middle,
upper_bound,
min_edge_offset,
DO_NOT_FORCE_LOOPS,
DO_NOT_FORCE_LOOPS);
routingStep<FORWARD_DIRECTION>(
facade, forward_heap3, reverse_heap3, middle, upper_bound, min_edge_offset, {}, {});
}
if (!reverse_heap3.Empty())
{
routingStep<REVERSE_DIRECTION>(facade,
reverse_heap3,
forward_heap3,
middle,
upper_bound,
min_edge_offset,
DO_NOT_FORCE_LOOPS,
DO_NOT_FORCE_LOOPS);
routingStep<REVERSE_DIRECTION>(
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<Algorithm> &engine_working_dat
InternalManyRoutesResult alternativePathSearch(SearchEngineData<Algorithm> &engine_working_data,
const DataFacade<Algorithm> &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<NodeID> alternative_path;
std::vector<NodeID> via_node_candidate_list;
std::vector<SearchSpaceEdge> forward_search_space;
@ -592,15 +577,13 @@ InternalManyRoutesResult alternativePathSearch(SearchEngineData<Algorithm> &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<Algorithm> &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<Algorithm> &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<Algorithm> &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;

View File

@ -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<Algorithm> &search_engine_data,
const Facade &facade,
const PhantomNodes &phantom_node_pair)
const PhantomEndpointCandidates &endpoint_candidates)
{
util::static_assert_iter_category<InputIt, std::input_iterator_tag>();
util::static_assert_iter_category<OutIt, std::output_iterator_tag>();
@ -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<WeightedViaNode>
makeCandidateVias(SearchEngineData<Algorithm> &search_engine_data,
const Facade &facade,
const PhantomNodes &phantom_node_pair,
const PhantomEndpointCandidates &endpoint_candidates,
const Parameters &parameters)
{
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<Algorithm> &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<Algorithm> &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<Algorithm> &search_engine_data,
// https://github.com/Project-OSRM/osrm-backend/issues/3905
InternalManyRoutesResult alternativePathSearch(SearchEngineData<Algorithm> &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<Algorithm> &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<Algorithm> &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<Algorithm> &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<Algorithm> &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,

View File

@ -19,7 +19,7 @@ namespace routing_algorithms
template <>
InternalRouteResult directShortestPathSearch(SearchEngineData<ch::Algorithm> &engine_working_data,
const DataFacade<ch::Algorithm> &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<ch::Algorithm> &en
EdgeWeight weight = INVALID_EDGE_WEIGHT;
std::vector<NodeID> 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<ch::Algorithm> &en
reverse_heap,
weight,
packed_leg,
DO_NOT_FORCE_LOOPS,
DO_NOT_FORCE_LOOPS,
phantom_nodes);
{},
{},
endpoint_candidates);
std::vector<NodeID> unpacked_nodes;
std::vector<EdgeID> unpacked_edges;
@ -60,19 +60,19 @@ InternalRouteResult directShortestPathSearch(SearchEngineData<ch::Algorithm> &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<mld::Algorithm> &engine_working_data,
const DataFacade<mld::Algorithm> &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<mld::Algorithm> &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

View File

@ -49,7 +49,7 @@ void relaxOutgoingEdges(
const DataFacade<Algorithm> &facade,
const typename SearchEngineData<Algorithm>::ManyToManyQueryHeap::HeapNode &heapNode,
typename SearchEngineData<Algorithm>::ManyToManyQueryHeap &query_heap,
const PhantomNode &)
const PhantomNodeCandidates &)
{
if (stallAtNode<DIRECTION>(facade, heapNode, query_heap))
{
@ -99,7 +99,7 @@ void forwardRoutingStep(const DataFacade<Algorithm> &facade,
std::vector<EdgeDuration> &durations_table,
std::vector<EdgeDistance> &distances_table,
std::vector<NodeID> &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<Algorithm> &facade,
}
}
relaxOutgoingEdges<FORWARD_DIRECTION>(facade, heapNode, query_heap, phantom_node);
relaxOutgoingEdges<FORWARD_DIRECTION>(facade, heapNode, query_heap, candidates);
}
void backwardRoutingStep(const DataFacade<Algorithm> &facade,
const unsigned column_index,
typename SearchEngineData<Algorithm>::ManyToManyQueryHeap &query_heap,
std::vector<NodeBucket> &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<Algorithm> &facade,
heapNode.data.duration,
heapNode.data.distance);
relaxOutgoingEdges<REVERSE_DIRECTION>(facade, heapNode, query_heap, phantom_node);
relaxOutgoingEdges<REVERSE_DIRECTION>(facade, heapNode, query_heap, candidates);
}
} // namespace ch
@ -181,7 +181,7 @@ template <>
std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
manyToManySearch(SearchEngineData<ch::Algorithm> &engine_working_data,
const DataFacade<ch::Algorithm> &facade,
const std::vector<PhantomNode> &phantom_nodes,
const std::vector<PhantomNodeCandidates> &candidates_list,
const std::vector<std::size_t> &source_indices,
const std::vector<std::size_t> &target_indices,
const bool calculate_distance)
@ -202,18 +202,18 @@ manyToManySearch(SearchEngineData<ch::Algorithm> &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<ch::Algorithm> &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<ch::Algorithm> &engine_working_data,
durations_table,
distances_table,
middle_nodes_table,
source_phantom);
source_candidates);
}
}

View File

@ -25,7 +25,7 @@ using PackedPath = std::vector<PackedEdge>;
template <typename MultiLevelPartition>
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<mld::Algorithm> &facade,
const typename SearchEngineData<mld::Algorithm>::ManyToManyQueryHeap::HeapNode &heapNode,
typename SearchEngineData<mld::Algorithm>::ManyToManyQueryHeap &query_heap,
Args... args)
const Args &... args)
{
BOOST_ASSERT(!facade.ExcludeNode(heapNode.node));
@ -214,59 +214,63 @@ template <bool DIRECTION>
std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
oneToManySearch(SearchEngineData<Algorithm> &engine_working_data,
const DataFacade<Algorithm> &facade,
const std::vector<PhantomNode> &phantom_nodes,
std::size_t phantom_index,
const std::vector<std::size_t> &phantom_indices,
const std::vector<PhantomNodeCandidates> &candidates_list,
std::size_t source_index,
const std::vector<std::size_t> &target_indices,
const bool calculate_distance)
{
std::vector<EdgeWeight> weights_table(phantom_indices.size(), INVALID_EDGE_WEIGHT);
std::vector<EdgeDuration> durations_table(phantom_indices.size(), MAXIMAL_EDGE_DURATION);
std::vector<EdgeDistance> distances_table(calculate_distance ? phantom_indices.size() : 0,
std::vector<EdgeWeight> weights_table(target_indices.size(), INVALID_EDGE_WEIGHT);
std::vector<EdgeDuration> durations_table(target_indices.size(), MAXIMAL_EDGE_DURATION);
std::vector<EdgeDistance> distances_table(calculate_distance ? target_indices.size() : 0,
MAXIMAL_EDGE_DISTANCE);
std::vector<NodeID> middle_nodes_table(phantom_indices.size(), SPECIAL_NODEID);
std::vector<NodeID> middle_nodes_table(target_indices.size(), SPECIAL_NODEID);
// Collect destination (source) nodes into a map
std::unordered_multimap<NodeID, std::tuple<std::size_t, EdgeWeight, EdgeDuration, EdgeDistance>>
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)
for (const auto &phantom_node : target_candidates)
{
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())});
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<Algorithm> &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<Algorithm> &engine_working_data,
// Relax outgoing edges
relaxOutgoingEdges<DIRECTION>(
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<Algorithm> &facade,
std::vector<EdgeDuration> &durations_table,
std::vector<EdgeDistance> &distances_table,
std::vector<NodeID> &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<Algorithm> &facade,
}
}
relaxOutgoingEdges<DIRECTION>(facade, heapNode, query_heap, phantom_node);
relaxOutgoingEdges<DIRECTION>(facade, heapNode, query_heap, candidates);
}
template <bool DIRECTION>
@ -463,7 +470,7 @@ void backwardRoutingStep(const DataFacade<Algorithm> &facade,
const unsigned column_idx,
typename SearchEngineData<Algorithm>::ManyToManyQueryHeap &query_heap,
std::vector<NodeBucket> &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<Algorithm> &facade,
const auto &partition = facade.GetMultiLevelPartition();
const auto maximal_level = partition.GetNumberOfLevels() - 1;
relaxOutgoingEdges<!DIRECTION>(facade, heapNode, query_heap, phantom_node, maximal_level);
relaxOutgoingEdges<!DIRECTION>(facade, heapNode, query_heap, candidates, maximal_level);
}
template <bool DIRECTION>
@ -524,7 +531,7 @@ template <bool DIRECTION>
std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
manyToManySearch(SearchEngineData<Algorithm> &engine_working_data,
const DataFacade<Algorithm> &facade,
const std::vector<PhantomNode> &phantom_nodes,
const std::vector<PhantomNodeCandidates> &candidates_list,
const std::vector<std::size_t> &source_indices,
const std::vector<std::size_t> &target_indices,
const bool calculate_distance)
@ -545,22 +552,22 @@ manyToManySearch(SearchEngineData<Algorithm> &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<DIRECTION>(
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<Algorithm> &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<Algorithm> &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<Algorithm> &engine_working_data,
durations_table,
distances_table,
middle_nodes_table,
source_phantom);
source_candidates);
}
}
@ -622,7 +629,7 @@ template <>
std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
manyToManySearch(SearchEngineData<mld::Algorithm> &engine_working_data,
const DataFacade<mld::Algorithm> &facade,
const std::vector<PhantomNode> &phantom_nodes,
const std::vector<PhantomNodeCandidates> &candidates_list,
const std::vector<std::size_t> &source_indices,
const std::vector<std::size_t> &target_indices,
const bool calculate_distance)
@ -631,7 +638,7 @@ manyToManySearch(SearchEngineData<mld::Algorithm> &engine_working_data,
{ // TODO: check if target_indices.size() == 1 and do a bi-directional search
return mld::oneToManySearch<FORWARD_DIRECTION>(engine_working_data,
facade,
phantom_nodes,
candidates_list,
source_indices.front(),
target_indices,
calculate_distance);
@ -641,7 +648,7 @@ manyToManySearch(SearchEngineData<mld::Algorithm> &engine_working_data,
{
return mld::oneToManySearch<REVERSE_DIRECTION>(engine_working_data,
facade,
phantom_nodes,
candidates_list,
target_indices.front(),
source_indices,
calculate_distance);
@ -651,7 +658,7 @@ manyToManySearch(SearchEngineData<mld::Algorithm> &engine_working_data,
{
return mld::manyToManySearch<REVERSE_DIRECTION>(engine_working_data,
facade,
phantom_nodes,
candidates_list,
target_indices,
source_indices,
calculate_distance);
@ -659,7 +666,7 @@ manyToManySearch(SearchEngineData<mld::Algorithm> &engine_working_data,
return mld::manyToManySearch<FORWARD_DIRECTION>(engine_working_data,
facade,
phantom_nodes,
candidates_list,
source_indices,
target_indices,
calculate_distance);

View File

@ -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<NodeID> getForwardLoopNodes(const PhantomEndpointCandidates &endpoint_candidates)
{
return needsLoopForward(phantoms.source_phantom, phantoms.target_phantom);
std::vector<NodeID> 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<NodeID> getForwardLoopNodes(const PhantomCandidatesToTarget &endpoint_candidates)
{
return needsLoopBackwards(phantoms.source_phantom, phantoms.target_phantom);
std::vector<NodeID> 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<NodeID> getBackwardLoopNodes(const PhantomEndpointCandidates &endpoint_candidates)
{
std::vector<NodeID> 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<NodeID> getBackwardLoopNodes(const PhantomCandidatesToTarget &endpoint_candidates)
{
std::vector<NodeID> 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<NodeID> &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

View File

@ -95,9 +95,8 @@ void search(SearchEngineData<Algorithm> & /*engine_working_data*/,
SearchEngineData<Algorithm>::QueryHeap &reverse_heap,
EdgeWeight &weight,
std::vector<NodeID> &packed_leg,
const bool force_loop_forward,
const bool force_loop_reverse,
const PhantomNodes & /*phantom_nodes*/,
const std::vector<NodeID> &force_loop_forward_nodes,
const std::vector<NodeID> &force_loop_reverse_nodes,
const EdgeWeight weight_upper_bound)
{
if (forward_heap.Empty() || reverse_heap.Empty())
@ -126,8 +125,8 @@ void search(SearchEngineData<Algorithm> & /*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<Algorithm> & /*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<Algorithm> &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<NodeID> packed_path;
@ -189,9 +189,9 @@ double getNetworkDistance(SearchEngineData<Algorithm> &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)

View File

@ -12,13 +12,13 @@ namespace routing_algorithms
template InternalRouteResult
shortestPathSearch(SearchEngineData<ch::Algorithm> &engine_working_data,
const DataFacade<ch::Algorithm> &facade,
const std::vector<PhantomNodes> &phantom_nodes_vector,
const std::vector<PhantomNodeCandidates> &waypoint_candidates,
const boost::optional<bool> continue_straight_at_waypoint);
template InternalRouteResult
shortestPathSearch(SearchEngineData<mld::Algorithm> &engine_working_data,
const DataFacade<mld::Algorithm> &facade,
const std::vector<PhantomNodes> &phantom_nodes_vector,
const std::vector<PhantomNodeCandidates> &waypoint_candidates,
const boost::optional<bool> continue_straight_at_waypoint);
} // namespace routing_algorithms

View File

@ -44,16 +44,16 @@ BOOST_AUTO_TEST_CASE(hint_encoding_decoding_roundtrip)
const PhantomNode phantom;
const osrm::test::MockDataFacade<osrm::engine::routing_algorithms::ch::Algorithm> 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<osrm::engine::routing_algorithms::ch::Algorithm> 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<const unsigned char *>(&hint),
reinterpret_cast<const unsigned char *>(&hint) + sizeof(Hint),
BOOST_CHECK(std::equal(reinterpret_cast<const unsigned char *>(&seg_hint),
reinterpret_cast<const unsigned char *>(&seg_hint) + sizeof(Hint),
reinterpret_cast<const unsigned char *>(&decoded)));
}

View File

@ -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<PathData>{pathy, kathy},
std::vector<PathData>{kathy, qathy, cathy},
std::vector<PathData>{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);

View File

@ -224,101 +224,35 @@ class ContiguousInternalMemoryDataFacade<routing_algorithms::offline::Algorithm>
return {};
}
std::vector<PhantomNodeWithDistance>
std::vector<engine::PhantomNodeWithDistance>
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<engine::Bearing> /*bearing*/,
const engine::Approach /*approach*/,
const bool /*use_all_edges*/) const override
{
return {};
}
};
std::vector<PhantomNodeWithDistance>
NearestPhantomNodesInRange(const util::Coordinate /*input_coordinate*/,
const float /*max_distance*/,
const Approach /*approach*/,
const bool /*use_all_edges*/) const override
{
return {};
}
std::vector<PhantomNodeWithDistance>
std::vector<engine::PhantomNodeWithDistance>
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
const size_t /*max_results*/,
const boost::optional<double> /*max_distance*/,
const boost::optional<engine::Bearing> /*bearing*/,
const engine::Approach /*approach*/) const override
{
return {};
}
};
std::vector<PhantomNodeWithDistance>
NearestPhantomNodes(const util::Coordinate /*input_coordinate*/,
const unsigned /*max_results*/,
const int /*bearing*/,
const int /*bearing_range*/,
const Approach /*approach*/) const override
engine::PhantomCandidateAlternatives NearestCandidatesWithAlternativeFromBigComponent(
const util::Coordinate /*input_coordinate*/,
const boost::optional<double> /*max_distance*/,
const boost::optional<engine::Bearing> /*bearing*/,
const engine::Approach /*approach*/,
const bool /*use_all_edges*/) const override
{
return {};
}
std::vector<PhantomNodeWithDistance>
NearestPhantomNodes(const util::Coordinate /*input_coordinate*/,
const unsigned /*max_results*/,
const Approach /*approach*/) const override
{
return {};
}
std::vector<PhantomNodeWithDistance>
NearestPhantomNodes(const util::Coordinate /*input_coordinate*/,
const unsigned /*max_results*/,
const double /*max_distance*/,
const Approach /*approach*/) const override
{
return {};
}
std::pair<PhantomNode, PhantomNode>
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate /*input_coordinate*/,
const Approach /*approach*/,
const bool /* use_all_edges */) const override
{
return {};
}
std::pair<PhantomNode, PhantomNode>
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate /*input_coordinate*/,
const double /*max_distance*/,
const Approach /*approach*/,
const bool /* use_all_edges */) const override
{
return {};
}
std::pair<PhantomNode, PhantomNode>
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<PhantomNode, PhantomNode>
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate /*input_coordinate*/,
const int /*bearing*/,
const int /*bearing_range*/,
const 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 <typename PhantomT>
inline void search(SearchEngineData<Algorithm> &engine_working_data,
const datafacade::ContiguousInternalMemoryDataFacade<Algorithm> &facade,
typename SearchEngineData<Algorithm>::QueryHeap &forward_heap,
typename SearchEngineData<Algorithm>::QueryHeap &reverse_heap,
EdgeWeight &weight,
std::vector<NodeID> &packed_leg,
const bool force_loop_forward,
const bool force_loop_reverse,
const PhantomNodes &phantom_nodes,
const std::vector<NodeID> &forward_loop_nodes,
const std::vector<NodeID> &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<Algorithm> &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 <typename RandomIter, typename FacadeT>
void unpackPath(const FacadeT &facade,
RandomIter packed_path_begin,
RandomIter packed_path_end,
const PhantomNodes &phantom_nodes,
const PhantomEndpoints &endpoints,
std::vector<PathData> &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<Algorithm> heaps;
osrm::engine::datafacade::ContiguousInternalMemoryDataFacade<Algorithm> facade;
std::vector<osrm::engine::PhantomNodes> phantom_nodes;
phantom_nodes.push_back({osrm::engine::PhantomNode{}, osrm::engine::PhantomNode{}});
std::vector<osrm::engine::PhantomNodeCandidates> 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);
}

View File

@ -110,99 +110,33 @@ class MockBaseDataFacade : public engine::datafacade::BaseDataFacade
std::vector<engine::PhantomNodeWithDistance>
NearestPhantomNodesInRange(const util::Coordinate /*input_coordinate*/,
const float /*max_distance*/,
const int /*bearing*/,
const int /*bearing_range*/,
const double /*max_distance*/,
const boost::optional<engine::Bearing> /*bearing*/,
const engine::Approach /*approach*/,
const bool /*use_all_edges*/) const override
{
return {};
}
std::vector<engine::PhantomNodeWithDistance>
NearestPhantomNodesInRange(const util::Coordinate /*input_coordinate*/,
const float /*max_distance*/,
const engine::Approach /*approach*/,
const bool /*use_all_edges*/) const override
{
return {};
}
};
std::vector<engine::PhantomNodeWithDistance>
NearestPhantomNodes(const util::Coordinate /*input_coordinate*/,
const unsigned /*max_results*/,
const double /*max_distance*/,
const int /*bearing*/,
const int /*bearing_range*/,
const size_t /*max_results*/,
const boost::optional<double> /*max_distance*/,
const boost::optional<engine::Bearing> /*bearing*/,
const engine::Approach /*approach*/) const override
{
return {};
}
};
std::vector<engine::PhantomNodeWithDistance>
NearestPhantomNodes(const util::Coordinate /*input_coordinate*/,
const unsigned /*max_results*/,
const int /*bearing*/,
const int /*bearing_range*/,
const engine::Approach /*approach*/) const override
engine::PhantomCandidateAlternatives NearestCandidatesWithAlternativeFromBigComponent(
const util::Coordinate /*input_coordinate*/,
const boost::optional<double> /*max_distance*/,
const boost::optional<engine::Bearing> /*bearing*/,
const engine::Approach /*approach*/,
const bool /*use_all_edges*/) const override
{
return {};
}
std::vector<engine::PhantomNodeWithDistance>
NearestPhantomNodes(const util::Coordinate /*input_coordinate*/,
const unsigned /*max_results*/,
const engine::Approach /*approach*/) const override
{
return {};
}
std::vector<engine::PhantomNodeWithDistance>
NearestPhantomNodes(const util::Coordinate /*input_coordinate*/,
const unsigned /*max_results*/,
const double /*max_distance*/,
const engine::Approach /*approach*/) const override
{
return {};
}
std::pair<engine::PhantomNode, engine::PhantomNode>
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate /*input_coordinate*/,
const engine::Approach /*approach*/,
const bool /* use_all_edges */) const override
{
return {};
}
std::pair<engine::PhantomNode, engine::PhantomNode>
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate /*input_coordinate*/,
const double /*max_distance*/,
const engine::Approach /*approach*/,
const bool /* use_all_edges */) const override
{
return {};
}
std::pair<engine::PhantomNode, engine::PhantomNode>
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
{
return {};
}
std::pair<engine::PhantomNode, engine::PhantomNode>
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; }

View File

@ -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<std::size_t>(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<TableParameters>("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<boost::optional<engine::Hint>> hints_4 = {engine::Hint{phantom_1, 0x1337},
engine::Hint{phantom_2, 0x1337}};
std::vector<boost::optional<engine::Hint>> 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<boost::optional<engine::Bearing>> 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<util::Coordinate> 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<RouteParameters>("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<RouteParameters>("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<boost::optional<engine::Hint>> 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<RouteParameters>("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);
}

View File

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