basic turn lane handling
This commit is contained in:
parent
2a05b70dfc
commit
efa29edf09
1
.gitignore
vendored
1
.gitignore
vendored
@ -43,6 +43,7 @@ Thumbs.db
|
||||
/example/build/
|
||||
/test/data/monaco*
|
||||
/cmake/postinst
|
||||
.bundle/
|
||||
|
||||
# Eclipse related files #
|
||||
#########################
|
||||
|
@ -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.
|
||||
|
@ -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 |
|
||||
|
@ -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 |
|
||||
|
@ -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 |
|
||||
|
@ -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 |
|
||||
|
@ -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 |
|
||||
|
653
features/guidance/turn-lanes.feature
Normal file
653
features/guidance/turn-lanes.feature
Normal 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, |
|
@ -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 => {
|
||||
|
@ -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/))
|
||||
|
@ -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 |
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 |
|
||||
|
@ -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 |
|
||||
|
@ -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 |
|
||||
|
@ -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 |
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
46
include/extractor/guidance/debug.hpp
Normal file
46
include/extractor/guidance/debug.hpp
Normal 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_ */
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
38
include/extractor/guidance/turn_discovery.hpp
Normal file
38
include/extractor/guidance/turn_discovery.hpp
Normal 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_*/
|
@ -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
|
||||
|
24
include/extractor/guidance/turn_lane_augmentation.hpp
Normal file
24
include/extractor/guidance/turn_lane_augmentation.hpp
Normal 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_ */
|
42
include/extractor/guidance/turn_lane_data.hpp
Normal file
42
include/extractor/guidance/turn_lane_data.hpp
Normal 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_ */
|
79
include/extractor/guidance/turn_lane_handler.hpp
Normal file
79
include/extractor/guidance/turn_lane_handler.hpp
Normal 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_
|
54
include/extractor/guidance/turn_lane_matcher.hpp
Normal file
54
include/extractor/guidance/turn_lane_matcher.hpp
Normal 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_*/
|
@ -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());
|
||||
}
|
||||
|
||||
|
@ -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))
|
||||
{
|
||||
|
@ -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
|
||||
|
85
include/util/guidance/turn_lanes.hpp
Normal file
85
include/util/guidance/turn_lanes.hpp
Normal 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 */
|
@ -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());
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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!");
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
@ -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()
|
||||
|
@ -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,
|
||||
|
@ -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));
|
||||
});
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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
|
||||
|
85
src/extractor/guidance/turn_discovery.cpp
Normal file
85
src/extractor/guidance/turn_discovery.cpp
Normal 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
|
@ -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 =
|
||||
|
304
src/extractor/guidance/turn_lane_augmentation.cpp
Normal file
304
src/extractor/guidance/turn_lane_augmentation.cpp
Normal 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
|
145
src/extractor/guidance/turn_lane_data.cpp
Normal file
145
src/extractor/guidance/turn_lane_data.cpp
Normal 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
|
505
src/extractor/guidance/turn_lane_handler.cpp
Normal file
505
src/extractor/guidance/turn_lane_handler.cpp
Normal 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
|
221
src/extractor/guidance/turn_lane_matcher.cpp
Normal file
221
src/extractor/guidance/turn_lane_matcher.cpp
Normal 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
|
@ -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",
|
||||
|
47
src/util/guidance/turn_lanes.cpp
Normal file
47
src/util/guidance/turn_lanes.cpp
Normal 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
|
@ -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
|
||||
|
30
taginfo.json
30
taginfo.json
@ -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" ],
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user