basic turn lane handling

This commit is contained in:
Moritz Kobitzsch 2016-05-13 19:18:00 +02:00
parent 2a05b70dfc
commit efa29edf09
68 changed files with 3010 additions and 207 deletions

1
.gitignore vendored
View File

@ -43,6 +43,7 @@ Thumbs.db
/example/build/
/test/data/monaco*
/cmake/postinst
.bundle/
# Eclipse related files #
#########################

View File

@ -1,3 +1,12 @@
# 5.3.0
- API
- Introduces new `TurnType` in the form of `use lane`. The type indicates that you have to stick to a lane without turning
- Introduces lanes to the route response, indicating which lanes are to be used on a turn
- Infrastructure
- BREAKING: The new turn type changes the turn-type order. This breaks the **data format**.
- BREAKING: Turn lane data introduces a new file (osrm.tld). This breaks the fileformat for older versions.
# 5.2.5
- Bugfixes
- Fixes a segfault caused by incorrect trimming logic for very short steps.

View File

@ -46,14 +46,17 @@ Feature: Bike - Mode flag
Scenario: Bike - Mode when pushing bike against oneways
Given the node map
| a | b | |
| | c | d |
| a | b | e |
| f | c | d |
And the ways
| nodes | highway | oneway |
| ab | primary | |
| bc | primary | yes |
| cd | primary | |
| be | primary | |
| cf | primary | |
When I route I should get
| from | to | route | modes |

View File

@ -18,8 +18,8 @@ Feature: Bike - Oneway streets
Scenario: Bike - Around the Block
Given the node map
| a | b |
| d | c |
| | a | b | |
| f | d | c | e |
And the ways
| nodes | oneway | foot |
@ -27,6 +27,8 @@ Feature: Bike - Oneway streets
| bc | | no |
| cd | | no |
| da | | no |
| df | | no |
| ce | | no |
When I route I should get
| from | to | route |

View File

@ -87,14 +87,16 @@ Feature: Bike - Accessability of different way types
Scenario: Bike - Instructions when pushing bike on oneways
Given the node map
| a | b | |
| | c | d |
| a | b | e |
| f | c | d |
And the ways
| nodes | highway | oneway |
| ab | primary | |
| bc | primary | yes |
| cd | primary | |
| be | primary | |
| cf | primary | |
When I route I should get
| from | to | route | modes |

View File

@ -35,8 +35,8 @@ Feature: Car - Oneway streets
Scenario: Car - Around the Block
Given the node map
| a | b |
| d | c |
| | a | b | |
| f | d | c | e |
And the ways
| nodes | oneway |
@ -44,6 +44,8 @@ Feature: Car - Oneway streets
| bc | |
| cd | |
| da | |
| ce | |
| df | |
When I route I should get
| from | to | route |

View File

@ -296,3 +296,21 @@ Feature: Fork Instructions
| waypoints | route | turns |
| a,c | abc,abc | depart,arrive |
| a,d | abc,bd,bd | depart,turn slight right,arrive |
Scenario: Fork on motorway links - don't fork on through
Given the node map
| i | | | | | a |
| j | | c | b | | x |
And the ways
| nodes | name | highway |
| xb | xbcj | motorway_link |
| bc | xbcj | motorway_link |
| cj | xbcj | motorway_link |
| ci | off | motorway_link |
| ab | on | motorway_link |
When I route I should get
| waypoints | route | turns |
| a,j | on,xbcj,xbcj | depart,merge slight left,arrive |
| a,i | on,xbcj,off,off | depart,merge slight left,turn slight right,arrive |

View File

@ -0,0 +1,653 @@
@routing @guidance @turn-lanes
Feature: Turn Lane Guidance
Background:
Given the profile "car"
Given a grid size of 20 meters
#requires https://github.com/cucumber/cucumber-js/issues/417
#Due to this, we use & as a pipe character. Switch them out for \| when 417 is fixed
@bug @WORKAROUND-FIXME
Scenario: Basic Turn Lane 3-way Turn with empty lanes
Given the node map
| a | | b | | c |
| | | d | | |
And the ways
| nodes | turn:lanes | turn:lanes:forward | turn:lanes:backward | name |
| ab | | through\|right | | in |
| bc | | | left\|through&& | straight |
| bd | | | left\|right | right |
When I route I should get
| waypoints | route | turns | lanes |
| a,c | in,straight,straight | depart,new name straight,arrive | ,1, |
| a,d | in,right,right | depart,turn right,arrive | ,0, |
| c,a | straight,in,in | depart,new name straight,arrive | ,0 1 2, |
| c,d | straight,right,right | depart,turn left,arrive | ,3, |
Scenario: Basic Turn Lane 4-Way Turn
Given the node map
| | | e | | |
| a | | b | | c |
| | | d | | |
And the ways
| nodes | turn:lanes | turn:lanes:forward | turn:lanes:backward | name |
| ab | | \|right | | in |
| bc | | | | straight |
| bd | | | left\| | right |
| be | | | | left |
When I route I should get
| waypoints | route | turns | lanes |
| a,c | in,straight,straight | depart,new name straight,arrive | ,1, |
| a,d | in,right,right | depart,turn right,arrive | ,0, |
| a,e | in,left,left | depart,turn left,arrive | ,1, |
| d,a | right,in,in | depart,turn left,arrive | ,1, |
| d,e | right,left,left | depart,new name straight,arrive | ,0, |
| d,c | right,straight,straight | depart,turn right,arrive | ,0, |
Scenario: Basic Turn Lane 4-Way Turn using none
Given the node map
| | | e | | |
| a | | b | | c |
| | | d | | |
And the ways
| nodes | turn:lanes | turn:lanes:forward | turn:lanes:backward | name |
| ab | | none\|right | | in |
| bc | | | | straight |
| bd | | | left\|none | right |
| be | | | | left |
When I route I should get
| waypoints | route | turns | lanes |
| a,c | in,straight,straight | depart,new name straight,arrive | ,1, |
| a,d | in,right,right | depart,turn right,arrive | ,0, |
| a,e | in,left,left | depart,turn left,arrive | ,1, |
Scenario: Basic Turn Lane 4-Way With U-Turn Lane
Given the node map
| | | e | | |
| a | 1 | b | | c |
| | | d | | |
And the ways
| nodes | turn:lanes | turn:lanes:forward | name |
| ab | | reverse;left\|through;right | in |
| bc | | | straight |
| bd | | | right |
| be | | | left |
When I route I should get
| from | to | bearings | route | turns | lanes |
| a | c | 180,180 180,180 | in,straight,straight | depart,new name straight,arrive | ,0, |
| a | d | 180,180 180,180 | in,right,right | depart,turn right,arrive | ,0, |
| a | e | 180,180 180,180 | in,left,left | depart,turn left,arrive | ,1, |
| 1 | a | 90,2 270,2 | in,in,in | depart,turn uturn,arrive | ,1, |
#this next test requires decision on how to announce lanes for going straight if there is no turn
@TODO @WORKAROUND-FIXME
Scenario: Turn with Bus-Lane
Given the node map
| a | | b | | c |
| | | | | |
| | | d | | |
And the ways
| nodes | name | turn:lanes:forward | lanes:psv:forward |
| ab | road | through\|right& | 1 |
| bc | road | | |
| bd | turn | | |
When I route I should get
| waypoints | route | turns | lanes |
| a,d | road,turn,turn | depart,turn right,arrive | ,0, |
| a,c | road,road,road | depart,use lane straight,arrive | ,1, |
#turn lanes are often drawn at the incoming road, even though the actual turn requires crossing the intersection first
@TODO @WORKAROUND-FIXME
Scenario: Turn Lanes at Segregated Road
Given the node map
| | | i | l | | |
| | | | | | |
| h | | g | f | | e |
| a | | b | c | | d |
| | | | | | |
| | | j | k | | |
And the ways
| nodes | name | turn:lanes:forward | oneway |
| ab | road | left\|through&right | yes |
| bc | road | left\|through | yes |
| cd | road | | yes |
| ef | road | \|through&through;right | yes |
| fg | road | left;through\|through& | yes |
| gh | road | | yes |
| ig | cross | | yes |
| gb | cross | left\|through | yes |
| bj | cross | | yes |
| kc | cross | left\|through;right | yes |
| cf | cross | left\|through | yes |
| fl | cross | | yes |
When I route I should get
| waypoints | route | turns | lanes | # |
| a,j | road,cross,cross | depart,turn right,arrive | ,0, | |
| a,d | road,road,road | depart,use lane straight,arrive | ,1, | #post-processing reduction |
| a,l | road,cross,cross | depart,turn left,arrive | ,2, | |
| a,h | road,road,road | depart,continue uturn,arrive | ,2, | |
| k,d | cross,road,road | depart,turn right,arrive | ,0, | |
| k,l | cross,cross,cross | depart,use lane straight,arrive | ,0, | |
| k,h | cross,road,road | depart,turn left,arrive | ,1, | |
| k,j | cross,cross,cross | depart,continue uturn,arrive | ,1, | |
| e,l | road,cross,cross | depart,turn right,arrive | ,0, | |
| e,h | road,road | depart,arrive | , | |
| e,j | road,cross,cross | depart,turn left,arrive | ,2, | |
| e,d | road,road,road | depart,continue uturn,arrive | ,2, | |
| i,h | cross,road,road | depart,turn right,arrive | ,, | |
| i,j | cross,cross,cross | depart,use lane straight,arrive | ,0, | |
| i,d | cross,road,road | depart,turn left,arrive | ,1, | |
| i,l | cross,cross,cross | depart,continue uturn,arrive | ,1, | |
Scenario: Turn Lanes at Segregated Road
Given the node map
| | | g | f | | |
| a | | b | c | | d |
| | | | | | |
| | | j | k | | |
And the ways
| nodes | name | turn:lanes:forward | oneway |
| ab | road | left\|through&right | yes |
| bc | road | | yes |
| cd | road | | yes |
| gb | cross | | yes |
| bj | cross | | yes |
| kc | cross | | yes |
| cf | cross | | yes |
When I route I should get
| waypoints | route | turns | lanes |
| a,j | road,cross,cross | depart,turn right,arrive | ,0, |
#this can happen due to traffic lights / lanes not drawn up to the intersection itself
Scenario: Turn Lanes Given earlier than actual turn
Given the node map
| a | | b | c | | d |
| | | | | | |
| | | | e | | |
And the ways
| nodes | name | turn:lanes:forward |
| ab | road | \|right |
| bc | road | |
| cd | road | |
| ce | turn | |
When I route I should get
| waypoints | route | turns | lanes |
| a,e | road,turn,turn | depart,turn right,arrive | ,0, |
| a,d | road,road,road | depart,use lane straight,arrive | ,1, |
Scenario: Turn Lanes Given earlier than actual turn
Given the node map
| a | | b | c | d | | e | | f | g | h | | i |
| | | j | | | | | | | | k | | |
And the ways
| nodes | name | turn:lanes:forward | turn:lanes:backward |
| abc | road | | |
| cd | road | | left\| |
| def | road | | |
| fg | road | \|right | |
| ghi | road | | |
| bj | first-turn | | |
| hk | second-turn | | |
When I route I should get
| waypoints | route | turns | lanes |
| a,k | road,second-turn,second-turn | depart,turn right,arrive | ,0, |
| a,i | road,road,road | depart,use lane straight,arrive | ,1, |
| i,j | road,first-turn,first-turn | depart,turn left,arrive | ,1, |
| i,a | road,road,road | depart,use lane straight,arrive | ,0, |
Scenario: Passing a one-way street
Given the node map
| e | | | f | |
| a | | b | c | d |
And the ways
| nodes | name | turn:lanes:forward | oneway |
| ab | road | left\|through | no |
| bcd | road | | no |
| eb | owi | | yes |
| cf | turn | | |
When I route I should get
| waypoints | route | turns | lanes |
| a,f | road,turn,turn | depart,turn left,arrive | ,1, |
Scenario: Passing a one-way street, partly pulled back lanes
Given the node map
| e | | | f | |
| a | | b | c | d |
| | | g | | |
And the ways
| nodes | name | turn:lanes:forward | oneway |
| ab | road | left\|through;right | no |
| bcd | road | | no |
| eb | owi | | yes |
| cf | turn | | no |
| bg | right | | no |
When I route I should get
| waypoints | route | turns | lanes |
| a,f | road,turn,turn | depart,turn left,arrive | ,1, |
| a,g | road,right,right | depart,turn right,arrive | ,0, |
Scenario: Passing a one-way street, partly pulled back lanes, no through
Given the node map
| e | | | f |
| a | | b | c |
| | | g | |
And the ways
| nodes | name | turn:lanes:forward | oneway |
| ab | road | left\|right | no |
| bc | road | | no |
| eb | owi | | yes |
| cf | turn | | no |
| bg | right | | no |
When I route I should get
| waypoints | route | turns | lanes |
| a,f | road,turn,turn | depart,turn left,arrive | ,1, |
| a,g | road,right,right | depart,turn right,arrive | ,0, |
Scenario: Narrowing Turn Lanes
Given the node map
| | | | | g | |
| | | | | | |
| a | | b | c | d | e |
| | | | f | | |
And the ways
| nodes | name | turn:lanes:forward |
| ab | road | left\|through&right |
| bc | road | |
| cd | road | left\|through |
| de | through | |
| dg | left | |
| cf | right | |
When I route I should get
| waypoints | route | turns | lanes |
| a,g | road,left,left | depart,turn left,arrive | ,2, |
| a,e | road,through,through | depart,new name straight,arrive | ,1, |
| a,f | road,right,right | depart,turn right,arrive | ,0, |
Scenario: Anticipate Lane Change
Given the node map
| a | | b | | x |
| | | | | |
| | | c | | d |
| | | | | |
| | | y | | |
And the ways
| nodes | turn:lanes:forward | turn:lanes:backward |
| ab | through\|right&right | |
| bx | | left\|left&through |
| bc | left\|through | left\|right |
| cd | | left\|right |
| cy | | |
When I route I should get
| waypoints | route | turns | lanes |
| d,a | cd,bc,ab,ab | depart,end of road right,end of road left,arrive | ,0,1, |
Scenario: Turn at a traffic light
Given the node map
| a | b | c | d |
| | | e | |
And the nodes
| node | highway |
| b | traffic_signals |
And the ways
| nodes | name | turn:lanes:forward |
| ab | road | through\|right |
| bc | road | |
| cd | road | |
| ce | turn | |
When I route I should get
| waypoints | route | turns | lanes |
| a,d | road,road,road | depart,use lane straight,arrive | ,1, |
| a,e | road,turn,turn | depart,turn right,arrive | ,0, |
Scenario: Theodor Heuss Platz
Given the node map
| | | | i | o | | | l | |
| | | b | | | | a | | m |
| | c | | | | | | | |
| | | | | | | | h | |
| | | | | | | | | |
| j | | | | | | | | |
| | | | | | | | g | |
| | | | | | | | | |
| | d | | | | | | | |
| | | e | | | | f | | |
| | | | | k | | | | n |
And the nodes
| node | highway |
| g | traffic_signals |
And the ways
| nodes | name | turn:lanes:forward | junction | oneway | highway |
| abcdef | roundabout | | roundabout | yes | primary |
| gha | roundabout | | roundabout | yes | primary |
| fg | roundabout | slight_left\|slight_left;slight_right&slight_right&slight_right | roundabout | yes | primary |
| aoib | top | | | yes | primary |
| cjd | left | | | yes | primary |
| ekf | bottom | | | yes | primary |
| fng | bottom-right | | | yes | primary |
| hma | top-right | | | yes | primary |
| hl | top-right-out | | | yes | secondary |
When I route I should get
| waypoints | route | turns | lanes |
| i,m | top,top-right,top-right | depart,roundabout-exit-4,arrive | ,0 1 2, |
| i,l | top,top-right-out,top-right-out | depart,roundabout-exit-4,arrive | ,2 3, |
| i,o | top,top,top | depart,roundabout-exit-5,arrive | ,, |
Scenario: Turn Lanes Breaking up
Given the node map
| | | | g | |
| | | | | |
| | | | c | |
| a | b | | d | e |
| | | | | |
| | | | f | |
And the ways
| nodes | name | turn:lanes:forward | oneway | highway |
| ab | road | left\|left&through&through | yes | primary |
| bd | road | through\|through | yes | primary |
| bc | road | left\|left | yes | primary |
| de | road | | yes | primary |
| fdcg | cross | | | secondary |
And the relations
| type | way:from | way:to | node:via | restriction |
| restriction | bd | fdcg | d | no_left_turn |
| restriction | bc | fdcg | c | no_right_turn |
When I route I should get
| waypoints | route | turns | lanes |
| a,g | road,cross,cross | depart,turn left,arrive | ,2 3, |
| a,e | road,road,road | depart,use lane straight,arrive | ,0 1, |
Scenario: U-Turn Road at Intersection
Given the node map
| | | | | | h | |
| | | | | f | e | j |
| a | b | | | | | |
| | | | | c | d | i |
| | | | | | g | |
And the ways
| nodes | name | turn:lanes:forward | oneway | highway |
| ab | road | | no | primary |
| di | road | | yes | primary |
| bc | road | \|through&right | yes | primary |
| cd | road | \|through&right | yes | primary |
| fc | road | | no | tertiary |
| jefb | road | | yes | primary |
| gdeh | cross | | no | primary |
When I route I should get
| from | to | bearings | route | turns | lanes |
| a | g | 180,180 180,180 | road,cross,cross | depart,turn right,arrive | ,0, |
| a | h | 180,180 180,180 | road,cross,cross | depart,turn left,arrive | ,2, |
| a | i | 180,180 180,180 | road,road,road | depart,use lane straight,arrive | ,1 2, |
| b | a | 90,2 270,2 | road,road,road | depart,continue uturn,arrive | ,2, |
Scenario: Segregated Intersection Merges With Lanes
Given the node map
| | | | | | | f |
| | | | | | | |
| e | | | d | | | |
| | | | | | c | g |
| a | | | b | | | |
| | | | | | | |
| | | | | | h | |
And the ways
| nodes | name | turn:lanes:forward | oneway | highway |
| abc | road | left\|left&left&through&through | yes | primary |
| cde | road | | yes | primary |
| hc | cross | | yes | secondary |
| cg | straight | | no | tertiary |
| cf | left | | yes | primary |
When I route I should get
| waypoints | route | turns | lanes |
| a,f | road,left,left | depart,turn left,arrive | ,2 3 4, |
| a,e | road,road,road | depart,turn uturn,arrive | ,4, |
| a,g | road,straight,straight | depart,new name straight,arrive | ,0 1, |
Scenario: Passing Through a Roundabout
Given the node map
| | | h | | g | | |
| | a | | | | f | k |
| i | | | | | | |
| | | | | | | |
| | b | | | | e | |
| | | c | | d | | |
| | | | | j | | |
And the ways
| nodes | name | turn:lanes:forward | oneway | highway | junction |
| efgha | round | | yes | primary | roundabout |
| ab | round | | yes | primary | roundabout |
| bc | round | slight_left\|slight_left&slight_right | yes | primary | roundabout |
| cd | round | | yes | primary | roundabout |
| de | round | slight_left\|slight_right | yes | primary | roundabout |
| ib | left | slight_left\|slight_left&slight_right | yes | primary | |
| cj | bottom | | yes | primary | |
| ek | right | | yes | primary | |
When I route I should get
| waypoints | route | turns | lanes |
| i,j | left,bottom,bottom | depart,round-exit-1,arrive | ,0, |
| i,k | left,right,right | depart,round-exit-2,arrive | ,1, |
Scenario: Crossing Traffic Light
Given the node map
| a | | b | | c | | d |
| | | | | | | e |
And the nodes
| node | highway |
| b | traffic_signals |
And the ways
| nodes | name | turn:lanes:forward | highway |
| abc | road | through\|through&through;slight_right&slight_right | primary |
| cd | road | | primary |
| ce | cross | | primary |
When I route I should get
| waypoints | route | turns | lanes |
| a,d | road,road,road | depart,use lane straight,arrive | ,1 2 3, |
| a,e | road,cross,cross | depart,turn slight right,arrive | ,0 1, |
Scenario: Highway Ramp
Given the node map
| a | | b | | c | | d |
| | | | | | | e |
And the ways
| nodes | name | turn:lanes:forward | highway |
| abc | hwy | through\|through&through;slight_right&slight_right | motorway |
| cd | hwy | | motorway |
| ce | ramp | | motorway_link |
When I route I should get
| waypoints | route | turns | lanes |
| a,d | hwy,hwy,hwy | depart,use lane straight,arrive | ,1 2 3, |
| a,e | hwy,ramp,ramp | depart,off ramp slight right,arrive | ,0 1, |
Scenario: Turning Off Ramp
Given the node map
| | a | |
| d | c | b |
| e | f | g |
| | h | |
And the ways
| nodes | name | turn:lanes:forward | highway | oneway |
| ac | off | left\|right | motorway_link | yes |
| bcd | road | | primary | yes |
| cf | road | | primary | |
| efg | road | | primary | yes |
| fh | on | | motorway_link | yes |
When I route I should get
| waypoints | route | turns | lanes |
| a,d | off,road,road | depart,turn_right,arrive | ,0, |
| a,g | off,road,road | depart,turn_left,arrive | ,1, |
| a,h | | | |
Scenario: Off Ramp In a Turn
Given the node map
| a | | | | | | | | | | | |
| | | | | | | | | | | | |
| | | | | | b | | | | | | c |
| | | | | | | | | | | d | |
And the ways
| nodes | name | turn:lanes:forward | highway | oneway |
| ab | hwy | through\|through&slight_right | motorway | yes |
| bc | hwy | | motorway | yes |
| bd | ramp | | motorway_link | yes |
When I route I should get
| waypoints | route | turns | lanes |
| a,c | hwy,hwy,hwy | depart,use lane slight left,arrive | ,1 2, |
| a,d | hwy,ramp,ramp | depart,off ramp slight right,arrive | ,0, |
Scenario: Reverse Lane in Segregated Road
Given the node map
| h | | | | | g | | | | | | f |
| | | | | | | | e | | | | |
| | | | | | | | d | | | | |
| a | | | | | b | | | | | | c |
And the ways
| nodes | name | turn:lanes:forward | highway | oneway |
| ab | road | reverse\|through&through | primary | yes |
| bc | road | | primary | yes |
| bdeg | road | | primary_link | yes |
| fgh | road | | primary | yes |
When I route I should get
| waypoints | route | turns | lanes |
| a,h | road,road,road | depart,continue uturn,arrive | ,2, |
Scenario: Reverse Lane in Segregated Road with none
Given the node map
| h | | | | | g | | | | | | f |
| | | | | | | | e | | | | |
| | | | | | | | d | | | | |
| a | | | | | b | | | | | | c |
And the ways
| nodes | name | turn:lanes:forward | highway | oneway |
| ab | road | reverse\|through&none | primary | yes |
| bc | road | | primary | yes |
| bdeg | road | | primary_link | yes |
| fgh | road | | primary | yes |
When I route I should get
| waypoints | route | turns | lanes |
| a,h | road,road,road | depart,continue uturn,arrive | ,2, |
Scenario: Reverse Lane in Segregated Road with none, Service Turn Prior
Given the node map
| h | | | | | g | | | | | | f |
| | | | | | | | e | | | | |
| | | | | | | | d | | | | |
| a | | j | | | b | | | | | | c |
| | | i | | | | | | | | | |
And the ways
| nodes | name | turn:lanes:forward | highway | oneway |
| ajb | road | reverse\|through&none | primary | yes |
| bc | road | | primary | yes |
| bdeg | road | | primary_link | yes |
| fgh | road | | primary | yes |
| ji | park | | service | no |
When I route I should get
| waypoints | route | turns | lanes |
| a,h | road,road,road | depart,continue uturn,arrive | ,2, |
Scenario: Don't collapse everything to u-turn / too wide
Given the node map
| a | | b | | e |
| | | | | |
| d | | c | | f |
And the ways
| nodes | highway | name | turn:lanes:forward |
| ab | primary | road | through\|right |
| bc | primary | road | |
| dc | primary | road | left\|through |
| be | secondary | top | |
| cf | secondary | bottom | |
When I route I should get
| waypoints | turns | route | lanes |
| a,d | depart,continue right,end of road right,arrive | road,road,road,road | ,0,, |
| d,a | depart,continue left,end of road left,arrive | road,road,road,road | ,1,, |
Scenario: Merge Lanes Onto Freeway
Given the node map
| a | | | b | c |
| | d | | | |
And the ways
| nodes | highway | name | turn:lanes:forward |
| abc | motorway | Hwy | |
| db | motorway_link | ramp | slight_right\|slight_right |
When I route I should get
| waypoints | turns | route | lanes |
| d,c | depart,merge slight left,arrive | ramp,Hwy,Hwy | ,0 1, |
Scenario: Fork on motorway links - don't fork on through but use lane
Given the node map
| i | | | | | a |
| j | | c | b | | x |
And the ways
| nodes | name | highway | turn:lanes:forward |
| xb | xbcj | motorway_link | |
| bc | xbcj | motorway_link | none\|slight_right |
| cj | xbcj | motorway_link | |
| ci | off | motorway_link | |
| ab | on | motorway_link | |
When I route I should get
| waypoints | route | turns | lanes |
| a,j | on,xbcj,xbcj,xbcj | depart,merge slight left,use lane straight,arrive | ,,1, |
| a,i | on,xbcj,off,off | depart,merge slight left,turn slight right,arrive | ,,0, |

View File

@ -168,6 +168,10 @@ module.exports = function () {
return instructions.legs.map(l => l.annotation.nodes.map(n => n.toString()).join(',')).join(',');
};
this.lanesList = (instructions) => {
return this.extractInstructionList(instructions, instruction => ('lanes' in instruction.maneuver ? instruction.maneuver.lanes.join(' ') : ''));
};
this.turnList = (instructions) => {
return instructions.legs.reduce((m, v) => m.concat(v.steps), [])
.map(v => {

View File

@ -33,7 +33,7 @@ module.exports = function () {
var afterRequest = (err, res, body) => {
if (err) return cb(err);
if (body && body.length) {
let destinations, pronunciations, instructions, bearings, turns, modes, times, distances, summary, intersections;
let destinations, pronunciations, instructions, bearings, turns, modes, times, distances, summary, intersections, lanes;
let json = JSON.parse(body);
@ -49,6 +49,7 @@ module.exports = function () {
modes = this.modeList(json.routes[0]);
times = this.timeList(json.routes[0]);
distances = this.distanceList(json.routes[0]);
lanes = this.lanesList(json.routes[0]);
summary = this.summary(json.routes[0]);
}
@ -102,6 +103,12 @@ module.exports = function () {
got.time = instructions ? util.format('%ds', time) : '';
}
if (headers.has('lanes')) {
got.lanes = (lanes || '').trim();
}
if (headers.has('speed')) {
if (row.speed !== '' && instructions) {
if (!row.speed.match(/\d+ km\/h/))

View File

@ -59,10 +59,10 @@ Feature: Compass bearing
Scenario: Bearing in a roundabout
Given the node map
| | d | c | |
| k | d | c | j |
| e | | | b |
| f | | | a |
| | g | h | |
| l | g | h | i |
And the ways
| nodes | oneway |
@ -74,6 +74,14 @@ Feature: Compass bearing
| fg | yes |
| gh | yes |
| ha | yes |
| dk | no |
| ke | no |
| fl | no |
| lg | no |
| hi | no |
| ia | no |
| bj | no |
| cj | no |
When I route I should get
| from | to | route | bearing |
@ -82,8 +90,10 @@ Feature: Compass bearing
Scenario: Bearing should stay constant when zig-zagging
Given the node map
| i | j | k | |
| b | d | f | h |
| a | c | e | g |
| | m | n | o |
And the ways
| nodes |
@ -94,6 +104,12 @@ Feature: Compass bearing
| ef |
| fg |
| gh |
| bi |
| cm |
| dj |
| en |
| fk |
| go |
When I route I should get
| from | to | route | bearing |

View File

@ -14,11 +14,11 @@ Feature: Bearing parameter
| ad |
When I route I should get
| from | to | bearings | route | bearing |
| from | to | bearings | route | bearing |
| b | c | 90 90 | ad,ad | 0->90,90->0|
| b | c | 180 90 | | |
| b | c | 180 90 | | |
| b | c | 80 100 | ad,ad | 0->90,90->0|
| b | c | 79 100 | | |
| b | c | 79 100 | | |
| b | c | 79,11 100 | ad,ad | 0->90,90->0|
Scenario: Testbot - Intial bearing in simple case
@ -33,13 +33,13 @@ Feature: Bearing parameter
| bc |
When I route I should get
| from | to | bearings | route | bearing |
| 0 | c | 0 0 | | |
| 0 | c | 45 45 | bc,bc | 0->44,44->0 +- 1|
| 0 | c | 85 85 | | |
| 0 | c | 95 95 | | |
| from | to | bearings | route | bearing |
| 0 | c | 0 0 | | |
| 0 | c | 45 45 | bc,bc | 0->44,44->0 +- 1 |
| 0 | c | 85 85 | | |
| 0 | c | 95 95 | | |
| 0 | c | 135 135 | ac,ac | 0->135,135->0 +- 1|
| 0 | c | 180 180 | | |
| 0 | c | 180 180 | | |
Scenario: Testbot - Initial bearing on split way
Given the node map
@ -58,21 +58,21 @@ Feature: Bearing parameter
| ha | yes |
When I route I should get
| from | to | bearings | route | bearing |
| 0 | b | 10 10 | bc,bc | 0->0,0->0 |
| 0 | b | 90 90 | ab,ab | 0->90,90->0 |
| from | to | bearings | route | bearing |
| 0 | b | 10 10 | bc,bc | 0->0,0->0 |
| 0 | b | 90 90 | ab,ab | 0->90,90->0 |
# The returned bearing is wrong here, it's based on the snapped
# coordinates, not the acutal edge bearing. This should be
# fixed one day, but it's only a problem when we snap two vias
# to the same point - DP
#| 0 | b | 170 170 | da | 180 |
#| 0 | b | 189 189 | da | 180 |
| 0 | 1 | 90 270 | ab,bc,cd,cd | 0->90,90->0,0->270,270->0 |
| 1 | d | 10 10 | bc,bc | 0->0,0->0 |
#| 0 | b | 170 170 | da | 180 |
#| 0 | b | 189 189 | da | 180 |
| 0 | 1 | 90 270 | ab,bc,cd,cd | 0->90,90->0,0->270,270->0 |
| 1 | d | 10 10 | bc,bc | 0->0,0->0 |
| 1 | d | 90 90 | ab,bc,cd,da,da | 0->90,90->0,0->270,270->180,180->0 |
| 1 | 0 | 189 189 | da,da | 0->180,180->0 |
| 1 | d | 270 270 | cd,cd | 0->270,270->0 |
| 1 | d | 349 349 | | |
| 1 | 0 | 189 189 | da,da | 0->180,180->0 |
| 1 | d | 270 270 | cd,cd | 0->270,270->0 |
| 1 | d | 349 349 | | |
Scenario: Testbot - Initial bearing in all direction
Given the node map

View File

@ -3,7 +3,7 @@ Feature: U-turns at via points
Background:
Given the profile "testbot"
Given a grid size of 100 meters
Given a grid size of 250 meters
Scenario: Continue straight at waypoints enabled by default
Given the node map

View File

@ -75,12 +75,12 @@ Feature: Avoid weird loops caused by rounding errors
And the node map
| a | | |
| b | e | |
| | | 1 |
| h | | 1 |
| | | |
| | | 2 |
| | | |
| g | | |
| | c | f |
| | d | |
| d | | |
And the ways
| nodes | highway |
@ -90,6 +90,8 @@ Feature: Avoid weird loops caused by rounding errors
| be | primary |
| ef | primary |
| cf | primary |
| cg | primary |
| bh | primary |
When I route I should get
| waypoints | route |

View File

@ -3,6 +3,7 @@ Feature: Testbot - oneways
Background:
Given the profile "testbot"
Given a grid size of 250 meters
Scenario: Routing on a oneway roundabout
Given the node map
@ -59,8 +60,8 @@ Feature: Testbot - oneways
Scenario: Testbot - Around the Block
Given the node map
| a | b |
| d | c |
| | a | b | |
| e | d | c | f |
And the ways
| nodes | oneway | foot |
@ -68,6 +69,8 @@ Feature: Testbot - oneways
| bc | | no |
| cd | | no |
| da | | no |
| de | | no |
| cf | | no |
When I route I should get
| from | to | route |

View File

@ -145,14 +145,16 @@ Feature: Estimation of travel time
Scenario: Time of travel on a series of ways
Given the node map
| a | b | |
| | c | d |
| a | b | e |
| f | c | d |
And the ways
| nodes | highway |
| ab | primary |
| bc | primary |
| cd | primary |
| be | primary |
| cf | primary |
When I route I should get
| from | to | route | time |

View File

@ -85,13 +85,16 @@ Feature: Via points
Scenario: Via points on ring of oneways
# xa it to avoid only having a single ring, which cna trigger edge cases
Given the node map
| x | | | | | | |
| a | 1 | b | 2 | c | 3 | d |
| f | | | | | | e |
| | x | | | | | | g | |
| | a | 1 | b | 2 | c | 3 | d | |
| i | f | | | | | | e | h |
And the ways
| nodes | oneway |
| xa | |
| if | |
| gd | |
| eh | |
| ab | yes |
| bc | yes |
| cd | yes |
@ -110,13 +113,16 @@ Feature: Via points
Scenario: Via points on ring on the same oneway
# xa it to avoid only having a single ring, which cna trigger edge cases
Given the node map
| x | | | | |
| a | 1 | 2 | 3 | b |
| d | | | | c |
| | x | | | | e | |
| | a | 1 | 2 | 3 | b | |
| g | d | | | | c | f |
And the ways
| nodes | oneway |
| xa | |
| eb | |
| cf | |
| dg | |
| ab | yes |
| bc | yes |
| cd | yes |

View File

@ -63,12 +63,6 @@ inline extractor::guidance::DirectionModifier::Enum angleToDirectionModifier(con
return extractor::guidance::DirectionModifier::Left;
}
inline double angularDeviation(const double angle, const double from)
{
const double deviation = std::abs(angle - from);
return std::min(360 - deviation, deviation);
}
} // namespace guidance
} // namespace engine
} // namespace osrm

View File

@ -55,7 +55,8 @@ class EdgeBasedGraphFactory
std::shared_ptr<const RestrictionMap> restriction_map,
const std::vector<QueryNode> &node_info_list,
ProfileProperties profile_properties,
const util::NameTable &name_table);
const util::NameTable &name_table,
const util::NameTable &turn_lanes);
void Run(const std::string &original_edge_data_filename,
lua_State *lua_state,
@ -117,6 +118,7 @@ class EdgeBasedGraphFactory
ProfileProperties profile_properties;
const util::NameTable &name_table;
const util::NameTable &turn_lanes;
void CompressGeometry();
unsigned RenumberEdges();

View File

@ -37,7 +37,9 @@ class ExtractionContainers
void WriteNodes(std::ofstream &file_out_stream) const;
void WriteRestrictions(const std::string &restrictions_file_name) const;
void WriteEdges(std::ofstream &file_out_stream) const;
void WriteNames(const std::string &names_file_name) const;
void WriteCharData(const std::string &file_name,
const stxxl::vector<unsigned> &offests,
const stxxl::vector<char> &char_data) const;
public:
using STXXLNodeIDVector = stxxl::vector<OSMNodeID>;
@ -51,6 +53,8 @@ class ExtractionContainers
STXXLEdgeVector all_edges_list;
stxxl::vector<char> name_char_data;
stxxl::vector<unsigned> name_lengths;
stxxl::vector<char> turn_lane_char_data;
stxxl::vector<unsigned> turn_lane_lengths;
STXXLRestrictionsVector restrictions_list;
STXXLWayIDStartEndVector way_start_end_id_list;
std::unordered_map<OSMNodeID, NodeID> external_to_internal_node_id_map;
@ -61,6 +65,7 @@ class ExtractionContainers
void PrepareData(const std::string &output_file_name,
const std::string &restrictions_file_name,
const std::string &names_file_name,
const std::string &turn_lane_file_name,
lua_State *segment_state);
};
}

View File

@ -7,6 +7,8 @@
#include <limits>
#include <string>
#include "extractor/guidance/toolkit.hpp"
namespace osrm
{
namespace extractor
@ -98,6 +100,12 @@ inline unsigned parseDuration(const std::string &s)
return !s.empty() && iter == s.end() ? duration : std::numeric_limits<unsigned>::max();
}
inline std::string
trimLaneString(std::string lane_string, std::int32_t count_left, std::int32_t count_right)
{
return extractor::guidance::trimLaneString(std::move(lane_string), count_left, count_right);
}
}
}

View File

@ -3,6 +3,7 @@
#include "extractor/travel_mode.hpp"
#include "util/typedefs.hpp"
#include "util/guidance/turn_lanes.hpp"
#include <string>
#include <vector>
@ -35,6 +36,8 @@ struct ExtractionWay
destinations.clear();
forward_travel_mode = TRAVEL_MODE_INACCESSIBLE;
backward_travel_mode = TRAVEL_MODE_INACCESSIBLE;
turn_lanes_forward.clear();
turn_lanes_backward.clear();
}
// These accessors exists because it's not possible to take the address of a bitfield,
@ -50,6 +53,8 @@ struct ExtractionWay
std::string name;
std::string pronunciation;
std::string destinations;
std::string turn_lanes_forward;
std::string turn_lanes_backward;
bool roundabout;
bool is_access_restricted;
bool is_startpoint;

View File

@ -38,6 +38,7 @@ class ExtractorCallbacks
using MapKey = std::pair<std::string, std::string>;
using MapVal = unsigned;
std::unordered_map<MapKey, MapVal, boost::hash<MapKey>> string_map;
std::unordered_map<std::string, LaneStringID> lane_map;
ExtractionContainers &external_memory;
public:

View File

@ -61,6 +61,7 @@ struct ExtractorConfig
output_file_name = basepath + ".osrm";
restriction_file_name = basepath + ".osrm.restrictions";
names_file_name = basepath + ".osrm.names";
turn_lane_file_name = basepath + ".osrm.tld";
timestamp_file_name = basepath + ".osrm.timestamp";
geometry_output_path = basepath + ".osrm.geometry";
node_output_path = basepath + ".osrm.nodes";
@ -82,6 +83,7 @@ struct ExtractorConfig
std::string output_file_name;
std::string restriction_file_name;
std::string names_file_name;
std::string turn_lane_file_name;
std::string timestamp_file_name;
std::string geometry_output_path;
std::string edge_output_path;

View File

@ -0,0 +1,46 @@
#ifndef OSRM_EXTRACTOR_GUIDANCE_DEBUG_HPP_
#define OSRM_EXTRACTOR_GUIDANCE_DEBUG_HPP_
#include <iomanip>
#include <iostream>
#include <string>
namespace osrm
{
namespace extractor
{
namespace guidance
{
inline void print(const LaneDataVector &turn_lane_data)
{
std::cout << " Tags:\n";
for (auto entry : turn_lane_data)
std::cout << "\t" << entry.tag << " from: " << static_cast<int>(entry.from)
<< " to: " << static_cast<int>(entry.to) << "\n";
std::cout << std::flush;
}
inline void printTurnAssignmentData(const NodeID at,
const LaneDataVector &turn_lane_data,
const Intersection &intersection,
const std::vector<QueryNode> &node_info_list)
{
std::cout << "[Turn Assignment Progress]\nLocation:";
auto coordinate = node_info_list[at];
std::cout << std::setprecision(12) << toFloating(coordinate.lat) << " "
<< toFloating(coordinate.lon) << "\n";
std::cout << " Intersection:\n";
for (const auto &road)
std::cout << "\t" << toString(road) << "\n";
//flushes as well
print(turn_lane_data);
}
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif /* OSRM_EXTRACTOR_GUIDANCE_DEBUG_HPP_ */

View File

@ -59,6 +59,9 @@ std::string toString(const ConnectedRoad &road);
typedef std::vector<ConnectedRoad> Intersection;
Intersection::const_iterator findClosestTurn(const Intersection &intersection, const double angle);
Intersection::iterator findClosestTurn(Intersection &intersection, const double angle);
} // namespace guidance
} // namespace extractor
} // namespace osrm

View File

@ -7,6 +7,7 @@
#include "extractor/restriction_map.hpp"
#include "util/node_based_graph.hpp"
#include "util/typedefs.hpp"
#include "util/name_table.hpp"
#include <unordered_set>
#include <vector>

View File

@ -2,7 +2,6 @@
#define OSRM_EXTRACTOR_GUIDANCE_MOTORWAY_HANDLER_HPP_
#include "extractor/guidance/intersection.hpp"
#include "extractor/guidance/intersection_generator.hpp"
#include "extractor/guidance/intersection_handler.hpp"
#include "extractor/query_node.hpp"
@ -26,8 +25,7 @@ class MotorwayHandler : public IntersectionHandler
MotorwayHandler(const util::NodeBasedDynamicGraph &node_based_graph,
const std::vector<QueryNode> &node_info_list,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table,
const IntersectionGenerator &intersection_generator);
const SuffixTable &street_name_suffix_table);
~MotorwayHandler() override final;
// check whether the handler can actually handle the intersection
@ -47,8 +45,6 @@ class MotorwayHandler : public IntersectionHandler
Intersection fromRamp(const EdgeID via_edge, Intersection intersection) const;
Intersection fallback(Intersection intersection) const;
const IntersectionGenerator &intersection_generator;
};
} // namespace guidance

View File

@ -476,6 +476,77 @@ inline bool hasRoundaboutType(const TurnInstruction instruction)
return std::find(valid_types, valid_end, instruction.type) != valid_end;
}
// Public service vehicle lanes and similar can introduce additional lanes into the lane string that
// are not specifically marked for left/right turns. This function can be used from the profile to
// trim the lane string appropriately
//
// left|throught|
// in combination with lanes:psv:forward=1
// will be corrected to left|throught, since the final lane is not drivable.
// This is in contrast to a situation with lanes:psv:forward=0 (or not set) where left|through|
// represents left|through|through
inline std::string
trimLaneString(std::string lane_string, std::int32_t count_left, std::int32_t count_right)
{
if (count_left)
{
bool sane = count_left < static_cast<std::int32_t>(lane_string.size());
for (std::int32_t i = 0; i < count_left; ++i)
// this is adjusted for our fake pipe. The moment cucumber can handle multiple escaped
// pipes, the '&' part can be removed
if (lane_string[i] != '|' && lane_string[i] != '&')
{
sane = false;
break;
}
if (sane)
{
lane_string.erase(lane_string.begin(), lane_string.begin() + count_left);
}
}
if (count_right)
{
bool sane = count_right < static_cast<std::int32_t>(lane_string.size());
for (auto itr = lane_string.rbegin();
itr != lane_string.rend() && itr != lane_string.rbegin() + count_right;
++itr)
{
if (*itr != '|' && *itr != '&')
{
sane = false;
break;
}
}
if (sane)
lane_string.resize(lane_string.size() - count_right);
}
return lane_string;
}
inline bool entersRoundabout(const extractor::guidance::TurnInstruction instruction)
{
return (instruction.type == extractor::guidance::TurnType::EnterRoundabout ||
instruction.type == extractor::guidance::TurnType::EnterRotary ||
instruction.type == extractor::guidance::TurnType::EnterRoundaboutIntersection ||
instruction.type == extractor::guidance::TurnType::EnterRoundaboutAtExit ||
instruction.type == extractor::guidance::TurnType::EnterRotaryAtExit ||
instruction.type == extractor::guidance::TurnType::EnterRoundaboutIntersectionAtExit ||
instruction.type == extractor::guidance::TurnType::EnterAndExitRoundabout ||
instruction.type == extractor::guidance::TurnType::EnterAndExitRotary ||
instruction.type == extractor::guidance::TurnType::EnterAndExitRotary);
}
inline bool leavesRoundabout(const extractor::guidance::TurnInstruction instruction)
{
return (instruction.type == extractor::guidance::TurnType::ExitRoundabout ||
instruction.type == extractor::guidance::TurnType::ExitRotary ||
instruction.type == extractor::guidance::TurnType::ExitRoundaboutIntersection ||
instruction.type == extractor::guidance::TurnType::EnterAndExitRoundabout ||
instruction.type == extractor::guidance::TurnType::EnterAndExitRotary ||
instruction.type == extractor::guidance::TurnType::EnterAndExitRoundaboutIntersection);
}
} // namespace guidance
} // namespace extractor
} // namespace osrm

View File

@ -44,10 +44,14 @@ class TurnAnalysis
const SuffixTable &street_name_suffix_table);
// the entry into the turn analysis
std::vector<TurnOperation> getTurns(const NodeID from_node, const EdgeID via_eid) const;
// access to the intersection representation for classification purposes
Intersection getIntersection(const NodeID from_node, const EdgeID via_eid) const;
Intersection
assignTurnTypes(const NodeID from_node, const EdgeID via_eid, Intersection intersection) const;
std::vector<TurnOperation>
transformIntersectionIntoTurns(const Intersection &intersection) const;
const IntersectionGenerator& getGenerator() const;
private:
const util::NodeBasedDynamicGraph &node_based_graph;

View File

@ -0,0 +1,38 @@
#ifndef OSRM_EXTRACTOR_GUIDANCE_TURN_DISCOVERY_HPP_
#define OSRM_EXTRACTOR_GUIDANCE_TURN_DISCOVERY_HPP_
#include "extractor/guidance/intersection.hpp"
#include "extractor/guidance/turn_analysis.hpp"
#include "util/typedefs.hpp"
#include <string>
namespace osrm
{
namespace extractor
{
namespace guidance
{
namespace lanes
{
// OSRM processes edges by looking at a via_edge, coming into an intersection. For turn lanes, we
// might require to actually look back a turn. We do so in the hope that the turn lanes match up at
// the previous intersection for all incoming lanes.
bool findPreviousIntersection(
const NodeID node,
const EdgeID via_edge,
const Intersection intersection,
const TurnAnalysis &turn_analysis, // to generate other intersections
const util::NodeBasedDynamicGraph &node_based_graph, // query edge data
// output parameters, will be in an arbitrary state on failure
NodeID &result_node,
EdgeID &result_via_edge,
Intersection &result_intersection);
} // namespace lanes
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif /*OSRM_EXTRACTOR_GUIDANCE_TURN_DISCOVERY_HPP_*/

View File

@ -6,6 +6,8 @@
#include <boost/assert.hpp>
#include "extractor/guidance/roundabout_type.hpp"
#include "util/guidance/turn_lanes.hpp"
#include "util/typedefs.hpp"
namespace osrm
{
@ -53,48 +55,47 @@ const constexpr Enum EnterRotary = 12; // Enter a rotary
const constexpr Enum EnterAndExitRotary = 13; // Touching a rotary
const constexpr Enum EnterRoundaboutIntersection = 14; // Entering a small Roundabout
const constexpr Enum EnterAndExitRoundaboutIntersection = 15; // Touching a roundabout
const constexpr Enum UseLane = 16; // No Turn, but you need to stay on a given lane!
// Values below here are silent instructions
const constexpr Enum NoTurn = 16; // end of segment without turn/middle of a segment
const constexpr Enum Suppressed = 17; // location that suppresses a turn
const constexpr Enum EnterRoundaboutAtExit = 18; // Entering a small Roundabout at a countable exit
const constexpr Enum ExitRoundabout = 19; // Exiting a small Roundabout
const constexpr Enum EnterRotaryAtExit = 20; // Enter A Rotary at a countable exit
const constexpr Enum ExitRotary = 21; // Exit a rotary
const constexpr Enum NoTurn = 17; // end of segment without turn/middle of a segment
const constexpr Enum Suppressed = 18; // location that suppresses a turn
const constexpr Enum EnterRoundaboutAtExit = 19; // Entering a small Roundabout at a countable exit
const constexpr Enum ExitRoundabout = 20; // Exiting a small Roundabout
const constexpr Enum EnterRotaryAtExit = 21; // Enter A Rotary at a countable exit
const constexpr Enum ExitRotary = 22; // Exit a rotary
const constexpr Enum EnterRoundaboutIntersectionAtExit =
22; // Entering a small Roundabout at a countable exit
const constexpr Enum ExitRoundaboutIntersection = 23; // Exiting a small Roundabout
const constexpr Enum StayOnRoundabout = 24; // Continue on Either a small or a large Roundabout
23; // Entering a small Roundabout at a countable exit
const constexpr Enum ExitRoundaboutIntersection = 24; // Exiting a small Roundabout
const constexpr Enum StayOnRoundabout = 25; // Continue on Either a small or a large Roundabout
const constexpr Enum Sliproad =
25; // Something that looks like a ramp, but is actually just a small sliproad
26; // Something that looks like a ramp, but is actually just a small sliproad
}
// turn angle in 1.40625 degree -> 128 == 180 degree
struct TurnInstruction
{
using LaneTupel = util::guidance::LaneTupel;
TurnInstruction(const TurnType::Enum type = TurnType::Invalid,
const DirectionModifier::Enum direction_modifier = DirectionModifier::Straight)
: type(type), direction_modifier(direction_modifier)
const DirectionModifier::Enum direction_modifier = DirectionModifier::Straight,
const LaneTupel lane_tupel = {0, INVALID_LANEID})
: type(type), direction_modifier(direction_modifier), lane_tupel(lane_tupel)
{
}
TurnType::Enum type : 5;
DirectionModifier::Enum direction_modifier : 3;
// the lane tupel that is used for the turn
LaneTupel lane_tupel;
static TurnInstruction INVALID()
{
return TurnInstruction(TurnType::Invalid, DirectionModifier::UTurn);
}
static TurnInstruction INVALID() { return {TurnType::Invalid, DirectionModifier::UTurn}; }
static TurnInstruction NO_TURN()
{
return TurnInstruction(TurnType::NoTurn, DirectionModifier::UTurn);
}
static TurnInstruction NO_TURN() { return {TurnType::NoTurn, DirectionModifier::UTurn}; }
static TurnInstruction REMAIN_ROUNDABOUT(const RoundaboutType,
const DirectionModifier::Enum modifier)
{
return TurnInstruction(TurnType::StayOnRoundabout, modifier);
return {TurnType::StayOnRoundabout, modifier};
}
static TurnInstruction ENTER_ROUNDABOUT(const RoundaboutType roundabout_type,
@ -146,16 +147,18 @@ struct TurnInstruction
}
};
static_assert(sizeof(TurnInstruction) == 1, "TurnInstruction does not fit one byte");
static_assert(sizeof(TurnInstruction) == 3, "TurnInstruction does not fit three byte");
inline bool operator!=(const TurnInstruction lhs, const TurnInstruction rhs)
{
return lhs.type != rhs.type || lhs.direction_modifier != rhs.direction_modifier;
return lhs.type != rhs.type || lhs.direction_modifier != rhs.direction_modifier ||
lhs.lane_tupel != rhs.lane_tupel;
}
inline bool operator==(const TurnInstruction lhs, const TurnInstruction rhs)
{
return lhs.type == rhs.type && lhs.direction_modifier == rhs.direction_modifier;
return lhs.type == rhs.type && lhs.direction_modifier == rhs.direction_modifier &&
lhs.lane_tupel == rhs.lane_tupel;
}
} // namespace guidance

View File

@ -0,0 +1,24 @@
#ifndef OSRM_EXTRACTOR_GUIDANCE_TURN_LANE_AUGMENTATION_HPP_
#define OSRM_EXTRACTOR_GUIDANCE_TURN_LANE_AUGMENTATION_HPP_
#include "extractor/guidance/intersection.hpp"
#include "extractor/guidance/turn_lane_data.hpp"
namespace osrm
{
namespace extractor
{
namespace guidance
{
namespace lanes
{
LaneDataVector handleNoneValueAtSimpleTurn(LaneDataVector lane_data,
const Intersection &intersection);
} // namespace lanes
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif /* OSRM_EXTRACTOR_GUIDANCE_TURN_LANE_AUGMENTATION_HPP_ */

View File

@ -0,0 +1,42 @@
#ifndef OSRM_EXTRACTOR_GUIDANCE_TURN_LANE_DATA_HPP_
#define OSRM_EXTRACTOR_GUIDANCE_TURN_LANE_DATA_HPP_
#include "util/typedefs.hpp"
#include <string>
#include <vector>
namespace osrm
{
namespace extractor
{
namespace guidance
{
namespace lanes
{
struct TurnLaneData
{
std::string tag;
LaneID from;
LaneID to;
bool operator<(const TurnLaneData &other) const;
};
typedef std::vector<TurnLaneData> LaneDataVector;
// convertes a string given in the OSM format into a TurnLaneData vector
LaneDataVector laneDataFromString(std::string turn_lane_string);
// Locate A Tag in a lane data vector
LaneDataVector::const_iterator findTag(const std::string &tag, const LaneDataVector &data);
LaneDataVector::iterator findTag(const std::string &tag, LaneDataVector &data);
bool hasTag(const std::string &tag, const LaneDataVector &data);
} // namespace lane_data_generation
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif /* OSRM_EXTRACTOR_GUIDANCE_TURN_LANE_DATA_HPP_ */

View File

@ -0,0 +1,79 @@
#ifndef OSRM_EXTRACTOR_GUIDANCE_TURN_LANE_HANDLER_HPP_
#define OSRM_EXTRACTOR_GUIDANCE_TURN_LANE_HANDLER_HPP_
#include "extractor/guidance/intersection.hpp"
#include "extractor/guidance/turn_analysis.hpp"
#include "extractor/guidance/turn_lane_data.hpp"
#include "extractor/query_node.hpp"
#include "util/guidance/turn_lanes.hpp"
#include "util/name_table.hpp"
#include "util/node_based_graph.hpp"
#include "util/typedefs.hpp"
#include <map>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
namespace osrm
{
namespace extractor
{
namespace guidance
{
// Given an Intersection, the graph to access the data and the turn lanes, the turn lane matcher
// assigns appropriate turn tupels to the different turns.
namespace lanes
{
class TurnLaneHandler
{
public:
typedef std::vector<TurnLaneData> LaneDataVector;
TurnLaneHandler(const util::NodeBasedDynamicGraph &node_based_graph,
const util::NameTable &turn_lane_strings,
const std::vector<QueryNode> &node_info_list,
const TurnAnalysis &turn_analysis);
Intersection
assignTurnLanes(const NodeID at, const EdgeID via_edge, Intersection intersection) const;
private:
using LaneTupel = util::guidance::LaneTupel;
std::unordered_map<LaneTupel, std::uint16_t> lane_tupels;
// we need to be able to look at previous intersections to, in some cases, find the correct turn
// lanes for a turn
const util::NodeBasedDynamicGraph &node_based_graph;
const util::NameTable &turn_lane_strings;
const std::vector<QueryNode> &node_info_list;
const TurnAnalysis &turn_analysis;
// check whether we can handle an intersection
bool isSimpleIntersection(const LaneDataVector &turn_lane_data,
const Intersection &intersection) const;
// in case of a simple intersection, assign the lane entries
Intersection simpleMatchTuplesToTurns(Intersection intersection,
const LaneDataVector &lane_data) const;
// partition lane data into lane data relevant at current turn and at next turn
std::pair<TurnLaneHandler::LaneDataVector, TurnLaneHandler::LaneDataVector> partitionLaneData(
const NodeID at, LaneDataVector turn_lane_data, const Intersection &intersection) const;
// if the current intersections turn string is empty, we check whether there is an incoming
// intersection whose turns might be related to this current intersection
Intersection handleTurnAtPreviousIntersection(const NodeID at,
const EdgeID via_edge,
Intersection intersection) const;
};
} // namespace lanes
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif // OSRM_EXTRACTOR_GUIDANCE_TURN_LANE_HANDLER_HPP_

View File

@ -0,0 +1,54 @@
#ifndef OSRM_EXTRACTOR_GUIDANCE_TURN_LANE_MATCHER_HPP_
#define OSRM_EXTRACTOR_GUIDANCE_TURN_LANE_MATCHER_HPP_
#include "extractor/guidance/intersection.hpp"
#include "extractor/guidance/turn_instruction.hpp"
#include "extractor/guidance/turn_lane_data.hpp"
#include "util/guidance/turn_lanes.hpp"
#include "util/node_based_graph.hpp"
namespace osrm
{
namespace extractor
{
namespace guidance
{
namespace lanes
{
// Translate Turn Lane Tags into a matching modifier
DirectionModifier::Enum getMatchingModifier(const std::string &tag);
// check whether a match of a given tag and a turn instruction can be seen as valid
bool isValidMatch(const std::string &tag, const TurnInstruction instruction);
// Every tag is somewhat idealized in form of the expected angle. A through lane should go straight
// (or follow a 180 degree turn angle between in/out segments.) The following function tries to find
// the best possible match for every tag in a given intersection, considering a few corner cases
// introduced to OSRM handling u-turns
typename Intersection::const_iterator findBestMatch(const std::string &tag,
const Intersection &intersection);
// Reverse is a special case, because it requires access to the leftmost tag. It has its own
// matching function as a result of that. The leftmost tag is required, since u-turns are disabled
// by default in OSRM. Therefor we cannot check whether a turn is allowed, since it could be
// possible that it is forbidden. In addition, the best u-turn angle does not necessarily represent
// the u-turn, since it could be a sharp-left turn instead on a road with a middle island.
typename Intersection::const_iterator findBestMatchForReverse(const std::string &leftmost_tag,
const Intersection &intersection);
// a match is trivial if all turns can be associated with their best match in a valid way and the
// matches occur in order
bool canMatchTrivially(const Intersection &intersection, const LaneDataVector &lane_data);
// perform a trivial match on the turn lanes
Intersection triviallyMatchLanesToTurns(Intersection intersection,
const LaneDataVector &lane_data,
const util::NodeBasedDynamicGraph &node_based_graph);
} // namespace lanes
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif /*OSRM_EXTRACTOR_GUIDANCE_TURN_LANE_MATCHER_HPP_*/

View File

@ -29,7 +29,6 @@ struct InternalExtractorEdge
struct WeightData
{
WeightData() : duration(0.0), type(WeightType::INVALID) {}
union {
@ -51,6 +50,7 @@ struct InternalExtractorEdge
true,
TRAVEL_MODE_INACCESSIBLE,
false,
INVALID_LANE_STRINGID,
guidance::RoadClassificationData())
{
}
@ -66,6 +66,7 @@ struct InternalExtractorEdge
bool startpoint,
TravelMode travel_mode,
bool is_split,
const LaneStringID lane_id,
guidance::RoadClassificationData road_classification)
: result(OSMNodeID(source),
OSMNodeID(target),
@ -78,6 +79,7 @@ struct InternalExtractorEdge
startpoint,
travel_mode,
is_split,
lane_id,
std::move(road_classification)),
weight_data(std::move(weight_data))
{
@ -104,6 +106,7 @@ struct InternalExtractorEdge
true,
TRAVEL_MODE_INACCESSIBLE,
false,
INVALID_LANE_STRINGID,
guidance::RoadClassificationData());
}
static InternalExtractorEdge max_osm_value()
@ -119,6 +122,7 @@ struct InternalExtractorEdge
true,
TRAVEL_MODE_INACCESSIBLE,
false,
INVALID_LANE_STRINGID,
guidance::RoadClassificationData());
}

View File

@ -26,6 +26,7 @@ struct NodeBasedEdge
bool startpoint,
TravelMode travel_mode,
bool is_split,
const LaneStringID lane_id,
guidance::RoadClassificationData road_classification);
bool operator<(const NodeBasedEdge &other) const;
@ -41,6 +42,7 @@ struct NodeBasedEdge
bool startpoint : 1;
bool is_split : 1;
TravelMode travel_mode : 4;
LaneStringID lane_string_id;
guidance::RoadClassificationData road_classification;
};
@ -57,6 +59,7 @@ struct NodeBasedEdgeWithOSM : NodeBasedEdge
bool startpoint,
TravelMode travel_mode,
bool is_split,
const LaneStringID lane_string_id,
guidance::RoadClassificationData road_classification);
OSMNodeID osm_source_id;
@ -68,7 +71,7 @@ struct NodeBasedEdgeWithOSM : NodeBasedEdge
inline NodeBasedEdge::NodeBasedEdge()
: source(SPECIAL_NODEID), target(SPECIAL_NODEID), name_id(0), weight(0), forward(false),
backward(false), roundabout(false), access_restricted(false), startpoint(true),
is_split(false), travel_mode(false)
is_split(false), travel_mode(false), lane_string_id(0)
{
}
@ -83,10 +86,11 @@ inline NodeBasedEdge::NodeBasedEdge(NodeID source,
bool startpoint,
TravelMode travel_mode,
bool is_split,
const LaneStringID lane_string_id,
guidance::RoadClassificationData road_classification)
: source(source), target(target), name_id(name_id), weight(weight), forward(forward),
backward(backward), roundabout(roundabout), access_restricted(access_restricted),
startpoint(startpoint), is_split(is_split), travel_mode(travel_mode),
startpoint(startpoint), is_split(is_split), travel_mode(travel_mode), lane_string_id(lane_string_id),
road_classification(std::move(road_classification))
{
}
@ -120,6 +124,7 @@ inline NodeBasedEdgeWithOSM::NodeBasedEdgeWithOSM(
bool startpoint,
TravelMode travel_mode,
bool is_split,
const LaneStringID lane_string_id,
guidance::RoadClassificationData road_classification)
: NodeBasedEdge(SPECIAL_NODEID,
SPECIAL_NODEID,
@ -132,6 +137,7 @@ inline NodeBasedEdgeWithOSM::NodeBasedEdgeWithOSM(
startpoint,
travel_mode,
is_split,
lane_string_id,
std::move(road_classification)),
osm_source_id(std::move(source)), osm_target_id(std::move(target))
{

View File

@ -65,6 +65,42 @@ mirrorDirectionModifier(const extractor::guidance::DirectionModifier::Enum modif
return results[modifier];
}
inline bool hasLeftModifier(const extractor::guidance::TurnInstruction instruction)
{
return instruction.direction_modifier == extractor::guidance::DirectionModifier::SharpLeft ||
instruction.direction_modifier == extractor::guidance::DirectionModifier::Left ||
instruction.direction_modifier == extractor::guidance::DirectionModifier::SlightLeft;
}
inline bool hasRightModifier(const extractor::guidance::TurnInstruction instruction)
{
return instruction.direction_modifier == extractor::guidance::DirectionModifier::SharpRight ||
instruction.direction_modifier == extractor::guidance::DirectionModifier::Right ||
instruction.direction_modifier == extractor::guidance::DirectionModifier::SlightRight;
}
inline bool isLeftTurn(const extractor::guidance::TurnInstruction instruction)
{
switch (instruction.type)
{
case extractor::guidance::TurnType::Merge:
return hasRightModifier(instruction);
default:
return hasLeftModifier(instruction);
}
}
inline bool isRightTurn(const extractor::guidance::TurnInstruction instruction)
{
switch (instruction.type)
{
case extractor::guidance::TurnType::Merge:
return hasLeftModifier(instruction);
default:
return hasRightModifier(instruction);
}
}
} // namespace guidance
} // namespace util
} // namespace osrm

View File

@ -0,0 +1,85 @@
#ifndef OSRM_UTIL_GUIDANCE_TURN_LANES_HPP
#define OSRM_UTIL_GUIDANCE_TURN_LANES_HPP
#include <cstddef>
#include <cstdint>
#include <functional>
#include <vector>
#include "util/typedefs.hpp"
#include <boost/functional/hash.hpp>
namespace osrm
{
namespace util
{
namespace guidance
{
class LaneTupel;
} // namespace guidance
} // namespace util
} // namespace osrm
namespace std
{
template <> struct hash<::osrm::util::guidance::LaneTupel>
{
inline std::size_t operator()(const ::osrm::util::guidance::LaneTupel &bearing_class) const;
};
} // namespace std
namespace osrm
{
namespace util
{
namespace guidance
{
// The mapping of turn lanes can be done two values. We describe every turn by the number of
// contributing lanes and the first lane from the right..
// Given a road like this:
// | | |
// | | |
// ----------- |
// -^ |
// ----------- -------------
// -^ ->
// --------------------------------
// -v |
// ---------- |
// | |
//
// we generate a set of tuples in the form of:
// (2,1), (1,1), (1,0) for left, through and right respectively
class LaneTupel
{
public:
LaneTupel();
LaneTupel(const LaneID lanes_in_turn, const LaneID first_lane_from_the_right);
bool operator==(const LaneTupel other) const;
bool operator!=(const LaneTupel other) const;
bool operator<(const LaneTupel other) const;
LaneID lanes_in_turn;
LaneID first_lane_from_the_right;
friend std::size_t std::hash<LaneTupel>::operator()(const LaneTupel &) const;
};
} // namespace guidance
} // namespace util
} // namespace osrm
// make Bearing Class hasbable
namespace std
{
inline size_t hash<::osrm::util::guidance::LaneTupel>::
operator()(const ::osrm::util::guidance::LaneTupel &lane_tupel) const
{
return boost::hash_value(*reinterpret_cast<const std::uint16_t *>(&lane_tupel));
}
} // namespace std
#endif /* OSRM_UTIL_GUIDANCE_TURN_LANES_HPP */

View File

@ -20,7 +20,7 @@ struct NodeBasedEdgeData
NodeBasedEdgeData()
: distance(INVALID_EDGE_WEIGHT), edge_id(SPECIAL_NODEID),
name_id(std::numeric_limits<unsigned>::max()), access_restricted(false), reversed(false),
roundabout(false), travel_mode(TRAVEL_MODE_INACCESSIBLE)
roundabout(false), travel_mode(TRAVEL_MODE_INACCESSIBLE), lane_string_id(INVALID_LANE_STRINGID)
{
}
@ -31,10 +31,11 @@ struct NodeBasedEdgeData
bool reversed,
bool roundabout,
bool startpoint,
extractor::TravelMode travel_mode)
extractor::TravelMode travel_mode,
const LaneStringID lane_string_id)
: distance(distance), edge_id(edge_id), name_id(name_id),
access_restricted(access_restricted), reversed(reversed), roundabout(roundabout),
startpoint(startpoint), travel_mode(travel_mode)
startpoint(startpoint), travel_mode(travel_mode), lane_string_id(lane_string_id)
{
}
@ -46,6 +47,7 @@ struct NodeBasedEdgeData
bool roundabout : 1;
bool startpoint : 1;
extractor::TravelMode travel_mode : 4;
LaneStringID lane_string_id;
extractor::guidance::RoadClassificationData road_classification;
bool IsCompatibleTo(const NodeBasedEdgeData &other) const
@ -80,6 +82,7 @@ NodeBasedDynamicGraphFromEdges(NodeID number_of_nodes,
output_edge.data.travel_mode = input_edge.travel_mode;
output_edge.data.startpoint = input_edge.startpoint;
output_edge.data.road_classification = input_edge.road_classification;
output_edge.data.lane_string_id = input_edge.lane_string_id;
});
tbb::parallel_sort(edges_list.begin(), edges_list.end());

View File

@ -59,13 +59,18 @@ using EdgeID = std::uint32_t;
using NameID = std::uint32_t;
using EdgeWeight = std::int32_t;
using LaneStringID = std::uint16_t;
using LaneID = std::uint8_t;
static const LaneID INVALID_LANEID = std::numeric_limits<LaneID>::max();
static const LaneStringID INVALID_LANE_STRINGID = std::numeric_limits<LaneStringID>::max();
using BearingClassID = std::uint32_t;
static const BearingClassID INVALID_BEARING_CLASSID = std::numeric_limits<std::uint32_t>::max();
static const BearingClassID INVALID_BEARING_CLASSID = std::numeric_limits<BearingClassID>::max();
using DiscreteBearing = std::uint16_t;
using EntryClassID = std::uint16_t;
static const EntryClassID INVALID_ENTRY_CLASSID = std::numeric_limits<std::uint16_t>::max();
static const EntryClassID INVALID_ENTRY_CLASSID = std::numeric_limits<EntryClassID>::max();
static const NodeID SPECIAL_NODEID = std::numeric_limits<NodeID>::max();
static const NodeID SPECIAL_SEGMENTID = std::numeric_limits<NodeID>::max() >> 1;

View File

@ -166,6 +166,56 @@ function get_exceptions(vector)
end
end
-- returns forward,backward psv lane count
local function getPSVCounts(way)
local psv = way:get_value_by_key("lanes:psv")
local psv_forward = way:get_value_by_key("lanes:psv:forward");
local psv_backward = way:get_value_by_key("lanes:psv:backward");
local fw = 0;
local bw = 0;
if( psv and psv ~= "" ) then
fw = tonumber(psv)
if( fw == nil ) then
fw = 0
end
end
if( psv_forward and psv_forward ~= "" ) then
fw = tonumber(psv_forward)
if( fw == nil ) then
fw = 0
end
end
if( psv_backward and psv_backward ~= "" ) then
bw = tonumber(psv_backward);
if( bw == nil ) then
fw = 0
end
end
return fw, bw
end
-- this is broken for left-sided driving. It needs to switch left and right in case of left-sided driving
local function getTurnLanes(way)
local fw_psv = 0
local bw_psv = 0
fw_psv, bw_psv = getPSVCounts(way)
local turn_lanes = way:get_value_by_key("turn:lanes")
local turn_lanes_fw = way:get_value_by_key("turn:lanes:forward")
local turn_lanes_bw = way:get_value_by_key("turn:lanes:backward")
if( fw_psv ~= 0 or bw_psv ~= 0 ) then
turn_lanes = trimLaneString(turn_lanes, bw_psv, fw_psv )
turn_lanes_fw = trimLaneString(turn_lanes_fw, bw_psv, fw_psv )
--backwards turn lanes need to treat bw_psv as fw_psv and vice versa
turn_lanes_bw = trimLaneString(turn_lanes_bw, fw_psv, bw_psv )
end
return turn_lanes, turn_lanes_fw, turn_lanes_bw
end
local function parse_maxspeed(source)
if not source then
return 0
@ -372,6 +422,25 @@ function way_function (way, result)
result.pronunciation = pronunciation
end
local turn_lanes = ""
local turn_lanes_forward = ""
local turn_lanes_backward = ""
turn_lanes, turn_lanes_forward, turn_lanes_backward = getTurnLanes(way)
if( turn_lanes ~= "" ) then
result.turn_lanes_forward = turn_lanes;
result.turn_lanes_backward = turn_lanes;
else
if( turn_lanes_forward ~= "" ) then
result.turn_lanes_forward = turn_lanes_forward;
end
if( turn_lanes_backward ~= "" ) then
result.turn_lanes_backward = turn_lanes_backward;
end
end
if junction and "roundabout" == junction then
result.roundabout = true
end

View File

@ -57,6 +57,7 @@ function way_function (way, result)
if name then
result.name = name
end
result.forward_mode = mode.driving
result.backward_mode = mode.driving

View File

@ -76,7 +76,7 @@ int Contractor::Run()
#ifdef WIN32
#pragma message("Memory consumption on Windows can be higher due to different bit packing")
#else
static_assert(sizeof(extractor::NodeBasedEdge) == 20,
static_assert(sizeof(extractor::NodeBasedEdge) == 24,
"changing extractor::NodeBasedEdge type has influence on memory consumption!");
static_assert(sizeof(extractor::EdgeBasedEdge) == 16,
"changing EdgeBasedEdge type has influence on memory consumption!");

View File

@ -7,6 +7,7 @@
#include "util/guidance/bearing_class.hpp"
#include "util/guidance/entry_class.hpp"
#include "util/guidance/toolkit.hpp"
#include "util/typedefs.hpp"
#include <boost/assert.hpp>
#include <boost/optional.hpp>
@ -47,7 +48,7 @@ const constexpr char *turn_type_names[] = {
"invalid", "new name", "continue", "turn", "merge",
"on ramp", "off ramp", "fork", "end of road", "notification",
"roundabout", "roundabout", "rotary", "rotary", "roundabout turn",
"roundabout turn", "invalid", "invalid", "invalid", "invalid",
"roundabout turn", "use lane", "invalid", "invalid", "invalid",
"invalid", "invalid", "invalid", "invalid", "invalid",
"invalid"};
@ -56,10 +57,13 @@ const constexpr char *waypoint_type_names[] = {"invalid", "arrive", "depart"};
// Check whether to include a modifier in the result of the API
inline bool isValidModifier(const guidance::StepManeuver maneuver)
{
if (maneuver.waypoint_type != guidance::WaypointType::None &&
maneuver.instruction.direction_modifier == DirectionModifier::UTurn)
return false;
return true;
return (maneuver.waypoint_type == guidance::WaypointType::None ||
maneuver.instruction.direction_modifier != DirectionModifier::UTurn);
}
inline bool hasValidLanes(const guidance::StepManeuver maneuver)
{
return maneuver.instruction.lane_tupel.lanes_in_turn > 0;
}
std::string instructionTypeToString(const TurnType::Enum type)
@ -67,6 +71,15 @@ std::string instructionTypeToString(const TurnType::Enum type)
return turn_type_names[static_cast<std::size_t>(type)];
}
util::json::Array laneArrayFromLaneTupe(const util::guidance::LaneTupel lane_tupel)
{
BOOST_ASSERT(lane_tupel.lanes_in_turn >= 1);
util::json::Array result;
for (LaneID i = 0; i < lane_tupel.lanes_in_turn; ++i)
result.values.push_back(lane_tupel.first_lane_from_the_right + i);
return result;
}
std::string instructionModifierToString(const DirectionModifier::Enum modifier)
{
return modifier_names[static_cast<std::size_t>(modifier)];
@ -148,6 +161,10 @@ util::json::Object makeStepManeuver(const guidance::StepManeuver &maneuver)
step_maneuver.values["modifier"] =
detail::instructionModifierToString(maneuver.instruction.direction_modifier);
if (detail::hasValidLanes(maneuver))
step_maneuver.values["lanes"] =
detail::laneArrayFromLaneTupe(maneuver.instruction.lane_tupel);
step_maneuver.values["location"] = detail::coordinateToLonLat(maneuver.location);
step_maneuver.values["bearing_before"] = std::round(maneuver.bearing_before);
step_maneuver.values["bearing_after"] = std::round(maneuver.bearing_after);

View File

@ -21,6 +21,8 @@ namespace TurnType = osrm::extractor::guidance::TurnType;
namespace DirectionModifier = osrm::extractor::guidance::DirectionModifier;
using osrm::util::guidance::angularDeviation;
using osrm::util::guidance::getTurnDirection;
using osrm::util::guidance::isLeftTurn;
using osrm::util::guidance::isRightTurn;
namespace osrm
{
@ -31,17 +33,19 @@ namespace guidance
namespace
{
const constexpr double MAX_COLLAPSE_DISTANCE = 25;
const constexpr std::size_t MIN_END_OF_ROAD_INTERSECTIONS = std::size_t{2};
const constexpr double MAX_COLLAPSE_DISTANCE = 30;
inline bool choiceless(const RouteStep &step, const RouteStep &previous)
{
// if the next turn is choiceless, we consider longer turn roads collapsable than usually
// accepted. We might need to improve this to find out whether we merge onto a through-street.
return previous.distance < 3 * MAX_COLLAPSE_DISTANCE &&
1 >= std::count(step.intersections.front().entry.begin(),
step.intersections.front().entry.end(),
true);
BOOST_ASSERT(!step.intersections.empty());
const auto is_without_choice = previous.distance < 4 * MAX_COLLAPSE_DISTANCE &&
1 >= std::count(step.intersections.front().entry.begin(),
step.intersections.front().entry.end(),
true);
return is_without_choice;
}
// List of types that can be collapsed, if all other restrictions pass
@ -62,9 +66,9 @@ bool isCollapsableInstruction(const TurnInstruction instruction)
// a possible u-turn.
bool collapsable(const RouteStep &step)
{
return step.distance < MAX_COLLAPSE_DISTANCE &&
isCollapsableInstruction(step.maneuver.instruction);
(step.maneuver.instruction.type == TurnType::UseLane ||
isCollapsableInstruction(step.maneuver.instruction));
}
bool compatible(const RouteStep &lhs, const RouteStep &rhs) { return lhs.mode == rhs.mode; }
@ -137,6 +141,8 @@ RouteStep forwardInto(RouteStep destination, const RouteStep &source)
destination.geometry_begin = std::min(destination.geometry_begin, source.geometry_begin);
destination.geometry_end = std::max(destination.geometry_end, source.geometry_end);
destination.maneuver.exit = source.maneuver.exit;
return destination;
}
@ -171,8 +177,7 @@ void fixFinalRoundabout(std::vector<RouteStep> &steps)
// instruction though. it is not contained somewhere until now
steps[propagation_index - 1] =
forwardInto(std::move(steps[propagation_index - 1]), propagation_step);
propagation_step.maneuver.instruction =
TurnInstruction::NO_TURN(); // mark intermediate instructions invalid
invalidateStep(propagation_step);
}
}
}
@ -220,10 +225,8 @@ void closeOffRoundabout(const bool on_roundabout,
const std::size_t step_index)
{
auto &step = steps[step_index];
step.maneuver.exit += 1;
if (!on_roundabout)
{
// We reached a special case that requires the addition of a special route step in the
// beginning. We started in a roundabout, so to announce the exit, we move use the exit
// instruction and move it right to the beginning to make sure to immediately announce the
@ -255,6 +258,7 @@ void closeOffRoundabout(const bool on_roundabout,
if (steps[1].maneuver.instruction.type == TurnType::EnterRotary)
steps[1].rotary_name = steps[0].name;
}
step.maneuver.exit += 1;
// Normal exit from the roundabout, or exit from a previously fixed roundabout. Propagate the
// index back to the entering location and prepare the current silent set of instructions for
@ -265,6 +269,8 @@ void closeOffRoundabout(const bool on_roundabout,
// intersections are locations passed along the way
const auto exit_intersection = steps[step_index].intersections.front();
const auto exit_bearing = exit_intersection.bearings[exit_intersection.out];
const auto destination_name = step.name;
const auto destinatino_name_id = step.name_id;
if (step_index > 1)
{
// The very first route-step is head, so we cannot iterate past that one
@ -299,22 +305,18 @@ void closeOffRoundabout(const bool on_roundabout,
::osrm::util::guidance::getTurnDirection(angle);
}
propagation_step.name = step.name;
propagation_step.name_id = step.name_id;
propagation_step.name = destination_name;
;
propagation_step.name_id = destinatino_name_id;
invalidateStep(steps[propagation_index + 1]);
break;
}
else
{
BOOST_ASSERT(propagation_step.maneuver.instruction.type ==
TurnType::StayOnRoundabout ||
propagation_step.maneuver.instruction.type == TurnType::Suppressed ||
propagation_step.maneuver.instruction.type == TurnType::NoTurn);
propagation_step.maneuver.instruction =
TurnInstruction::NO_TURN(); // mark intermediate instructions invalid
invalidateStep(steps[propagation_index + 1]);
}
}
// remove exit
step.maneuver.instruction = TurnInstruction::NO_TURN();
}
}
@ -364,9 +366,15 @@ void collapseTurnAt(std::vector<RouteStep> &steps,
const auto &one_back_step = steps[one_back_index];
// This function assumes driving on the right hand side of the streat
const auto bearingsAreReversed = [](const double bearing_in, const double bearing_out) {
// Nearly perfectly reversed angles have a difference close to 180 degrees (straight)
return angularDeviation(bearing_in, bearing_out) > 170;
const double left_turn_angle = [&]() {
if (0 <= bearing_out && bearing_out <= bearing_in)
return bearing_in - bearing_out;
return bearing_in + 360 - bearing_out;
}();
return angularDeviation(left_turn_angle, 180) <= 35;
};
BOOST_ASSERT(!one_back_step.intersections.empty() && !current_step.intersections.empty());
@ -396,7 +404,6 @@ void collapseTurnAt(std::vector<RouteStep> &steps,
DirectionModifier::Straight &&
one_back_step.intersections.front().bearings.size() > 2)
steps[step_index].maneuver.instruction.type = TurnType::Turn;
steps[two_back_index] = elongate(std::move(steps[two_back_index]), one_back_step);
// If the previous instruction asked to continue, the name change will have to
// be changed into a turn
@ -407,6 +414,7 @@ void collapseTurnAt(std::vector<RouteStep> &steps,
else if (one_back_step.distance <= MAX_COLLAPSE_DISTANCE &&
isCollapsableInstruction(current_step.maneuver.instruction))
{
// TODO check for lanes
if (compatible(one_back_step, current_step))
{
steps[one_back_index] = elongate(std::move(steps[one_back_index]), steps[step_index]);
@ -467,7 +475,8 @@ void collapseTurnAt(std::vector<RouteStep> &steps,
// additionall collapse a name-change as well
const bool continues_with_name_change =
(step_index + 1 < steps.size()) &&
isCollapsableInstruction(steps[step_index + 1].maneuver.instruction);
(steps[step_index + 1].maneuver.instruction.type == TurnType::UseLane ||
isCollapsableInstruction(steps[step_index + 1].maneuver.instruction));
const bool u_turn_with_name_change =
continues_with_name_change && steps[step_index + 1].name == steps[two_back_index].name;
@ -576,6 +585,8 @@ std::vector<RouteStep> postProcess(std::vector<RouteStep> steps)
has_entered_roundabout = false;
on_roundabout = false;
}
else if (has_entered_roundabout)
steps[step_index + 1].maneuver.exit = step.maneuver.exit;
}
// unterminated roundabout

View File

@ -1,5 +1,5 @@
#include "extractor/edge_based_graph_factory.hpp"
#include "extractor/edge_based_edge.hpp"
#include "extractor/edge_based_graph_factory.hpp"
#include "util/coordinate.hpp"
#include "util/coordinate_calculation.hpp"
#include "util/exception.hpp"
@ -11,6 +11,7 @@
#include "extractor/guidance/toolkit.hpp"
#include "extractor/guidance/turn_analysis.hpp"
#include "extractor/guidance/turn_lane_handler.hpp"
#include "extractor/suffix_table.hpp"
#include <boost/assert.hpp>
@ -39,12 +40,14 @@ EdgeBasedGraphFactory::EdgeBasedGraphFactory(
std::shared_ptr<const RestrictionMap> restriction_map,
const std::vector<QueryNode> &node_info_list,
ProfileProperties profile_properties,
const util::NameTable &name_table)
const util::NameTable &name_table,
const util::NameTable &turn_lanes)
: m_max_edge_id(0), m_node_info_list(node_info_list),
m_node_based_graph(std::move(node_based_graph)),
m_restriction_map(std::move(restriction_map)), m_barrier_nodes(barrier_nodes),
m_traffic_lights(traffic_lights), m_compressed_edge_container(compressed_edge_container),
profile_properties(std::move(profile_properties)), name_table(name_table)
profile_properties(std::move(profile_properties)), name_table(name_table),
turn_lanes(turn_lanes)
{
}
@ -339,6 +342,8 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
m_compressed_edge_container,
name_table,
street_name_suffix_table);
guidance::lanes::TurnLaneHandler turn_lane_handler(
*m_node_based_graph, turn_lanes, m_node_info_list, turn_analysis);
bearing_class_by_node_based_node.resize(m_node_based_graph->GetNumberOfNodes(),
std::numeric_limits<std::uint32_t>::max());
@ -353,19 +358,23 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
continue;
}
++node_based_edge_counter;
auto possible_turns = turn_analysis.getTurns(node_u, edge_from_u);
const NodeID node_v = m_node_based_graph->GetTarget(edge_from_u);
++node_based_edge_counter;
auto intersection = turn_analysis.getIntersection(node_u, edge_from_u);
intersection =
turn_analysis.assignTurnTypes(node_u, edge_from_u, std::move(intersection));
intersection =
turn_lane_handler.assignTurnLanes(node_u, edge_from_u, std::move(intersection));
const auto possible_turns = turn_analysis.transformIntersectionIntoTurns(intersection);
// the entry class depends on the turn, so we have to classify the interesction for
// every edge
const auto turn_classification =
classifyIntersection(node_v,
turn_analysis.getIntersection(node_u, edge_from_u),
*m_node_based_graph,
m_compressed_edge_container,
m_node_info_list);
const auto turn_classification = classifyIntersection(node_v,
intersection,
*m_node_based_graph,
m_compressed_edge_container,
m_node_info_list);
const auto entry_class_id = [&](const util::guidance::EntryClass entry_class) {
if (0 == entry_class_hash.count(entry_class))

View File

@ -51,6 +51,9 @@ ExtractionContainers::ExtractionContainers()
name_lengths.push_back(0);
name_lengths.push_back(0);
name_lengths.push_back(0);
name_lengths.push_back(0);
name_lengths.push_back(0);
turn_lane_lengths.push_back(0);
}
/**
@ -66,6 +69,7 @@ ExtractionContainers::ExtractionContainers()
void ExtractionContainers::PrepareData(const std::string &output_file_name,
const std::string &restrictions_file_name,
const std::string &name_file_name,
const std::string &turn_lane_file_name,
lua_State *segment_state)
{
try
@ -83,7 +87,8 @@ void ExtractionContainers::PrepareData(const std::string &output_file_name,
PrepareRestrictions();
WriteRestrictions(restrictions_file_name);
WriteNames(name_file_name);
WriteCharData(name_file_name,name_lengths,name_char_data);
WriteCharData(turn_lane_file_name,turn_lane_lengths,turn_lane_char_data);
}
catch (const std::exception &e)
{
@ -91,44 +96,46 @@ void ExtractionContainers::PrepareData(const std::string &output_file_name,
}
}
void ExtractionContainers::WriteNames(const std::string &names_file_name) const
void ExtractionContainers::WriteCharData(const std::string &file_name,
const stxxl::vector<unsigned> &offsets,
const stxxl::vector<char> &char_data) const
{
std::cout << "[extractor] writing street name index ... " << std::flush;
TIMER_START(write_name_index);
boost::filesystem::ofstream name_file_stream(names_file_name, std::ios::binary);
TIMER_START(write_index);
boost::filesystem::ofstream file_stream(file_name, std::ios::binary);
unsigned total_length = 0;
for (const auto name_length : name_lengths)
for (const auto length : offsets)
{
total_length += name_length;
total_length += length;
}
// builds and writes the index
util::RangeTable<> name_index_range(name_lengths);
name_file_stream << name_index_range;
util::RangeTable<> index_range(offsets);
file_stream << index_range;
name_file_stream.write((char *)&total_length, sizeof(unsigned));
file_stream.write((char *)&total_length, sizeof(unsigned));
// write all chars consecutively
char write_buffer[WRITE_BLOCK_BUFFER_SIZE];
unsigned buffer_len = 0;
for (const auto c : name_char_data)
for (const auto c : char_data)
{
write_buffer[buffer_len++] = c;
if (buffer_len >= WRITE_BLOCK_BUFFER_SIZE)
{
name_file_stream.write(write_buffer, WRITE_BLOCK_BUFFER_SIZE);
file_stream.write(write_buffer, WRITE_BLOCK_BUFFER_SIZE);
buffer_len = 0;
}
}
name_file_stream.write(write_buffer, buffer_len);
file_stream.write(write_buffer, buffer_len);
TIMER_STOP(write_name_index);
std::cout << "ok, after " << TIMER_SEC(write_name_index) << "s" << std::endl;
TIMER_STOP(write_index);
std::cout << "ok, after " << TIMER_SEC(write_index) << "s" << std::endl;
}
void ExtractionContainers::PrepareNodes()

View File

@ -239,6 +239,7 @@ int Extractor::run()
extraction_containers.PrepareData(config.output_file_name,
config.restriction_file_name,
config.names_file_name,
config.turn_lane_file_name,
main_context.state);
WriteProfileProperties(config.profile_properties_output_path, main_context.properties);
@ -503,6 +504,7 @@ Extractor::BuildEdgeExpandedGraph(lua_State *lua_state,
compressed_edge_container.SerializeInternalVector(config.geometry_output_path);
util::NameTable name_table(config.names_file_name);
util::NameTable turn_lanes(config.turn_lane_file_name);
EdgeBasedGraphFactory edge_based_graph_factory(
node_based_graph,
@ -512,7 +514,8 @@ Extractor::BuildEdgeExpandedGraph(lua_State *lua_state,
std::const_pointer_cast<RestrictionMap const>(restriction_map),
internal_to_external_node_map,
profile_properties,
name_table);
name_table,
turn_lanes);
edge_based_graph_factory.Run(config.edge_output_path,
lua_state,

View File

@ -1,13 +1,16 @@
#include "extractor/extraction_containers.hpp"
#include "extractor/extraction_node.hpp"
#include "extractor/extraction_way.hpp"
#include "extractor/extractor_callbacks.hpp"
#include "extractor/external_memory_node.hpp"
#include "extractor/restriction.hpp"
#include "util/for_each_pair.hpp"
#include "util/guidance/turn_lanes.hpp"
#include "util/simple_logger.hpp"
#include "extractor/extractor_callbacks.hpp"
#include <boost/numeric/conversion/cast.hpp>
#include <boost/optional/optional.hpp>
#include <osmium/osm.hpp>
@ -29,6 +32,7 @@ ExtractorCallbacks::ExtractorCallbacks(ExtractionContainers &extraction_containe
{
// we reserved 0, 1, 2 for the empty case
string_map[MapKey("", "")] = 0;
lane_map[""] = 0;
}
/**
@ -145,6 +149,30 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti
// Otherwise fetches the id based on the name and returns it without insertion.
const constexpr auto MAX_STRING_LENGTH = 255u;
const auto requestId = [this,MAX_STRING_LENGTH](const std::string turn_lane_string) {
if( turn_lane_string == "" )
return INVALID_LANE_STRINGID;
const auto &lane_map_iterator = lane_map.find(turn_lane_string);
if (lane_map.end() == lane_map_iterator)
{
LaneStringID turn_lane_id =
boost::numeric_cast<LaneStringID>(external_memory.turn_lane_lengths.size());
auto turn_lane_length = std::min<unsigned>(MAX_STRING_LENGTH, turn_lane_string.size());
std::copy(turn_lane_string.c_str(),
turn_lane_string.c_str() + turn_lane_length,
std::back_inserter(external_memory.turn_lane_char_data));
external_memory.turn_lane_lengths.push_back(turn_lane_length);
lane_map.insert(std::make_pair(turn_lane_string, turn_lane_id));
return turn_lane_id;
}
else
{
return lane_map_iterator->second;
}
};
const auto turn_lane_id_forward = requestId(parsed_way.turn_lanes_forward);
const auto turn_lane_id_backward = requestId(parsed_way.turn_lanes_backward);
// Get the unique identifier for the street name
// Get the unique identifier for the street name and destination
@ -191,7 +219,8 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti
(parsed_way.backward_speed > 0) &&
(TRAVEL_MODE_INACCESSIBLE != parsed_way.backward_travel_mode) &&
((parsed_way.forward_speed != parsed_way.backward_speed) ||
(parsed_way.forward_travel_mode != parsed_way.backward_travel_mode));
(parsed_way.forward_travel_mode != parsed_way.backward_travel_mode) ||
(turn_lane_id_forward != turn_lane_id_backward));
external_memory.used_node_id_list.reserve(external_memory.used_node_id_list.size() +
input_way.nodes().size());
@ -224,6 +253,7 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti
parsed_way.is_startpoint,
parsed_way.backward_travel_mode,
false,
turn_lane_id_backward,
road_classification));
});
@ -254,6 +284,7 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti
parsed_way.is_startpoint,
parsed_way.forward_travel_mode,
split_edge,
turn_lane_id_forward,
road_classification));
});
if (split_edge)
@ -275,6 +306,7 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti
parsed_way.is_startpoint,
parsed_way.backward_travel_mode,
true,
turn_lane_id_backward,
road_classification));
});
}

View File

@ -113,10 +113,8 @@ void GraphCompressor::Compress(const std::unordered_set<NodeID> &barrier_nodes,
// traffic signals in the `traffic_lights` list, which EdgeData
// doesn't have access to.
const bool has_node_penalty = traffic_lights.find(node_v) != traffic_lights.end();
if (has_node_penalty)
{
if( has_node_penalty )
continue;
}
// Get distances before graph is modified
const int forward_weight1 = graph.GetEdgeData(forward_e1).distance;
@ -139,6 +137,42 @@ void GraphCompressor::Compress(const std::unordered_set<NodeID> &barrier_nodes,
graph.SetTarget(forward_e1, node_w);
graph.SetTarget(reverse_e1, node_u);
/*
* Remember Lane Data for compressed parts. This handles scenarios where lane-data is
* only kept up until a traffic light.
*
* | |
* ---------------- |
* -^ | |
* ----------- |
* -v | |
* --------------- |
* | |
*
* u ------- v ---- w
*
* Since the edge is compressable, we can transfer:
* "left|right" (uv) and "" (uw) into a string with "left|right" (uw) for the compressed
* edge.
* Doing so, we might mess up the point from where the lanes are shown. It should be
* reasonable, since the announcements have to come early anyhow. So there is a
* potential danger in here, but it saves us from adding a lot of additional edges for
* turn-lanes. Without this,we would have to treat any turn-lane beginning/ending just
* like a barrier.
*/
const auto selectLaneID = [](const LaneStringID front, const LaneStringID back) {
// A lane has tags: u - (front) - v - (back) - w
// During contraction, we keep only one of the tags. Usually the one closer to the
// intersection is preferred. If its empty, however, we keep the non-empty one
if (back == INVALID_LANE_STRINGID)
return front;
return back;
};
graph.GetEdgeData(forward_e1).lane_string_id =
selectLaneID(graph.GetEdgeData(forward_e1).lane_string_id, fwd_edge_data2.lane_string_id);
graph.GetEdgeData(reverse_e1).lane_string_id =
selectLaneID(graph.GetEdgeData(reverse_e1).lane_string_id, rev_edge_data2.lane_string_id);
// remove e2's (if bidir, otherwise only one)
graph.DeleteEdge(node_v, forward_e2);
graph.DeleteEdge(node_v, reverse_e2);

View File

@ -1,4 +1,5 @@
#include "extractor/guidance/intersection.hpp"
#include "extractor/guidance/toolkit.hpp"
namespace osrm
{
@ -21,11 +22,34 @@ std::string toString(const ConnectedRoad &road)
result += " angle: ";
result += std::to_string(road.turn.angle);
result += " instruction: ";
result += std::to_string(static_cast<std::int32_t>(road.turn.instruction.type)) + " " +
std::to_string(static_cast<std::int32_t>(road.turn.instruction.direction_modifier));
result +=
std::to_string(static_cast<std::int32_t>(road.turn.instruction.type)) + " " +
std::to_string(static_cast<std::int32_t>(road.turn.instruction.direction_modifier)) + " " +
std::to_string(static_cast<std::int32_t>(road.turn.instruction.lane_tupel.lanes_in_turn)) +
" " + std::to_string(static_cast<std::int32_t>(
road.turn.instruction.lane_tupel.first_lane_from_the_right));
return result;
}
Intersection::iterator findClosestTurn(Intersection &intersection, const double angle)
{
return std::min_element(intersection.begin(),
intersection.end(),
[angle](const ConnectedRoad &lhs, const ConnectedRoad &rhs) {
return angularDeviation(lhs.turn.angle, angle) <
angularDeviation(rhs.turn.angle, angle);
});
}
Intersection::const_iterator findClosestTurn(const Intersection &intersection, const double angle)
{
return std::min_element(intersection.cbegin(),
intersection.cend(),
[angle](const ConnectedRoad &lhs, const ConnectedRoad &rhs) {
return angularDeviation(lhs.turn.angle, angle) <
angularDeviation(rhs.turn.angle, angle);
});
}
} // namespace guidance
} // namespace extractor
} // namespace osrm

View File

@ -1,5 +1,5 @@
#include "extractor/guidance/intersection_generator.hpp"
#include "extractor/guidance/constants.hpp"
#include "extractor/guidance/intersection_generator.hpp"
#include "extractor/guidance/toolkit.hpp"
#include <algorithm>

View File

@ -42,10 +42,8 @@ inline bool isRampClass(EdgeID eid, const util::NodeBasedDynamicGraph &node_base
MotorwayHandler::MotorwayHandler(const util::NodeBasedDynamicGraph &node_based_graph,
const std::vector<QueryNode> &node_info_list,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table,
const IntersectionGenerator &intersection_generator)
: IntersectionHandler(node_based_graph, node_info_list, name_table, street_name_suffix_table),
intersection_generator(intersection_generator)
const SuffixTable &street_name_suffix_table)
: IntersectionHandler(node_based_graph, node_info_list, name_table, street_name_suffix_table)
{
}

View File

@ -373,7 +373,13 @@ Intersection RoundaboutHandler::handleRoundabouts(const RoundaboutType roundabou
if (1 == node_based_graph.GetDirectedOutDegree(node_v))
{
// No turn possible.
turn.instruction = TurnInstruction::NO_TURN();
if( intersection.size() == 2 )
turn.instruction = TurnInstruction::NO_TURN();
else
{
turn.instruction.type = TurnType::Suppressed; //make sure to report intersection
turn.instruction.direction_modifier = getTurnDirection(turn.angle);
}
}
else
{

View File

@ -48,19 +48,15 @@ TurnAnalysis::TurnAnalysis(const util::NodeBasedDynamicGraph &node_based_graph,
compressed_edge_container,
name_table,
street_name_suffix_table),
motorway_handler(node_based_graph,
node_info_list,
name_table,
street_name_suffix_table,
intersection_generator),
motorway_handler(node_based_graph, node_info_list, name_table, street_name_suffix_table),
turn_handler(node_based_graph, node_info_list, name_table, street_name_suffix_table)
{
}
std::vector<TurnOperation> TurnAnalysis::getTurns(const NodeID from_nid, const EdgeID via_eid) const
Intersection TurnAnalysis::assignTurnTypes(const NodeID from_nid,
const EdgeID via_eid,
Intersection intersection) const
{
auto intersection = intersection_generator(from_nid, via_eid);
// Roundabouts are a main priority. If there is a roundabout instruction present, we process the
// turn as a roundabout
if (roundabout_handler.canProcess(from_nid, via_eid, intersection))
@ -81,7 +77,6 @@ std::vector<TurnOperation> TurnAnalysis::getTurns(const NodeID from_nid, const E
intersection = turn_handler(from_nid, via_eid, std::move(intersection));
}
}
// Handle sliproads
intersection = handleSliproads(via_eid, std::move(intersection));
@ -94,6 +89,12 @@ std::vector<TurnOperation> TurnAnalysis::getTurns(const NodeID from_nid, const E
});
}
return intersection;
}
std::vector<TurnOperation>
TurnAnalysis::transformIntersectionIntoTurns(const Intersection &intersection) const
{
std::vector<TurnOperation> turns;
for (auto road : intersection)
if (road.entry_allowed)
@ -234,6 +235,8 @@ Intersection TurnAnalysis::handleSliproads(const EdgeID source_edge_id,
return intersection;
}
const IntersectionGenerator &TurnAnalysis::getGenerator() const { return intersection_generator; }
} // namespace guidance
} // namespace extractor
} // namespace osrm

View File

@ -0,0 +1,85 @@
#include "extractor/guidance/constants.hpp"
#include "extractor/guidance/turn_discovery.hpp"
namespace osrm
{
namespace extractor
{
namespace guidance
{
namespace lanes
{
bool findPreviousIntersection(const NodeID node_v,
const EdgeID via_edge,
const Intersection intersection,
const TurnAnalysis &turn_analysis,
const util::NodeBasedDynamicGraph &node_based_graph,
// output parameters
NodeID &result_node,
EdgeID &result_via_edge,
Intersection &result_intersection)
{
/* We need to find the intersection that is located prior to via_edge.
*
* NODE_U -> PREVIOUS_ID -> NODE_V -> VIA_EDGE -> NODE_W:INTERSECTION
* NODE_U? <- STRAIGHTMOST <- NODE_V <- UTURN
* NODE_U? -> UTURN == PREVIOUSE_ID? -> NODE_V -> VIA_EDGE
*
* To do so, we first get the intersection atNODE and find the straightmost turn from that
* node. This will result in NODE_X. The uturn in the intersection at NODE_X should be
* PREVIOUS_ID. To verify that find, we check the intersection using our PREVIOUS_ID candidate
* to check the intersection at NODE for via_edge
*/
const constexpr double COMBINE_DISTANCE_CUTOFF = 30;
// we check if via-edge is too short. In this case the previous turn cannot influence the turn
// at via_edge and the intersection at NODE_W
if (node_based_graph.GetEdgeData(via_edge).distance > COMBINE_DISTANCE_CUTOFF)
return false;
// Node -> Via_Edge -> Intersection[0 == UTURN] -> reverse_of(via_edge) -> Intersection at node
// (looking at the reverse direction).
const auto node_w = node_based_graph.GetTarget(via_edge);
const auto u_turn_at_node_w = intersection[0].turn.eid;
const auto node_v_reverse_intersection =
turn_analysis.getIntersection(node_w, u_turn_at_node_w);
// Continue along the straightmost turn. If there is no straight turn, we cannot find a valid
// previous intersection.
const auto straightmost_at_v_in_reverse =
findClosestTurn(node_v_reverse_intersection, STRAIGHT_ANGLE);
if (angularDeviation(straightmost_at_v_in_reverse->turn.angle, STRAIGHT_ANGLE) >
FUZZY_ANGLE_DIFFERENCE)
return false;
const auto node_u = node_based_graph.GetTarget(straightmost_at_v_in_reverse->turn.eid);
const auto node_u_reverse_intersection =
turn_analysis.getIntersection(node_v, straightmost_at_v_in_reverse->turn.eid);
// now check that the u-turn at the given intersection connects to via-edge
// The u-turn at the now found intersection should, hopefully, represent the previous edge.
result_node = node_u;
result_via_edge = node_u_reverse_intersection[0].turn.eid;
// if the edge is not traversable, we obviously don't have a previous intersection or couldn't
// find it.
if (node_based_graph.GetEdgeData(result_via_edge).reversed)
return false;
result_intersection = turn_analysis.getIntersection(node_u, result_via_edge);
const auto check_via_edge = findClosestTurn(result_intersection, STRAIGHT_ANGLE)->turn.eid;
if (check_via_edge != via_edge)
return false;
result_intersection =
turn_analysis.assignTurnTypes(node_u, result_via_edge, std::move(result_intersection));
return true;
}
} // namespace lanes
} // namespace guidance
} // namespace extractor
} // namespace osrm

View File

@ -78,7 +78,8 @@ Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection
const auto &first_data = node_based_graph.GetEdgeData(intersection[1].turn.eid);
const auto &second_data = node_based_graph.GetEdgeData(intersection[2].turn.eid);
BOOST_ASSERT(intersection[0].turn.angle < 0.001);
const auto isObviousOfTwo = [this](const ConnectedRoad road, const ConnectedRoad other) {
const auto isObviousOfTwo = [this, in_data](const ConnectedRoad road,
const ConnectedRoad other) {
const auto first_class =
node_based_graph.GetEdgeData(road.turn.eid).road_classification.road_class;
@ -104,7 +105,8 @@ Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection
const bool turn_is_perfectly_straight = angularDeviation(road.turn.angle, STRAIGHT_ANGLE) <
std::numeric_limits<double>::epsilon();
if (turn_is_perfectly_straight)
if (turn_is_perfectly_straight && in_data.name_id != EMPTY_NAMEID &&
in_data.name_id == node_based_graph.GetEdgeData(road.turn.eid).name_id)
return true;
const bool is_much_narrower_than_other =

View File

@ -0,0 +1,304 @@
#include "extractor/guidance/turn_lane_augmentation.hpp"
#include "util/simple_logger.hpp"
#include <algorithm>
#include <cstddef>
#include <utility>
#include <boost/assert.hpp>
namespace osrm
{
namespace extractor
{
namespace guidance
{
namespace lanes
{
namespace
{
const constexpr char *tag_by_modifier[] = {"reverse",
"sharp_right",
"right",
"slight_right",
"through",
"slight_left",
"left",
"sharp_left"};
std::size_t getNumberOfTurns(const Intersection &intersection)
{
return std::count_if(intersection.begin(), intersection.end(), [](const ConnectedRoad &road) {
return road.entry_allowed;
});
}
LaneDataVector augmentMultiple(const std::size_t none_index,
const std::size_t connection_count,
LaneDataVector lane_data,
const Intersection &intersection)
{
// a none-turn is allowing multiple turns. we have to add a lane-data entry for
// every possible turn. This should, hopefully, only be the case for single lane
// entries?
// looking at the left side first
const auto range = [&]() {
if (none_index == 0)
{
// find first connection_count - lane_data.size() valid turns
std::size_t count = 0;
for (std::size_t intersection_index = 1; intersection_index < intersection.size();
++intersection_index)
{
count += static_cast<int>(intersection[intersection_index].entry_allowed);
if (count > connection_count - lane_data.size())
return std::make_pair(std::size_t{1}, intersection_index + 1);
}
}
else if (none_index + 1 == lane_data.size())
{
BOOST_ASSERT(!lane_data.empty());
// find last connection-count - last_data.size() valid turns
std::size_t count = 0;
for (std::size_t intersection_index = intersection.size() - 1; intersection_index > 0;
--intersection_index)
{
count += static_cast<int>(intersection[intersection_index].entry_allowed);
if (count > connection_count - lane_data.size())
return std::make_pair(intersection_index, intersection.size());
}
}
else
{
// skip the first #index valid turns, find next connection_count -
// lane_data.size() valid ones
std::size_t begin = 1, count = 0, intersection_index;
for (intersection_index = 1; intersection_index < intersection.size();
++intersection_index)
{
count += static_cast<int>(intersection[intersection_index].entry_allowed);
// if we reach the amount of
if (count >= none_index)
{
begin = intersection_index + 1;
break;
}
}
// reset count to find the number of necessary entries
count = 0;
for (intersection_index = begin; intersection_index < intersection.size();
++intersection_index)
{
count += static_cast<int>(intersection[intersection_index].entry_allowed);
if (count > connection_count - lane_data.size())
{
return std::make_pair(begin, intersection_index + 1);
}
}
}
// this should, theoretically, never be reached
util::SimpleLogger().Write(logWARNING) << "Failed lane assignment. Reached bad situation.";
return std::make_pair(std::size_t{0}, std::size_t{0});
}();
for (auto intersection_index = range.first; intersection_index < range.second;
++intersection_index)
{
if (intersection[intersection_index].entry_allowed)
{
// FIXME this probably can be only a subset of these turns here?
lane_data.push_back({tag_by_modifier[intersection[intersection_index]
.turn.instruction.direction_modifier],
lane_data[none_index].from,
lane_data[none_index].to});
}
}
lane_data.erase(lane_data.begin() + none_index);
return std::move(lane_data);
}
// Merging none-tag into its neighboring fields
// This handles situations like "left | | | right".
LaneDataVector mergeNoneTag(const std::size_t none_index, LaneDataVector lane_data)
{
if (none_index == 0 || none_index + 1 == lane_data.size())
{
if (none_index == 0)
{
lane_data[1].from = lane_data[0].from;
}
else
{
lane_data[none_index - 1].to = lane_data[none_index].to;
}
lane_data.erase(lane_data.begin() + none_index);
}
else if (lane_data[none_index].to - lane_data[none_index].from <= 1)
{
lane_data[none_index - 1].to = lane_data[none_index].from;
lane_data[none_index + 1].from = lane_data[none_index].to;
lane_data.erase(lane_data.begin() + none_index);
}
return std::move(lane_data);
}
LaneDataVector handleRenamingSituations(const std::size_t none_index,
LaneDataVector lane_data,
const Intersection &intersection)
{
bool has_right = false;
bool has_through = false;
bool has_left = false;
for (const auto &road : intersection)
{
if (!road.entry_allowed)
continue;
const auto modifier = road.turn.instruction.direction_modifier;
has_right |= modifier == DirectionModifier::Right;
has_right |= modifier == DirectionModifier::SlightRight;
has_right |= modifier == DirectionModifier::SharpRight;
has_through |= modifier == DirectionModifier::Straight;
has_left |= modifier == DirectionModifier::Left;
has_left |= modifier == DirectionModifier::SlightLeft;
has_left |= modifier == DirectionModifier::SharpLeft;
}
// find missing tag and augment neighboring, if possible
if (none_index == 0)
{
if (has_right &&
(lane_data.size() == 1 || (lane_data[none_index + 1].tag != "sharp_right" &&
lane_data[none_index + 1].tag != "right")))
{
lane_data[none_index].tag = "right";
if (lane_data.size() > 1 && lane_data[none_index + 1].tag == "through")
{
lane_data[none_index + 1].from = lane_data[none_index].from;
// turning right through a possible through lane is not possible
lane_data[none_index].to = lane_data[none_index].from;
}
}
else if (has_through &&
(lane_data.size() == 1 || lane_data[none_index + 1].tag != "through"))
{
lane_data[none_index].tag = "through";
}
}
else if (none_index + 1 == lane_data.size())
{
if (has_left && ((lane_data[none_index - 1].tag != "sharp_left" &&
lane_data[none_index - 1].tag != "left")))
{
lane_data[none_index].tag = "left";
if (lane_data[none_index - 1].tag == "through")
{
lane_data[none_index - 1].to = lane_data[none_index].to;
// turning left through a possible through lane is not possible
lane_data[none_index].from = lane_data[none_index].to;
}
}
else if (has_through && lane_data[none_index - 1].tag != "through")
{
lane_data[none_index].tag = "through";
}
}
else
{
if ((lane_data[none_index + 1].tag == "left" ||
lane_data[none_index + 1].tag == "slight_left" ||
lane_data[none_index + 1].tag == "sharp_left") &&
(lane_data[none_index - 1].tag == "right" ||
lane_data[none_index - 1].tag == "slight_right" ||
lane_data[none_index - 1].tag == "sharp_right"))
{
lane_data[none_index].tag = "through";
}
}
return std::move(lane_data);
}
} // namespace
/*
Lanes can have the tag none. While a nice feature for visibility, it is a terrible feature
for parsing. None can be part of neighboring turns, or not. We have to look at both the
intersection and the lane data to see what turns we have to augment by the none-lanes
*/
LaneDataVector handleNoneValueAtSimpleTurn(LaneDataVector lane_data,
const Intersection &intersection)
{
const bool needs_no_processing =
(intersection.empty() || lane_data.empty() || !hasTag("none", lane_data));
if (needs_no_processing)
return std::move(lane_data);
// FIXME all this needs to consider the number of lanes at the target to ensure that we
// augment lanes correctly, if the target lane allows for more turns
//
// -----------------
//
// ----- ----
// -v |
// ----- |
// | | |
//
// A situation like this would allow a right turn from the through lane.
//
// -----------------
//
// ----- --------
// -v |
// ----- |
// | |
//
// Here, the number of lanes in the right road would not allow turns from both lanes, but
// only from the right one.
const std::size_t connection_count =
getNumberOfTurns(intersection) -
((intersection[0].entry_allowed && lane_data.back().tag != "reverse") ? 1 : 0);
// TODO check for impossible turns to see whether the turn lane is at the correct place
const std::size_t none_index = std::distance(lane_data.begin(), findTag("none", lane_data));
BOOST_ASSERT(none_index != lane_data.size());
// we have to create multiple turns
if (connection_count > lane_data.size())
{
lane_data =
augmentMultiple(none_index, connection_count, std::move(lane_data), intersection);
}
// we have to reduce it, assigning it to neighboring turns
else if (connection_count < lane_data.size())
{
// a prerequisite is simple turns. Larger differences should not end up here
// an additional line at the side is only reasonable if it is targeting public
// service vehicles. Otherwise, we should not have it
BOOST_ASSERT(connection_count + 1 == lane_data.size());
lane_data = mergeNoneTag(none_index, std::move(lane_data));
}
// we have to rename and possibly augment existing ones. The pure count remains the
// same.
else
{
lane_data = handleRenamingSituations(none_index, std::move(lane_data), intersection);
}
// finally make sure we are still sorted
std::sort(lane_data.begin(), lane_data.end());
return std::move(lane_data);
}
} // namespace lanes
} // namespace guidance
} // namespace extractor
} // namespace osrm

View File

@ -0,0 +1,145 @@
#include "extractor/guidance/turn_lane_data.hpp"
#include "util/guidance/turn_lanes.hpp"
#include <boost/numeric/conversion/cast.hpp>
#include <algorithm>
#include <cstddef>
#include <string>
#include <unordered_map>
#include <utility>
namespace osrm
{
namespace extractor
{
namespace guidance
{
namespace lanes
{
bool TurnLaneData::operator<(const TurnLaneData &other) const
{
if (from < other.from)
return true;
if (from > other.from)
return false;
if (to < other.to)
return true;
if (to > other.to)
return false;
const constexpr char *tag_by_modifier[] = {"sharp_right",
"right",
"slight_right",
"through",
"slight_left",
"left",
"sharp_left",
"reverse"};
return std::find(tag_by_modifier, tag_by_modifier + 8, this->tag) <
std::find(tag_by_modifier, tag_by_modifier + 8, other.tag);
}
LaneDataVector laneDataFromString(std::string turn_lane_string)
{
typedef std::unordered_map<std::string, std::pair<LaneID, LaneID>> LaneMap;
// FIXME this is a workaround due to https://github.com/cucumber/cucumber-js/issues/417,
// need to switch statements when fixed
// const auto num_lanes = std::count(turn_lane_string.begin(), turn_lane_string.end(), '|') + 1;
// count the number of lanes
const auto num_lanes = [](const std::string &turn_lane_string) {
return boost::numeric_cast<LaneID>(
std::count(turn_lane_string.begin(), turn_lane_string.end(), '|') + 1 +
std::count(turn_lane_string.begin(), turn_lane_string.end(), '&'));
}(turn_lane_string);
const auto getNextTag = [](std::string &string, const char *separators) {
auto pos = string.find_last_of(separators);
auto result = pos != std::string::npos ? string.substr(pos + 1) : string;
string.resize(pos == std::string::npos ? 0 : pos);
return result;
};
const auto setLaneData = [&](LaneMap &map, std::string lane, const LaneID current_lane) {
do
{
auto identifier = getNextTag(lane, ";");
if (identifier.empty())
identifier = "none";
auto map_iterator = map.find(identifier);
if (map_iterator == map.end())
map[identifier] = std::make_pair(current_lane, current_lane);
else
{
map_iterator->second.second = current_lane;
}
} while (!lane.empty());
};
// check whether a given turn lane string resulted in valid lane data
const auto hasValidOverlaps = [](const LaneDataVector &lane_data) {
// Allow an overlap of at most one. Larger overlaps would result in crossing another turn,
// which is invalid
for (std::size_t index = 1; index < lane_data.size(); ++index)
{
if (lane_data[index - 1].to > lane_data[index].from)
return false;
}
return true;
};
LaneMap lane_map;
LaneID lane_nr = 0;
LaneDataVector lane_data;
if (turn_lane_string.empty())
return lane_data;
do
{
// FIXME this is a cucumber workaround, since escaping does not work properly in
// cucumber.js (see https://github.com/cucumber/cucumber-js/issues/417). Needs to be
// changed to "|" only, when the bug is fixed
auto lane = getNextTag(turn_lane_string, "|&");
setLaneData(lane_map, lane, lane_nr);
++lane_nr;
} while (lane_nr < num_lanes);
for (const auto tag : lane_map)
{
lane_data.push_back({tag.first, tag.second.first, tag.second.second});
}
std::sort(lane_data.begin(), lane_data.end());
if (!hasValidOverlaps(lane_data))
{
lane_data.clear();
}
return lane_data;
}
LaneDataVector::iterator findTag(const std::string &tag, LaneDataVector &data)
{
return std::find_if(data.begin(), data.end(), [&](const TurnLaneData &lane_data) {
return tag == lane_data.tag;
});
}
LaneDataVector::const_iterator findTag(const std::string &tag, const LaneDataVector &data)
{
return std::find_if(data.cbegin(), data.cend(), [&](const TurnLaneData &lane_data) {
return tag == lane_data.tag;
});
}
bool hasTag(const std::string &tag, const LaneDataVector &data){
return findTag(tag,data) != data.cend();
}
} // namespace lanes
} // namespace guidance
} // namespace extractor
} // namespace osrm

View File

@ -0,0 +1,505 @@
#include "extractor/guidance/constants.hpp"
#include "extractor/guidance/turn_discovery.hpp"
#include "extractor/guidance/turn_lane_handler.hpp"
#include "extractor/guidance/turn_lane_matcher.hpp"
#include "extractor/guidance/turn_lane_augmentation.hpp"
#include "util/simple_logger.hpp"
#include "util/typedefs.hpp"
#include <cstdint>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/numeric/conversion/cast.hpp>
namespace osrm
{
namespace extractor
{
namespace guidance
{
namespace lanes
{
namespace
{
std::size_t getNumberOfTurns(const Intersection &intersection)
{
return std::count_if(intersection.begin(), intersection.end(), [](const ConnectedRoad &road) {
return road.entry_allowed;
});
}
} // namespace
TurnLaneHandler::TurnLaneHandler(const util::NodeBasedDynamicGraph &node_based_graph,
const util::NameTable &turn_lane_strings,
const std::vector<QueryNode> &node_info_list,
const TurnAnalysis &turn_analysis)
: node_based_graph(node_based_graph), turn_lane_strings(turn_lane_strings),
node_info_list(node_info_list), turn_analysis(turn_analysis)
{
}
/*
Turn lanes are given in the form of strings that closely correspond to the direction modifiers
we use for our turn types. However, we still cannot simply perform a 1:1 assignment.
This function parses the turn_lane_strings of a format that describes an intersection as:
----------
A -^
----------
B -> -v
----------
C -v
----------
with a string like |left|through;right|right| and performs an assignment onto the turns:
for example: (130, turn slight right), (180, ramp straight), (320, turn sharp left)
*/
Intersection TurnLaneHandler::assignTurnLanes(const NodeID at,
const EdgeID via_edge,
Intersection intersection) const
{
// initialize to invalid
for (auto &road : intersection)
road.turn.instruction.lane_tupel = {0, INVALID_LANEID};
const auto &data = node_based_graph.GetEdgeData(via_edge);
const auto turn_lane_string = data.lane_string_id != INVALID_LANE_STRINGID
? turn_lane_strings.GetNameForID(data.lane_string_id)
: "";
// going straight, due to traffic signals, we can have uncompressed geometry
if (intersection.size() == 2 &&
((data.lane_string_id != INVALID_LANE_STRINGID &&
data.lane_string_id ==
node_based_graph.GetEdgeData(intersection[1].turn.eid).lane_string_id) ||
angularDeviation(intersection[1].turn.angle, STRAIGHT_ANGLE) < FUZZY_ANGLE_DIFFERENCE))
return std::move(intersection);
auto lane_data = laneDataFromString(turn_lane_string);
// if we see an invalid conversion, we stop immediately
if (!turn_lane_string.empty() && lane_data.empty())
return std::move(intersection);
// might be reasonable to handle multiple turns, if we know of a sequence of lanes
// e.g. one direction per lane, if three lanes and right, through, left available
if (!turn_lane_string.empty() && lane_data.size() == 1 && lane_data[0].tag == "none")
return std::move(intersection);
const std::size_t possible_entries = getNumberOfTurns(intersection);
// merge does not justify an instruction
const bool has_merge_lane = (hasTag("merge_to_left", lane_data) ||
hasTag("merge_to_right", lane_data));
// Dead end streets that don't have any left-tag. This can happen due to the fallbacks for
// broken data/barriers.
const bool has_non_usable_u_turn =
(intersection[0].entry_allowed && !hasTag("none", lane_data) &&
!hasTag("left", lane_data) &&
!hasTag("sharp_left", lane_data) &&
!hasTag("reverse", lane_data) &&
lane_data.size() + 1 == possible_entries);
if (has_merge_lane || has_non_usable_u_turn)
return std::move(intersection);
if (!lane_data.empty() && canMatchTrivially(intersection, lane_data) &&
lane_data.size() !=
static_cast<std::size_t>(
lane_data.back().tag != "reverse" && intersection[0].entry_allowed ? 1 : 0) +
possible_entries &&
intersection[0].entry_allowed && !hasTag("none", lane_data))
lane_data.push_back({"reverse", lane_data.back().to, lane_data.back().to});
bool is_simple = isSimpleIntersection(lane_data, intersection);
// simple intersections can be assigned directly
if (is_simple)
{
lane_data = handleNoneValueAtSimpleTurn(std::move(lane_data), intersection);
return simpleMatchTuplesToTurns(std::move(intersection), lane_data);
}
// if the intersection is not simple but we have lane data, we check for intersections with
// middle islands. We have two cases. The first one is providing lane data on the current
// segment and we only need to consider the part of the current segment. In this case we
// partition the data and only consider the first part.
else if (!lane_data.empty())
{
if (lane_data.size() >= possible_entries)
{
lane_data = partitionLaneData(node_based_graph.GetTarget(via_edge),
std::move(lane_data),
intersection)
.first;
// check if we were successfull in trimming
if (lane_data.size() == possible_entries &&
isSimpleIntersection(lane_data, intersection))
{
lane_data = handleNoneValueAtSimpleTurn(std::move(lane_data), intersection);
return simpleMatchTuplesToTurns(std::move(intersection), lane_data);
}
}
}
// The second part does not provide lane data on the current segment, but on the segment prior
// to the turn. We try to partition the data and only consider the second part.
else if (turn_lane_string.empty())
{
// acquire the lane data of a previous segment and, if possible, use it for the current
// intersection.
return handleTurnAtPreviousIntersection(at, via_edge, std::move(intersection));
}
return std::move(intersection);
}
// At segregated intersections, turn lanes will often only be specified up until the first turn. To
// actually take the turn, we need to look back to the edge we drove onto the intersection with.
Intersection TurnLaneHandler::handleTurnAtPreviousIntersection(const NodeID at,
const EdgeID via_edge,
Intersection intersection) const
{
NodeID previous_node = SPECIAL_NODEID;
Intersection previous_intersection;
EdgeID previous_id = SPECIAL_EDGEID;
// Get the previous lane string. We only accept strings that stem from a not-simple intersection
// and are not empty.
const auto previous_lane_string = [&]() -> std::string {
if (!findPreviousIntersection(at,
via_edge,
intersection,
turn_analysis,
node_based_graph,
previous_node,
previous_id,
previous_intersection))
return "";
const auto &previous_data = node_based_graph.GetEdgeData(previous_id);
auto previous_string = previous_data.lane_string_id != INVALID_LANE_STRINGID
? turn_lane_strings.GetNameForID(previous_data.lane_string_id)
: "";
if (previous_string.empty())
return "";
previous_intersection = turn_analysis.assignTurnTypes(
previous_node, previous_id, std::move(previous_intersection));
auto previous_lane_data = laneDataFromString(previous_string);
if (isSimpleIntersection(previous_lane_data, previous_intersection))
return "";
return previous_string;
}();
// no lane string, no problems
if (previous_lane_string.empty())
return std::move(intersection);
auto lane_data = laneDataFromString(previous_lane_string);
// stop on invalid lane data conversion
if (lane_data.empty())
return std::move(intersection);
const auto is_simple = isSimpleIntersection(lane_data, intersection);
if (is_simple)
{
lane_data = handleNoneValueAtSimpleTurn(std::move(lane_data), intersection);
return simpleMatchTuplesToTurns(std::move(intersection), lane_data);
}
else
{
if (lane_data.size() >= getNumberOfTurns(previous_intersection) &&
previous_intersection.size() != 2)
{
lane_data = partitionLaneData(node_based_graph.GetTarget(previous_id),
std::move(lane_data),
previous_intersection)
.second;
std::sort(lane_data.begin(), lane_data.end());
// check if we were successfull in trimming
if (lane_data.size() == getNumberOfTurns(intersection) &&
isSimpleIntersection(lane_data, intersection))
{
lane_data = handleNoneValueAtSimpleTurn(std::move(lane_data), intersection);
return simpleMatchTuplesToTurns(std::move(intersection), lane_data);
}
}
}
return std::move(intersection);
}
/* A simple intersection does not depend on the next intersection coming up. This is important
* for turn lanes, since traffic signals and/or segregated a intersection can influence the
* interpretation of turn-lanes at a given turn.
*
* Here we check for a simple intersection. A simple intersection has a long enough segment
* followin the turn, offers no straight turn, or only non-trivial turn operations.
*/
bool TurnLaneHandler::isSimpleIntersection(const LaneDataVector &lane_data,
const Intersection &intersection) const
{
if (lane_data.empty())
return false;
// if we are on a straight road, turn lanes are only reasonable in connection to the next
// intersection, or in case of a merge. If not all but one (straight) are merges, we don't
// consider the intersection simple
if (intersection.size() == 2)
{
return std::count_if(
lane_data.begin(),
lane_data.end(),
[](const TurnLaneData &data) { return boost::starts_with(data.tag, "merge"); }) +
std::size_t{1} >=
lane_data.size();
}
// in case an intersection offers far more lane data items than actual turns, some of them
// have
// to be for another intersection. A single additional item can be for an invalid bus lane.
const auto num_turns = [&]() {
auto count = getNumberOfTurns(intersection);
if (count < lane_data.size() && !intersection[0].entry_allowed &&
lane_data.back().tag == "reverse")
return count + 1;
return count;
}();
// more than two additional lane data entries -> lanes target a different intersection
if (num_turns + std::size_t{2} <= lane_data.size())
{
return false;
}
// single additional lane data entry is alright, if it is none at the side. This usually
// refers to a bus-lane
if (num_turns + std::size_t{1} == lane_data.size() && lane_data.front().tag != "none" &&
lane_data.back().tag != "none")
{
return false;
}
// more turns than lane data
if (num_turns > lane_data.size() &&
lane_data.end() ==
std::find_if(lane_data.begin(), lane_data.end(), [](const TurnLaneData &data) {
return data.tag == "none";
}))
{
return false;
}
if (num_turns > lane_data.size() && intersection[0].entry_allowed &&
!( hasTag("reverse", lane_data) ||
(lane_data.back().tag != "left" && lane_data.back().tag != "sharp_left")))
{
return false;
}
// check if we can find a valid 1:1 mapping in a straightforward manner
bool all_simple = true;
bool has_none = false;
std::unordered_set<std::size_t> matched_indices;
for (const auto &data : lane_data)
{
if (data.tag == "none")
{
has_none = true;
continue;
}
const auto best_match = [&]() {
if (data.tag != "reverse" || lane_data.size() == 1)
return findBestMatch(data.tag, intersection);
// lane_data.size() > 1
if (lane_data.back().tag == "reverse")
return findBestMatchForReverse(lane_data[lane_data.size() - 2].tag, intersection);
BOOST_ASSERT(lane_data.front().tag == "reverse");
return findBestMatchForReverse(lane_data[1].tag, intersection);
}();
std::size_t match_index = std::distance(intersection.begin(), best_match);
all_simple &= (matched_indices.count(match_index) == 0);
matched_indices.insert(match_index);
// in case of u-turns, we might need to activate them first
all_simple &= (best_match->entry_allowed || match_index == 0);
all_simple &= isValidMatch(data.tag, best_match->turn.instruction);
}
// either all indices are matched, or we have a single none-value
if (all_simple && (matched_indices.size() == lane_data.size() ||
(matched_indices.size() + 1 == lane_data.size() && has_none)))
return true;
// better save than sorry
return false;
}
std::pair<LaneDataVector, LaneDataVector> TurnLaneHandler::partitionLaneData(
const NodeID at, LaneDataVector turn_lane_data, const Intersection &intersection) const
{
BOOST_ASSERT(turn_lane_data.size() >= getNumberOfTurns(intersection));
/*
* A Segregated intersection can provide turn lanes for turns that are not yet possible.
* The straightforward example would be coming up to the following situation:
* (1) (2)
* | A | | A |
* | | | | ^ |
* | v | | | |
* ------- ----------- ------
* B ->-^ B
* ------- ----------- ------
* B ->-v B
* ------- ----------- ------
* | A | | A |
*
* Traveling on road B, we have to pass A at (1) to turn left onto A at (2). The turn
* lane itself may only be specified prior to (1) and/or could be repeated between (1)
* and (2). To make sure to announce the lane correctly, we need to treat the (in this
* case left) turn lane as if it were to continue straight onto the intersection and
* look back between (1) and (2) to make sure we find the correct lane for the left-turn.
*
* Intersections like these have two parts. Turns that can be made at the first intersection and
* turns that have to be made at the second. The partitioning returns the lane data split into
* two parts, one for the first and one for the second intersection.
*/
// Try and maitch lanes to available turns. For Turns that are not directly matchable, check
// whether we can match them at the upcoming intersection.
const auto straightmost = findClosestTurn(intersection, STRAIGHT_ANGLE);
BOOST_ASSERT(straightmost < intersection.cend());
// we need to be able to enter the straightmost turn
if (!straightmost->entry_allowed)
return {turn_lane_data, {}};
std::vector<bool> matched_at_first(turn_lane_data.size(), false);
std::vector<bool> matched_at_second(turn_lane_data.size(), false);
// find out about the next intersection. To check for valid matches, we also need the turn types
auto next_intersection = turn_analysis.getIntersection(at, straightmost->turn.eid);
next_intersection =
turn_analysis.assignTurnTypes(at, straightmost->turn.eid, std::move(next_intersection));
// check where we can match turn lanes
std::size_t straightmost_tag_index = turn_lane_data.size();
for (std::size_t lane = 0; lane < turn_lane_data.size(); ++lane)
{
if (turn_lane_data[lane].tag == "none" || turn_lane_data[lane].tag == "reverse")
continue;
const auto best_match = findBestMatch(turn_lane_data[lane].tag, intersection);
if (isValidMatch(turn_lane_data[lane].tag, best_match->turn.instruction))
{
matched_at_first[lane] = true;
if (straightmost == best_match)
straightmost_tag_index = lane;
}
const auto best_match_at_next_intersection =
findBestMatch(turn_lane_data[lane].tag, next_intersection);
if (isValidMatch(turn_lane_data[lane].tag,
best_match_at_next_intersection->turn.instruction))
matched_at_second[lane] = true;
// we need to match all items to either the current or the next intersection
if (!(matched_at_first[lane] || matched_at_second[lane]))
return {turn_lane_data, {}};
}
std::size_t none_index = std::distance(turn_lane_data.begin(),findTag("none", turn_lane_data));
// if the turn lanes are pull forward, we might have to add an additional straight tag
// did we find something that matches against the straightmost road?
if (straightmost_tag_index == turn_lane_data.size())
{
if (none_index != turn_lane_data.size())
straightmost_tag_index = none_index;
}
// TODO handle reverse
// handle none values
if (none_index != turn_lane_data.size())
{
if (static_cast<std::size_t>(
std::count(matched_at_first.begin(), matched_at_first.end(), true)) <=
getNumberOfTurns(intersection))
matched_at_first[none_index] = true;
if (static_cast<std::size_t>(
std::count(matched_at_second.begin(), matched_at_second.end(), true)) <=
getNumberOfTurns(next_intersection))
matched_at_second[none_index] = true;
}
const auto augmentEntry = [&](TurnLaneData &data) {
for (std::size_t lane = 0; lane < turn_lane_data.size(); ++lane)
if (matched_at_second[lane])
{
data.from = std::min(turn_lane_data[lane].from, data.from);
data.to = std::max(turn_lane_data[lane].to, data.to);
}
};
LaneDataVector first, second;
for (std::size_t lane = 0; lane < turn_lane_data.size(); ++lane)
{
if (matched_at_second[lane])
second.push_back(turn_lane_data[lane]);
// augment straightmost at this intersection to match all turns that happen at the next
if (lane == straightmost_tag_index)
{
augmentEntry(turn_lane_data[straightmost_tag_index]);
}
if (matched_at_first[lane])
first.push_back(turn_lane_data[lane]);
}
if (straightmost_tag_index == turn_lane_data.size() &&
static_cast<std::size_t>(
std::count(matched_at_second.begin(), matched_at_second.end(), true)) ==
getNumberOfTurns(next_intersection))
{
TurnLaneData data = {"through", 255, 0};
augmentEntry(data);
first.push_back(data);
std::sort(first.begin(), first.end());
}
// TODO augment straightmost turn
return {std::move(first), std::move(second)};
}
Intersection TurnLaneHandler::simpleMatchTuplesToTurns(Intersection intersection,
const LaneDataVector &lane_data) const
{
if (lane_data.empty() || !canMatchTrivially(intersection, lane_data))
return std::move(intersection);
BOOST_ASSERT(!hasTag("none", lane_data));
BOOST_ASSERT(std::count_if(lane_data.begin(), lane_data.end(), [](const TurnLaneData &data) {
return boost::starts_with(data.tag, "merge");
}) == 0);
return triviallyMatchLanesToTurns(std::move(intersection), lane_data, node_based_graph);
}
} // namespace lanes
} // namespace guidance
} // namespace extractor
} // namespace osrm

View File

@ -0,0 +1,221 @@
#include "extractor/guidance/toolkit.hpp"
#include "extractor/guidance/turn_lane_matcher.hpp"
#include "util/guidance/toolkit.hpp"
#include <boost/assert.hpp>
namespace osrm
{
namespace extractor
{
namespace guidance
{
namespace lanes
{
// Translate Turn Tags into a Matching Direction Modifier
DirectionModifier::Enum getMatchingModifier(const std::string &tag)
{
const constexpr char *tag_by_modifier[] = {"reverse",
"sharp_right",
"right",
"slight_right",
"through",
"slight_left",
"left",
"sharp_left",
"merge_to_left",
"merge_to_right"};
const auto index =
std::distance(tag_by_modifier, std::find(tag_by_modifier, tag_by_modifier + 10, tag));
BOOST_ASSERT(index <= 10);
const constexpr DirectionModifier::Enum modifiers[11] = {
DirectionModifier::UTurn,
DirectionModifier::SharpRight,
DirectionModifier::Right,
DirectionModifier::SlightRight,
DirectionModifier::Straight,
DirectionModifier::SlightLeft,
DirectionModifier::Left,
DirectionModifier::SharpLeft,
DirectionModifier::Straight,
DirectionModifier::Straight,
DirectionModifier::UTurn}; // fallback for invalid tags
return modifiers[index];
}
// check whether a match of a given tag and a turn instruction can be seen as valid
bool isValidMatch(const std::string &tag, const TurnInstruction instruction)
{
using util::guidance::hasLeftModifier;
using util::guidance::hasRightModifier;
const auto isMirroredModifier = [](const TurnInstruction instruction) {
return instruction.type == TurnType::Merge;
};
if (tag == "reverse")
{
return hasLeftModifier(instruction) ||
instruction.direction_modifier == DirectionModifier::UTurn;
}
else if (tag == "sharp_right" || tag == "right" || tag == "slight_right")
{
if (isMirroredModifier(instruction))
return hasLeftModifier(instruction);
else
// needs to be adjusted for left side driving
return leavesRoundabout(instruction) || hasRightModifier(instruction);
}
else if (tag == "through")
{
return instruction.direction_modifier == DirectionModifier::Straight ||
instruction.type == TurnType::Suppressed || instruction.type == TurnType::NewName ||
instruction.type == TurnType::StayOnRoundabout || entersRoundabout(instruction) ||
(instruction.type ==
TurnType::Fork && // Forks can be experienced, even for straight segments
(instruction.direction_modifier == DirectionModifier::SlightLeft ||
instruction.direction_modifier == DirectionModifier::SlightRight)) ||
(instruction.type ==
TurnType::Continue && // Forks can be experienced, even for straight segments
(instruction.direction_modifier == DirectionModifier::SlightLeft ||
instruction.direction_modifier == DirectionModifier::SlightRight)) ||
instruction.type == TurnType::UseLane;
}
else if (tag == "slight_left" || tag == "left" || tag == "sharp_left")
{
if (isMirroredModifier(instruction))
return hasRightModifier(instruction);
else
{
// Needs to be fixed for left side driving
return (instruction.type == TurnType::StayOnRoundabout) || hasLeftModifier(instruction);
}
}
return false;
}
typename Intersection::const_iterator findBestMatch(const std::string &tag,
const Intersection &intersection)
{
const constexpr double idealized_turn_angles[] = {0, 35, 90, 135, 180, 225, 270, 315};
const auto idealized_angle = idealized_turn_angles[getMatchingModifier(tag)];
return std::min_element(
intersection.begin(),
intersection.end(),
[idealized_angle, &tag](const ConnectedRoad &lhs, const ConnectedRoad &rhs) {
// prefer valid matches
if (isValidMatch(tag, lhs.turn.instruction) != isValidMatch(tag, rhs.turn.instruction))
return isValidMatch(tag, lhs.turn.instruction);
// if the entry allowed flags don't match, we select the one with
// entry allowed set to true
if (lhs.entry_allowed != rhs.entry_allowed)
return lhs.entry_allowed;
return angularDeviation(idealized_angle, lhs.turn.angle) <
angularDeviation(idealized_angle, rhs.turn.angle);
});
}
typename Intersection::const_iterator findBestMatchForReverse(const std::string &leftmost_tag,
const Intersection &intersection)
{
const auto leftmost_itr = findBestMatch(leftmost_tag, intersection);
if (leftmost_itr + 1 == intersection.cend())
return intersection.begin();
const constexpr double idealized_turn_angles[] = {0, 35, 90, 135, 180, 225, 270, 315};
const std::string tag = "reverse";
const auto idealized_angle = idealized_turn_angles[getMatchingModifier(tag)];
return std::min_element(
intersection.begin() + std::distance(intersection.begin(), leftmost_itr),
intersection.end(),
[idealized_angle, &tag](const ConnectedRoad &lhs, const ConnectedRoad &rhs) {
// prefer valid matches
if (isValidMatch(tag, lhs.turn.instruction) != isValidMatch(tag, rhs.turn.instruction))
return isValidMatch(tag, lhs.turn.instruction);
// if the entry allowed flags don't match, we select the one with
// entry allowed set to true
if (lhs.entry_allowed != rhs.entry_allowed)
return lhs.entry_allowed;
return angularDeviation(idealized_angle, lhs.turn.angle) <
angularDeviation(idealized_angle, rhs.turn.angle);
});
}
bool canMatchTrivially(const Intersection &intersection, const LaneDataVector &lane_data)
{
std::size_t road_index = 1, lane = 0;
for (; road_index < intersection.size() && lane < lane_data.size(); ++road_index)
{
if (intersection[road_index].entry_allowed)
{
BOOST_ASSERT(lane_data[lane].from != INVALID_LANEID);
if (!isValidMatch(lane_data[lane].tag, intersection[road_index].turn.instruction))
return false;
if (findBestMatch(lane_data[lane].tag, intersection) !=
intersection.begin() + road_index)
return false;
++lane;
}
}
return lane == lane_data.size() ||
(lane + 1 == lane_data.size() && lane_data.back().tag == "reverse");
}
Intersection triviallyMatchLanesToTurns(Intersection intersection,
const LaneDataVector &lane_data,
const util::NodeBasedDynamicGraph &node_based_graph)
{
std::size_t road_index = 1, lane = 0;
for (; road_index < intersection.size() && lane < lane_data.size(); ++road_index)
{
if (intersection[road_index].entry_allowed)
{
BOOST_ASSERT(lane_data[lane].from != INVALID_LANEID);
BOOST_ASSERT(
isValidMatch(lane_data[lane].tag, intersection[road_index].turn.instruction));
BOOST_ASSERT(findBestMatch(lane_data[lane].tag, intersection) ==
intersection.begin() + road_index);
if (TurnType::Suppressed == intersection[road_index].turn.instruction.type)
intersection[road_index].turn.instruction.type = TurnType::UseLane;
intersection[road_index].turn.instruction.lane_tupel = {
LaneID(lane_data[lane].to - lane_data[lane].from + 1), lane_data[lane].from};
++lane;
}
}
// handle reverse tag, if present
if (lane + 1 == lane_data.size() && lane_data.back().tag == "reverse")
{
std::size_t u_turn = 0;
if (node_based_graph.GetEdgeData(intersection[0].turn.eid).reversed)
{
if (intersection.back().entry_allowed ||
intersection.back().turn.instruction.direction_modifier !=
DirectionModifier::SharpLeft)
{
// cannot match u-turn in a valid way
return std::move(intersection);
}
u_turn = intersection.size() - 1;
}
intersection[u_turn].entry_allowed = true;
intersection[u_turn].turn.instruction.type = TurnType::Turn;
intersection[u_turn].turn.instruction.direction_modifier = DirectionModifier::UTurn;
intersection[u_turn].turn.instruction.lane_tupel = {
LaneID(lane_data.back().to - lane_data.back().from + 1), lane_data.back().from};
}
return std::move(intersection);
}
} // namespace lane_matching
} // namespace guidance
} // namespace extractor
} // namespace osrm

View File

@ -77,6 +77,7 @@ void ScriptingEnvironment::InitContext(ScriptingEnvironment::Context &context)
luabind::module(context.state)
[luabind::def("durationIsValid", durationIsValid),
luabind::def("parseDuration", parseDuration),
luabind::def("trimLaneString", trimLaneString),
luabind::class_<TravelMode>("mode").enum_(
"enums")[luabind::value("inaccessible", TRAVEL_MODE_INACCESSIBLE),
luabind::value("driving", TRAVEL_MODE_DRIVING),
@ -141,6 +142,8 @@ void ScriptingEnvironment::InitContext(ScriptingEnvironment::Context &context)
.def_readwrite("is_access_restricted", &ExtractionWay::is_access_restricted)
.def_readwrite("is_startpoint", &ExtractionWay::is_startpoint)
.def_readwrite("duration", &ExtractionWay::duration)
.def_readwrite("turn_lanes_forward", &ExtractionWay::turn_lanes_forward)
.def_readwrite("turn_lanes_backward", &ExtractionWay::turn_lanes_backward)
.property(
"forward_mode", &ExtractionWay::get_forward_mode, &ExtractionWay::set_forward_mode)
.property("backward_mode",

View File

@ -0,0 +1,47 @@
#include "util/guidance/turn_lanes.hpp"
#include <algorithm>
#include <iostream>
#include <boost/assert.hpp>
namespace osrm
{
namespace util
{
namespace guidance
{
LaneTupel::LaneTupel()
: lanes_in_turn(0), first_lane_from_the_right(INVALID_LANEID)
{
// basic constructor, set everything to zero
}
LaneTupel::LaneTupel(const LaneID lanes_in_turn, const LaneID first_lane_from_the_right)
: lanes_in_turn(lanes_in_turn), first_lane_from_the_right(first_lane_from_the_right)
{
}
// comparation based on interpretation as unsigned 32bit integer
bool LaneTupel::operator==(const LaneTupel other) const
{
static_assert(sizeof(LaneTupel) == sizeof(std::uint16_t),
"Comparation requires LaneTupel to be the of size 16Bit");
return *reinterpret_cast<const std::uint16_t *>(this) ==
*reinterpret_cast<const std::uint16_t *>(&other);
}
bool LaneTupel::operator!=(const LaneTupel other) const { return !(*this == other); }
// comparation based on interpretation as unsigned 32bit integer
bool LaneTupel::operator<(const LaneTupel other) const
{
static_assert(sizeof(LaneTupel) == sizeof(std::uint16_t),
"Comparation requires LaneTupel to be the of size 16Bit");
return *reinterpret_cast<const std::uint16_t *>(this) <
*reinterpret_cast<const std::uint16_t *>(&other);
}
} // namespace guidance
} // namespace util
} // namespace osrm

View File

@ -36,11 +36,11 @@ NameTable::NameTable(const std::string &filename)
}
else
{
util::SimpleLogger().Write(logWARNING) << "list of street names is empty";
util::SimpleLogger().Write(logINFO) << "list of street names is empty in construction of name table from: \"" << filename << "\"";
}
if (!name_stream)
throw exception("Failed to read " + std::to_string(number_of_chars) +
" characters from file.");
" characters from " + filename);
}
std::string NameTable::GetNameForID(const unsigned name_id) const

View File

@ -200,6 +200,36 @@
"object_types": [ "way" ],
"description": "Name of road for navigation instructions."
},
{
"key": "turn:lanes",
"object_types": [ "way" ],
"description": "Turn Lanes for lane guidance."
},
{
"key": "turn:lanes:forward",
"object_types": [ "way" ],
"description": "Turn Lanes for lane guidance."
},
{
"key": "turn:lanes:backward",
"object_types": [ "way" ],
"description": "Turn Lanes for lane guidance."
},
{
"key": "lanes:psv",
"object_types": [ "way" ],
"description": "Turn Lanes for lane guidance."
},
{
"key": "lanes:psv:forward",
"object_types": [ "way" ],
"description": "Turn Lanes for lane guidance."
},
{
"key": "lanes:psv:backward",
"object_types": [ "way" ],
"description": "Turn Lanes for lane guidance."
},
{
"key": "ref",
"object_types": [ "way" ],

View File

@ -30,14 +30,14 @@ BOOST_AUTO_TEST_CASE(long_road_test)
std::vector<InputEdge> edges = {
// src, tgt, dist, edge_id, name_id, access_restricted, fwd, bkwd, roundabout, travel_mode
{0, 1, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
{1, 0, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
{1, 2, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
{2, 1, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
{2, 3, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
{3, 2, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
{3, 4, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
{4, 3, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE}};
{0, 1, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE,INVALID_LANE_STRINGID},
{1, 0, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE,INVALID_LANE_STRINGID},
{1, 2, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE,INVALID_LANE_STRINGID},
{2, 1, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE,INVALID_LANE_STRINGID},
{2, 3, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE,INVALID_LANE_STRINGID},
{3, 2, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE,INVALID_LANE_STRINGID},
{3, 4, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE,INVALID_LANE_STRINGID},
{4, 3, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE,INVALID_LANE_STRINGID}};
BOOST_ASSERT(edges[0].data.IsCompatibleTo(edges[2].data));
BOOST_ASSERT(edges[2].data.IsCompatibleTo(edges[4].data));
@ -69,18 +69,18 @@ BOOST_AUTO_TEST_CASE(loop_test)
std::vector<InputEdge> edges = {
// src, tgt, dist, edge_id, name_id, access_restricted, fwd, bkwd, roundabout, travel_mode
{0, 1, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
{0, 5, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
{1, 0, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
{1, 2, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
{2, 1, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
{2, 3, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
{3, 2, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
{3, 4, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
{4, 3, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
{4, 5, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
{5, 0, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
{5, 4, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
{0, 1, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE,INVALID_LANE_STRINGID},
{0, 5, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE,INVALID_LANE_STRINGID},
{1, 0, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE,INVALID_LANE_STRINGID},
{1, 2, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE,INVALID_LANE_STRINGID},
{2, 1, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE,INVALID_LANE_STRINGID},
{2, 3, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE,INVALID_LANE_STRINGID},
{3, 2, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE,INVALID_LANE_STRINGID},
{3, 4, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE,INVALID_LANE_STRINGID},
{4, 3, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE,INVALID_LANE_STRINGID},
{4, 5, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE,INVALID_LANE_STRINGID},
{5, 0, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE,INVALID_LANE_STRINGID},
{5, 4, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE,INVALID_LANE_STRINGID},
};
BOOST_ASSERT(edges.size() == 12);
@ -124,12 +124,12 @@ BOOST_AUTO_TEST_CASE(t_intersection)
std::vector<InputEdge> edges = {
// src, tgt, dist, edge_id, name_id, access_restricted, fwd, bkwd, roundabout, travel_mode
{0, 1, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
{1, 0, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
{1, 2, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
{1, 3, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
{2, 1, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
{3, 1, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
{0, 1, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE,INVALID_LANE_STRINGID},
{1, 0, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE,INVALID_LANE_STRINGID},
{1, 2, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE,INVALID_LANE_STRINGID},
{1, 3, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE,INVALID_LANE_STRINGID},
{2, 1, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE,INVALID_LANE_STRINGID},
{3, 1, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE,INVALID_LANE_STRINGID},
};
BOOST_ASSERT(edges[0].data.IsCompatibleTo(edges[1].data));
@ -160,10 +160,10 @@ BOOST_AUTO_TEST_CASE(street_name_changes)
std::vector<InputEdge> edges = {
// src, tgt, dist, edge_id, name_id, access_restricted, fwd, bkwd, roundabout, travel_mode
{0, 1, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
{1, 0, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
{1, 2, 1, SPECIAL_EDGEID, 1, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
{2, 1, 1, SPECIAL_EDGEID, 1, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
{0, 1, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE,INVALID_LANE_STRINGID},
{1, 0, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE,INVALID_LANE_STRINGID},
{1, 2, 1, SPECIAL_EDGEID, 1, false, false, false, true, TRAVEL_MODE_INACCESSIBLE,INVALID_LANE_STRINGID},
{2, 1, 1, SPECIAL_EDGEID, 1, false, false, false, true, TRAVEL_MODE_INACCESSIBLE,INVALID_LANE_STRINGID},
};
BOOST_ASSERT(edges[0].data.IsCompatibleTo(edges[1].data));
@ -190,10 +190,10 @@ BOOST_AUTO_TEST_CASE(direction_changes)
std::vector<InputEdge> edges = {
// src, tgt, dist, edge_id, name_id, access_restricted, fwd, bkwd, roundabout, travel_mode
{0, 1, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
{1, 0, 1, SPECIAL_EDGEID, 0, false, true, false, true, TRAVEL_MODE_INACCESSIBLE},
{1, 2, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
{2, 1, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE},
{0, 1, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE,INVALID_LANE_STRINGID},
{1, 0, 1, SPECIAL_EDGEID, 0, false, true, false, true, TRAVEL_MODE_INACCESSIBLE,INVALID_LANE_STRINGID},
{1, 2, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE,INVALID_LANE_STRINGID},
{2, 1, 1, SPECIAL_EDGEID, 0, false, false, false, true, TRAVEL_MODE_INACCESSIBLE,INVALID_LANE_STRINGID},
};
Graph graph(5, edges);