refactor merging of segregated roads
adjust to generalFindMaximum function moved parallel detection to ratio/absolute based regression testing considerably improved detection quality using normalised regression lines only follow initial direction/narrow turns for parallel detection
This commit is contained in:
parent
f7ad2e1e26
commit
e6ff17ab2a
@ -20,6 +20,7 @@ A guard (ScopedGeojsonLoggerGuard) requires a logging policy. Per default we pro
|
|||||||
|
|
||||||
The initialisation to do so looks like this:
|
The initialisation to do so looks like this:
|
||||||
`util::ScopedGeojsonLoggerGuard<util::NodeIdVectorToLineString> geojson_guard( "debug.geojson", data-for-conversion);`
|
`util::ScopedGeojsonLoggerGuard<util::NodeIdVectorToLineString> geojson_guard( "debug.geojson", data-for-conversion);`
|
||||||
|
Make sure to give the guar a name, so it actually gets a lifetime.
|
||||||
|
|
||||||
The field `data-for-conversion` can be an arbitrary long set of features and needs to match the parameters used for constructing our policy (in this case `util::NodeIdVectorToLineString`).
|
The field `data-for-conversion` can be an arbitrary long set of features and needs to match the parameters used for constructing our policy (in this case `util::NodeIdVectorToLineString`).
|
||||||
|
|
||||||
|
@ -144,36 +144,32 @@ Feature: Collapse
|
|||||||
Given the node map
|
Given the node map
|
||||||
"""
|
"""
|
||||||
n m
|
n m
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
g h
|
g h
|
||||||
|
c - b - a
|
||||||
|
d - e - f
|
||||||
c b a
|
|
||||||
d e f
|
|
||||||
|
|
||||||
|
|
||||||
j i
|
j i
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
k l
|
k l
|
||||||
"""
|
"""
|
||||||
|
|
||||||
And the ways
|
And the ways
|
||||||
| nodes | highway | name | oneway |
|
| nodes | highway | name | oneway | lanes |
|
||||||
| ab | primary | first | yes |
|
| ab | primary | first | yes | |
|
||||||
| bc | primary | first | yes |
|
| bc | primary | first | yes | |
|
||||||
| de | primary | first | yes |
|
| de | primary | first | yes | |
|
||||||
| ef | primary | first | yes |
|
| ef | primary | first | yes | |
|
||||||
| be | primary | first | no |
|
| be | primary | first | no | |
|
||||||
| ngbhm | primary | second | yes |
|
| ngbhm | primary | second | yes | 5 |
|
||||||
| liejk | primary | second | yes |
|
| liejk | primary | second | yes | 5 |
|
||||||
|
|
||||||
When I route I should get
|
When I route I should get
|
||||||
| waypoints | route | turns |
|
| waypoints | route | turns |
|
||||||
@ -198,32 +194,36 @@ Feature: Collapse
|
|||||||
Given the node map
|
Given the node map
|
||||||
"""
|
"""
|
||||||
n m
|
n m
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
g h
|
g h
|
||||||
|
\ /
|
||||||
|
c - b - a
|
||||||
c b a
|
d - e - f
|
||||||
d e f
|
/ \
|
||||||
|
|
||||||
|
|
||||||
j i
|
j i
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
k l
|
k l
|
||||||
"""
|
"""
|
||||||
|
|
||||||
And the ways
|
And the ways
|
||||||
| nodes | highway | name | oneway |
|
| nodes | highway | name | oneway | lanes |
|
||||||
| ab | primary | first | yes |
|
| ab | primary | first | yes | |
|
||||||
| bc | primary | first | yes |
|
| bc | primary | first | yes | |
|
||||||
| de | primary | first | yes |
|
| de | primary | first | yes | |
|
||||||
| ef | primary | first | yes |
|
| ef | primary | first | yes | |
|
||||||
| be | primary | second | no |
|
| be | primary | second | no | |
|
||||||
| ngbhm | primary | second | yes |
|
| ngbhm | primary | second | yes | 5 |
|
||||||
| liejk | primary | second | yes |
|
| liejk | primary | second | yes | 5 |
|
||||||
|
|
||||||
When I route I should get
|
When I route I should get
|
||||||
| waypoints | route | turns |
|
| waypoints | route | turns |
|
||||||
@ -359,7 +359,7 @@ Feature: Collapse
|
|||||||
| a,g | first,second,second | depart,turn left,arrive |
|
| a,g | first,second,second | depart,turn left,arrive |
|
||||||
| d,g | first,second,second | depart,turn right,arrive |
|
| d,g | first,second,second | depart,turn right,arrive |
|
||||||
| g,f | second,first,first | depart,turn right,arrive |
|
| g,f | second,first,first | depart,turn right,arrive |
|
||||||
| g,c | second,first,first | depart,turn left,arrive |
|
| g,c | second,first,first | depart,end of road left,arrive |
|
||||||
|
|
||||||
Scenario: Do not collapse turning roads
|
Scenario: Do not collapse turning roads
|
||||||
Given the node map
|
Given the node map
|
||||||
@ -425,18 +425,28 @@ Feature: Collapse
|
|||||||
Scenario: Pankenbruecke
|
Scenario: Pankenbruecke
|
||||||
Given the node map
|
Given the node map
|
||||||
"""
|
"""
|
||||||
j h i
|
k j
|
||||||
b c d e f g
|
| |
|
||||||
k a
|
| |
|
||||||
|
| |
|
||||||
|
a h
|
||||||
|
b
|
||||||
|
c
|
||||||
|
d
|
||||||
|
e
|
||||||
|
f-i
|
||||||
|
|
|
||||||
|
|
|
||||||
|
|
|
||||||
|
g
|
||||||
"""
|
"""
|
||||||
|
|
||||||
And the ways
|
And the ways
|
||||||
| nodes | highway | name | oneway |
|
| nodes | highway | name | oneway | lanes |
|
||||||
| kabhj | primary | inroad | yes |
|
| kabhj | primary | inroad | yes | 4 |
|
||||||
| bc | primary | inroad | no |
|
| bc | primary | inroad | no | |
|
||||||
| cd | primary | bridge | no |
|
| cd | primary | bridge | no | |
|
||||||
| defg | primary | outroad | no |
|
| defg | primary | outroad | no | |
|
||||||
| fi | primary | cross | no |
|
| fi | primary | cross | no | |
|
||||||
|
|
||||||
When I route I should get
|
When I route I should get
|
||||||
| waypoints | route | turns |
|
| waypoints | route | turns |
|
||||||
|
525
features/guidance/merge-segregated-roads.feature
Normal file
525
features/guidance/merge-segregated-roads.feature
Normal file
@ -0,0 +1,525 @@
|
|||||||
|
@guidance @merge-segregated
|
||||||
|
Feature: Merge Segregated Roads
|
||||||
|
|
||||||
|
Background:
|
||||||
|
Given the profile "car"
|
||||||
|
Given a grid size of 3 meters
|
||||||
|
|
||||||
|
#http://www.openstreetmap.org/#map=18/52.49950/13.33916
|
||||||
|
@negative
|
||||||
|
Scenario: oneway link road
|
||||||
|
Given the node map
|
||||||
|
"""
|
||||||
|
f - - - - - - -_-_e - - - - d
|
||||||
|
...''
|
||||||
|
a - - - b'- - - - - - - - - c
|
||||||
|
"""
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | name | oneway |
|
||||||
|
| abc | road | yes |
|
||||||
|
| def | road | yes |
|
||||||
|
| be | road | yes |
|
||||||
|
|
||||||
|
When I route I should get
|
||||||
|
| waypoints | route | intersections |
|
||||||
|
| a,c | road,road | true:90,true:75 true:90 false:270;true:270 |
|
||||||
|
| d,f | road,road | true:270,false:90 false:255 true:270;true:90 |
|
||||||
|
|
||||||
|
#http://www.openstreetmap.org/#map=18/52.48337/13.36184
|
||||||
|
@negative
|
||||||
|
Scenario: Square Area - Same Name as road for in/out
|
||||||
|
Given the node map
|
||||||
|
"""
|
||||||
|
i
|
||||||
|
|
|
||||||
|
|
|
||||||
|
|
|
||||||
|
g
|
||||||
|
/ \
|
||||||
|
/ \
|
||||||
|
/ \
|
||||||
|
/ \
|
||||||
|
/ \
|
||||||
|
a - - - - c e - - - - f
|
||||||
|
\ /
|
||||||
|
\ /
|
||||||
|
\ /
|
||||||
|
\ /
|
||||||
|
\ /
|
||||||
|
d
|
||||||
|
|
|
||||||
|
|
|
||||||
|
|
|
||||||
|
j
|
||||||
|
"""
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | name | oneway |
|
||||||
|
| ac | road | no |
|
||||||
|
| ef | road | no |
|
||||||
|
| cdegc | road | yes |
|
||||||
|
| ig | top | no |
|
||||||
|
| jd | bot | no |
|
||||||
|
|
||||||
|
When I route I should get
|
||||||
|
| waypoints | route | intersections |
|
||||||
|
| a,f | road,road,road,road | true:90,false:45 true:135 false:270;true:45 true:180 false:315;true:90 false:225 true:315;true:270 |
|
||||||
|
|
||||||
|
#https://www.openstreetmap.org/#map=19/52.50003/13.33915
|
||||||
|
@negative
|
||||||
|
Scenario: Short Segment due to different roads
|
||||||
|
Given the node map
|
||||||
|
"""
|
||||||
|
. d
|
||||||
|
. '
|
||||||
|
. '
|
||||||
|
. '
|
||||||
|
. '
|
||||||
|
a - - - - - - - b - - c - - - - - - e
|
||||||
|
. .
|
||||||
|
. .
|
||||||
|
. .
|
||||||
|
. .
|
||||||
|
.
|
||||||
|
f
|
||||||
|
|
|
||||||
|
|
|
||||||
|
|
|
||||||
|
|
|
||||||
|
g
|
||||||
|
"""
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | name | oneway |
|
||||||
|
| abce | pass | no |
|
||||||
|
| db | pass | yes |
|
||||||
|
| fg | aug | no |
|
||||||
|
| bfc | aug | yes |
|
||||||
|
|
||||||
|
When I route I should get
|
||||||
|
| waypoints | route | intersections |
|
||||||
|
| a,e | pass,pass | true:90,false:60 true:90 true:165 false:270,true:90 false:195 false:270;true:270 |
|
||||||
|
|
||||||
|
@negative
|
||||||
|
Scenario: Tripple Merge should not be possible
|
||||||
|
Given the node map
|
||||||
|
"""
|
||||||
|
. f - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - g
|
||||||
|
.
|
||||||
|
a - - - - b - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - e
|
||||||
|
'
|
||||||
|
' c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - d
|
||||||
|
"""
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | name | oneway |
|
||||||
|
| ab | in | no |
|
||||||
|
| gfb | merge | yes |
|
||||||
|
| be | merge | yes |
|
||||||
|
| dcb | merge | yes |
|
||||||
|
|
||||||
|
When I route I should get
|
||||||
|
| waypoints | route | intersections |
|
||||||
|
| a,e | in,merge,merge | true:90;false:60 true:90 false:120 false:270;true:270 |
|
||||||
|
|
||||||
|
Scenario: Tripple Merge should not be possible
|
||||||
|
Given the node map
|
||||||
|
"""
|
||||||
|
. f - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - g
|
||||||
|
.
|
||||||
|
a - - - - b - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - e
|
||||||
|
'
|
||||||
|
' c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - d
|
||||||
|
"""
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | name | oneway |
|
||||||
|
| ab | in | no |
|
||||||
|
| gfb | merge | yes |
|
||||||
|
| eb | merge | yes |
|
||||||
|
| bcd | merge | yes |
|
||||||
|
|
||||||
|
When I route I should get
|
||||||
|
| waypoints | route | intersections |
|
||||||
|
| a,d | in,merge,merge | true:90;false:60 false:90 true:120 false:270;true:270 |
|
||||||
|
|
||||||
|
@negative
|
||||||
|
Scenario: Don't accept turn-restrictions
|
||||||
|
Given the node map
|
||||||
|
"""
|
||||||
|
c - - - - - - - - - - - - - - - - - - - - - - - - - - - - - d
|
||||||
|
/ \
|
||||||
|
a - - - b g - - h
|
||||||
|
\ /
|
||||||
|
e - - - - - - - - - - - - - - - - - - - - - - - - - - - - - f
|
||||||
|
"""
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | name | oneway |
|
||||||
|
| ab | road | yes |
|
||||||
|
| befgh | road | yes |
|
||||||
|
| bcdg | road | yes |
|
||||||
|
|
||||||
|
# This is an artificial scenario - not reasonable. It is only to test the merging on turn-restrictions
|
||||||
|
And the relations
|
||||||
|
| type | way:from | way:to | node:via | restriction |
|
||||||
|
| restriction | ab | bcdg | b | no_left_turn |
|
||||||
|
|
||||||
|
When I route I should get
|
||||||
|
| waypoints | route | intersections |
|
||||||
|
| a,h | road,road | true:90,false:60 true:120 false:270,true:90 false:240 false:300;true:270 |
|
||||||
|
|
||||||
|
@negative
|
||||||
|
Scenario: Actual Turn into segregated ways
|
||||||
|
Given the node map
|
||||||
|
"""
|
||||||
|
a - - - b - < - < - < - < - < - < - < - < - < - < - < c -
|
||||||
|
| \
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
d |
|
||||||
|
\ |
|
||||||
|
\ |
|
||||||
|
e > - > - > - > - > - > - > - > - > - > - > f - - - - - - g
|
||||||
|
"""
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | name | oneway |
|
||||||
|
| ab | road | no |
|
||||||
|
| fcb | road | yes |
|
||||||
|
| bdef | road | yes |
|
||||||
|
| fg | road | no |
|
||||||
|
|
||||||
|
When I route I should get
|
||||||
|
| waypoints | route | intersections |
|
||||||
|
| a,g | road,road | true:90,false:90 true:150 false:270,true:90 false:270 true:345;true:270 |
|
||||||
|
|
||||||
|
Scenario: Merging parallel roads with intermediate bridges
|
||||||
|
# https://www.mapillary.com/app/?lat=52.466483333333336&lng=13.431908333333332&z=17&focus=photo&pKey=LWXnKqoGqUNLnG0lofiO0Q
|
||||||
|
# http://www.openstreetmap.org/#map=19/52.46750/13.43171
|
||||||
|
Given the node map
|
||||||
|
"""
|
||||||
|
f
|
||||||
|
|
|
||||||
|
.e.
|
||||||
|
/ \
|
||||||
|
/ \
|
||||||
|
g d
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
h c
|
||||||
|
\ /
|
||||||
|
\ /
|
||||||
|
\ /
|
||||||
|
b
|
||||||
|
|
|
||||||
|
a
|
||||||
|
|
|
||||||
|
|
|
||||||
|
r - x - s
|
||||||
|
|
|
||||||
|
|
|
||||||
|
y
|
||||||
|
"""
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | name | highway | oneway | lanes |
|
||||||
|
| ab | Hermannstr | secondary | | 2 |
|
||||||
|
| bc | Hermannstr | secondary | yes | 2 |
|
||||||
|
| cd | Hermannbruecke | secondary | yes | 2 |
|
||||||
|
| de | Hermannstr | secondary | yes | 2 |
|
||||||
|
| ef | Hermannstr | secondary | | 4 |
|
||||||
|
| eg | Hermannstr | secondary | yes | 2 |
|
||||||
|
| gh | Hermannbruecke | secondary | yes | 2 |
|
||||||
|
| hb | Hermannstr | secondary | yes | 2 |
|
||||||
|
| xa | Hermannstr | secondary | | 4 |
|
||||||
|
| yx | Hermannstr | secondary | | 4 |
|
||||||
|
| rxs | Silbersteinstr | tertiary | | 1 |
|
||||||
|
|
||||||
|
And the nodes
|
||||||
|
| node | highway |
|
||||||
|
| x | traffic_signals |
|
||||||
|
|
||||||
|
#the intermediate intersections of degree two indicate short segments of new names. At some point, we probably want to get rid of these
|
||||||
|
When I route I should get
|
||||||
|
| waypoints | turns | route | intersections |
|
||||||
|
| a,f | depart,arrive | Hermannstr,Hermannstr | true:0,true:0 false:180,true:0 false:180;true:180 |
|
||||||
|
| f,a | depart,arrive | Hermannstr,Hermannstr | true:180,false:0 true:180,false:0 true:180;true:0 |
|
||||||
|
| y,f | depart,arrive | Hermannstr,Hermannstr | true:0,true:0 true:90 false:180 true:270,true:0 false:180,true:0 false:180;true:180 |
|
||||||
|
| f,y | depart,arrive | Hermannstr,Hermannstr | true:180,false:0 true:180,false:0 true:180,false:0 true:90 true:180 true:270;true:0 |
|
||||||
|
|
||||||
|
Scenario: Four Way Intersection Double Through Street Segregated
|
||||||
|
Given the node map
|
||||||
|
"""
|
||||||
|
q p
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
b c
|
||||||
|
\ /
|
||||||
|
\ /
|
||||||
|
\ /
|
||||||
|
j - - - - - - - - - - - - - - - - - i . \ / , d - - - - - - - - - - - - - - - - - o
|
||||||
|
. \/ .
|
||||||
|
> a <
|
||||||
|
. /\ '
|
||||||
|
. / \ '
|
||||||
|
k - - - - - - - - - - - - - - - - - h / \ e - - - - - - - - - - - - - - - - - n
|
||||||
|
/ \
|
||||||
|
/ \
|
||||||
|
g f
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
l m
|
||||||
|
"""
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | highway | oneway | name | lanes |
|
||||||
|
| khaij | primary | yes | first | 4 |
|
||||||
|
| odaen | primary | yes | first | 4 |
|
||||||
|
| qbacp | primary | yes | second | 4 |
|
||||||
|
| mfagl | primary | yes | second | 4 |
|
||||||
|
|
||||||
|
When I route I should get
|
||||||
|
| waypoints | route | turns |
|
||||||
|
| f,e | second,first,first | depart,turn right,arrive |
|
||||||
|
| f,c | second,second | depart,arrive |
|
||||||
|
| f,i | second,first,first | depart,turn left,arrive |
|
||||||
|
| f,g | second,second,second | depart,continue uturn,arrive |
|
||||||
|
| d,c | first,second,second | depart,turn right,arrive |
|
||||||
|
| d,i | first,first | depart,arrive |
|
||||||
|
| d,g | first,second,second | depart,turn left,arrive |
|
||||||
|
| d,e | first,first,first | depart,continue uturn,arrive |
|
||||||
|
| b,i | second,first,first | depart,turn right,arrive |
|
||||||
|
| b,g | second,second | depart,arrive |
|
||||||
|
| b,e | second,first,first | depart,turn left,arrive |
|
||||||
|
| b,c | second,second,second | depart,continue uturn,arrive |
|
||||||
|
| h,g | first,second,second | depart,turn right,arrive |
|
||||||
|
| h,e | first,first | depart,arrive |
|
||||||
|
| h,c | first,second,second | depart,turn left,arrive |
|
||||||
|
| h,i | first,first,first | depart,continue uturn,arrive |
|
||||||
|
|
||||||
|
Scenario: Middle Island Over Bridge
|
||||||
|
Given the node map
|
||||||
|
"""
|
||||||
|
a
|
||||||
|
|
|
||||||
|
.b.
|
||||||
|
c h
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
1 2
|
||||||
|
| |
|
||||||
|
d g
|
||||||
|
'e'
|
||||||
|
|
|
||||||
|
f
|
||||||
|
"""
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | name | oneway |
|
||||||
|
| ab | road | no |
|
||||||
|
| ef | road | no |
|
||||||
|
| bc | road | yes |
|
||||||
|
| cd | bridge | yes |
|
||||||
|
| de | road | yes |
|
||||||
|
| eg | road | yes |
|
||||||
|
| gh | bridge | yes |
|
||||||
|
| hb | road | yes |
|
||||||
|
|
||||||
|
When I route I should get
|
||||||
|
| waypoints | turns | route | intersections |
|
||||||
|
| a,f | depart,arrive | road,road | true:180,false:0 true:180,false:0 true:180;true:0 |
|
||||||
|
| c,f | depart,new name straight,arrive | bridge,road,road | true:180;false:0 true:180;true:0 |
|
||||||
|
| 1,f | depart,new name straight,arrive | bridge,road,road | true:180;false:0 true:180;true:0 |
|
||||||
|
| f,a | depart,arrive | road,road | true:0,true:0 false:180,true:0 false:180;true:180 |
|
||||||
|
| g,a | depart,new name straight,arrive | bridge,road,road | true:0;true:0 false:180;true:180 |
|
||||||
|
| 2,a | depart,new name straight,arrive | bridge,road,road | true:0;true:0 false:180;true:180 |
|
||||||
|
|
||||||
|
@negative
|
||||||
|
Scenario: Traffic Circle
|
||||||
|
Given the node map
|
||||||
|
"""
|
||||||
|
a - - - - b - - - e - - - c - - - - d
|
||||||
|
\ /
|
||||||
|
\ /
|
||||||
|
f
|
||||||
|
|
|
||||||
|
|
|
||||||
|
|
|
||||||
|
g
|
||||||
|
"""
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | name | oneway |
|
||||||
|
| ab | left | no |
|
||||||
|
| bfceb | circle | yes |
|
||||||
|
| fg | bottom | no |
|
||||||
|
| cd | right | no |
|
||||||
|
|
||||||
|
When I route I should get
|
||||||
|
| waypoints | route | intersections |
|
||||||
|
| a,d | left,circle,circle,right,right | true:90;false:90 true:120 false:270;true:60 true:180 false:300;true:90 false:240 true:270;true:270 |
|
||||||
|
| g,d | bottom,circle,right,right | true:0;true:60 false:180 false:300;true:90 false:240 true:270;true:270 |
|
||||||
|
|
||||||
|
Scenario: Middle Island
|
||||||
|
Given the node map
|
||||||
|
"""
|
||||||
|
a
|
||||||
|
|
|
||||||
|
b
|
||||||
|
c h
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
d g
|
||||||
|
e
|
||||||
|
|
|
||||||
|
f
|
||||||
|
"""
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | name | oneway |
|
||||||
|
| ab | road | no |
|
||||||
|
| ef | road | no |
|
||||||
|
| bcde | road | yes |
|
||||||
|
| eghb | road | yes |
|
||||||
|
|
||||||
|
When I route I should get
|
||||||
|
| waypoints | turns | route |
|
||||||
|
| a,f | depart,arrive | road,road |
|
||||||
|
| c,f | depart,arrive | road,road |
|
||||||
|
| f,a | depart,arrive | road,road |
|
||||||
|
| g,a | depart,arrive | road,road |
|
||||||
|
|
||||||
|
Scenario: Traffic Island
|
||||||
|
Given the node map
|
||||||
|
"""
|
||||||
|
f
|
||||||
|
a - - b < > d - - e
|
||||||
|
c
|
||||||
|
"""
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | name | oneway |
|
||||||
|
| ab | road | no |
|
||||||
|
| de | road | no |
|
||||||
|
| bcdfb | road | yes |
|
||||||
|
|
||||||
|
When I route I should get
|
||||||
|
| waypoints | route | intersections |
|
||||||
|
| a,e | road,road | true:90;true:270 |
|
||||||
|
|
||||||
|
@negative
|
||||||
|
Scenario: Turning Road, Don't remove sliproads
|
||||||
|
Given the node map
|
||||||
|
"""
|
||||||
|
h - - - - - g - - - - - - f - - - - - e
|
||||||
|
_ '
|
||||||
|
.
|
||||||
|
a - - - - - b - - - - - - c - - - - - d
|
||||||
|
|
|
||||||
|
|
|
||||||
|
|
|
||||||
|
i
|
||||||
|
"""
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | name | oneway |
|
||||||
|
| ab | road | yes |
|
||||||
|
| bcd | road | yes |
|
||||||
|
| efgh | road | yes |
|
||||||
|
| fb | road | yes |
|
||||||
|
| bi | turn | yes |
|
||||||
|
|
||||||
|
And the relations
|
||||||
|
| type | way:from | way:to | node:via | restriction |
|
||||||
|
| restriction | fb | bcd | b | no_left_turn |
|
||||||
|
|
||||||
|
When I route I should get
|
||||||
|
| waypoints | route | turns | intersections |
|
||||||
|
| a,d | road,road | depart,arrive | true:90,false:60 true:90 true:180 false:270;true:270 |
|
||||||
|
| e,h | road,road | depart,arrive | true:270,false:90 true:240 true:270;true:90 |
|
||||||
|
| e,i | road,turn,turn | depart,turn left,arrive | true:270;false:90 true:240 true:270,false:60 false:90 true:180 false:270;true:0 |
|
||||||
|
@negative
|
||||||
|
Scenario: Meeting Turn Roads
|
||||||
|
Given the node map
|
||||||
|
"""
|
||||||
|
k l
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
h - - - - - g - - - - - - - f - - - - - e
|
||||||
|
| ' ' |
|
||||||
|
| x |
|
||||||
|
| . . |
|
||||||
|
a - - - - - b - - - - - - - c - - - - - d
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
i j
|
||||||
|
"""
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | name | oneway |
|
||||||
|
| ab | horiz | yes |
|
||||||
|
| bc | horiz | yes |
|
||||||
|
| cd | horiz | yes |
|
||||||
|
| ef | horiz | yes |
|
||||||
|
| fg | horiz | yes |
|
||||||
|
| gh | horiz | yes |
|
||||||
|
| kg | vert | yes |
|
||||||
|
| gb | vert | yes |
|
||||||
|
| bi | vert | yes |
|
||||||
|
| jc | vert | yes |
|
||||||
|
| cf | vert | yes |
|
||||||
|
| fl | vert | yes |
|
||||||
|
| gx | horiz | no |
|
||||||
|
| xc | horiz | no |
|
||||||
|
| fx | horiz | no |
|
||||||
|
| xb | horiz | no |
|
||||||
|
And the relations
|
||||||
|
| type | way:from | way:to | node:via | restriction |
|
||||||
|
| restriction | bc | cf | c | no_left_turn |
|
||||||
|
| restriction | fg | gb | g | no_left_turn |
|
||||||
|
| restriction | cf | fg | f | no_left_turn |
|
||||||
|
| restriction | gb | bc | b | no_left_turn |
|
||||||
|
| restriction | xb | bc | b | no_left_turn |
|
||||||
|
| restriction | xc | cf | c | no_left_turn |
|
||||||
|
| restriction | xf | fg | f | no_left_turn |
|
||||||
|
| restriction | xg | gb | g | no_left_turn |
|
||||||
|
|
||||||
|
# the goal here should be not to mention the intersection in the middle at all and also suppress the segregated parts
|
||||||
|
When I route I should get
|
||||||
|
| waypoints | route | intersections |
|
||||||
|
| a,l | horiz,vert,vert | true:90;false:0 true:60 true:90 true:180 false:270,true:60 true:120 false:240 true:300,true:0 false:90 false:180 false:240 false:270;true:180 |
|
||||||
|
| a,d | horiz,horiz | true:90,false:0 true:60 true:90 true:180 false:270,false:0 true:90 false:180 false:270 true:300;true:270 |
|
||||||
|
| j,h | vert,horiz,horiz | true:0;true:0 true:90 false:180 false:270 true:300,true:60 false:120 true:240 true:300,false:0 false:90 false:120 false:180 true:270;true:90 |
|
||||||
|
| j,l | vert,vert | true:0,true:0 true:90 false:180 false:270 true:300,true:0 false:90 false:180 true:240 false:270;true:180 |
|
@ -13,23 +13,28 @@ Feature: Simple Turns
|
|||||||
^
|
^
|
||||||
/ \
|
/ \
|
||||||
c d
|
c d
|
||||||
|\
|
| |\
|
||||||
| e
|
| | e
|
||||||
|
|
| |
|
||||||
f
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
g f
|
||||||
"""
|
"""
|
||||||
|
|
||||||
And the ways
|
And the ways
|
||||||
| nodes | name | highway | oneway |
|
| nodes | name | highway | oneway |
|
||||||
| ab | road | primary | no |
|
| ab | road | primary | no |
|
||||||
| bc | road | primary | yes |
|
| bcg | road | primary | yes |
|
||||||
| fdb | road | primary | yes |
|
| fdb | road | primary | yes |
|
||||||
| de | turn | primary | no |
|
| ed | turn | primary | yes |
|
||||||
|
|
||||||
When I route I should get
|
When I route I should get
|
||||||
| waypoints | turns | route |
|
| waypoints | turns | route | intersections |
|
||||||
| f,a | depart,arrive | road,road |
|
| f,a | depart,arrive | road,road | true:0,true:0 false:150 false:180;true:180 |
|
||||||
| e,a | depart,turn slight right,arrive | turn,road,road |
|
| e,a | depart,turn slight right,arrive | turn,road,road | true:333;true:0 false:150 false:180;true:180 |
|
||||||
|
|
||||||
Scenario: Turning into splitting road
|
Scenario: Turning into splitting road
|
||||||
Given the node map
|
Given the node map
|
||||||
@ -39,16 +44,22 @@ Feature: Simple Turns
|
|||||||
/\
|
/\
|
||||||
/ \
|
/ \
|
||||||
c d
|
c d
|
||||||
|\
|
| |\
|
||||||
| e
|
| | e
|
||||||
|
|
| |
|
||||||
f
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
h f
|
||||||
"""
|
"""
|
||||||
|
|
||||||
And the ways
|
And the ways
|
||||||
| nodes | name | highway | oneway |
|
| nodes | name | highway | oneway |
|
||||||
| ab | road | primary | no |
|
| ab | road | primary | no |
|
||||||
| bc | road | primary | yes |
|
| bch | road | primary | yes |
|
||||||
| fdb | road | primary | yes |
|
| fdb | road | primary | yes |
|
||||||
| de | turn | primary | no |
|
| de | turn | primary | no |
|
||||||
| bg | left | primary | yes |
|
| bg | left | primary | yes |
|
||||||
@ -61,108 +72,6 @@ Feature: Simple Turns
|
|||||||
| f,g | depart,turn left,arrive | road,left,left |
|
| f,g | depart,turn left,arrive | road,left,left |
|
||||||
| f,c | depart,continue uturn,arrive | road,road,road |
|
| f,c | depart,continue uturn,arrive | road,road,road |
|
||||||
|
|
||||||
Scenario: Middle Island
|
|
||||||
Given the node map
|
|
||||||
"""
|
|
||||||
a
|
|
||||||
|
|
||||||
b
|
|
||||||
c h
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
d g
|
|
||||||
e
|
|
||||||
|
|
||||||
f
|
|
||||||
"""
|
|
||||||
|
|
||||||
And the ways
|
|
||||||
| nodes | name | oneway |
|
|
||||||
| ab | road | no |
|
|
||||||
| ef | road | no |
|
|
||||||
| bcde | road | yes |
|
|
||||||
| eghb | road | yes |
|
|
||||||
|
|
||||||
When I route I should get
|
|
||||||
| waypoints | turns | route |
|
|
||||||
| a,f | depart,arrive | road,road |
|
|
||||||
| c,f | depart,arrive | road,road |
|
|
||||||
| f,a | depart,arrive | road,road |
|
|
||||||
| g,a | depart,arrive | road,road |
|
|
||||||
|
|
||||||
Scenario: Middle Island Over Bridge
|
|
||||||
Given the node map
|
|
||||||
"""
|
|
||||||
a
|
|
||||||
|
|
|
||||||
.b.
|
|
||||||
c h
|
|
||||||
| |
|
|
||||||
| |
|
|
||||||
1 2
|
|
||||||
| |
|
|
||||||
d g
|
|
||||||
'e'
|
|
||||||
|
|
|
||||||
f
|
|
||||||
"""
|
|
||||||
|
|
||||||
And the ways
|
|
||||||
| nodes | name | oneway |
|
|
||||||
| ab | road | no |
|
|
||||||
| ef | road | no |
|
|
||||||
| bc | road | yes |
|
|
||||||
| cd | bridge | yes |
|
|
||||||
| de | road | yes |
|
|
||||||
| eg | road | yes |
|
|
||||||
| gh | bridge | yes |
|
|
||||||
| hb | road | yes |
|
|
||||||
|
|
||||||
When I route I should get
|
|
||||||
| waypoints | turns | route |
|
|
||||||
| a,f | depart,arrive | road,road |
|
|
||||||
| c,f | depart,new name straight,arrive | bridge,road,road |
|
|
||||||
| 1,f | depart,new name straight,arrive | bridge,road,road |
|
|
||||||
| f,a | depart,arrive | road,road |
|
|
||||||
| g,a | depart,new name straight,arrive | bridge,road,road |
|
|
||||||
| 2,a | depart,new name straight,arrive | bridge,road,road |
|
|
||||||
|
|
||||||
@negative
|
|
||||||
Scenario: Don't Collapse Places:
|
|
||||||
Given the node map
|
|
||||||
"""
|
|
||||||
h
|
|
||||||
g
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
a b e f
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
c
|
|
||||||
d
|
|
||||||
"""
|
|
||||||
|
|
||||||
And the ways
|
|
||||||
| nodes | name | oneway |
|
|
||||||
| ab | place | no |
|
|
||||||
| cd | bottom | no |
|
|
||||||
| ef | place | no |
|
|
||||||
| gh | top | no |
|
|
||||||
| bcegb | place | yes |
|
|
||||||
|
|
||||||
When I route I should get
|
|
||||||
| waypoints | turns | route |
|
|
||||||
| a,d | depart,turn right,arrive | place,bottom,bottom |
|
|
||||||
| a,f | depart,continue left,continue right,arrive | place,place,place,place |
|
|
||||||
| d,f | depart,turn right,continue right,arrive | bottom,place,place,place |
|
|
||||||
| d,h | depart,turn right,continue left,turn right,arrive | bottom,place,place,top,top |
|
|
||||||
|
|
||||||
@bug @not-sorted @3179
|
@bug @not-sorted @3179
|
||||||
Scenario: Adjusting road angles to not be sorted
|
Scenario: Adjusting road angles to not be sorted
|
||||||
Given the node map
|
Given the node map
|
||||||
|
@ -768,6 +768,30 @@ Feature: Simple Turns
|
|||||||
|
|
||||||
. .
|
. .
|
||||||
|
|
||||||
|
. .
|
||||||
|
|
||||||
|
. .
|
||||||
|
|
||||||
|
. .
|
||||||
|
|
||||||
|
. .
|
||||||
|
|
||||||
|
. .
|
||||||
|
|
||||||
|
. .
|
||||||
|
|
||||||
|
. .
|
||||||
|
|
||||||
|
. .
|
||||||
|
|
||||||
|
. .
|
||||||
|
|
||||||
|
. .
|
||||||
|
|
||||||
|
. .
|
||||||
|
|
||||||
|
. .
|
||||||
|
|
||||||
g h
|
g h
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -784,15 +808,15 @@ Feature: Simple Turns
|
|||||||
| cjk | Friede | no | | tertiary |
|
| cjk | Friede | no | | tertiary |
|
||||||
|
|
||||||
When I route I should get
|
When I route I should get
|
||||||
| waypoints | route | turns |
|
| waypoints | route | turns | intersections |
|
||||||
| a,g | Perle,Heide,Heide | depart,turn right,arrive |
|
| a,g | Perle,Heide,Heide | depart,turn right,arrive | true:90;true:90 true:180 false:270 true:345;true:18 |
|
||||||
| a,k | Perle,Friede,Friede | depart,turn left,arrive |
|
| a,k | Perle,Friede,Friede | depart,turn left,arrive | true:90;true:90 true:180 false:270 true:345;true:153 |
|
||||||
| a,e | Perle,Perle | depart,arrive |
|
| a,e | Perle,Perle | depart,arrive | true:90,true:90 true:180 false:270 true:345;true:270 |
|
||||||
| e,k | Perle,Friede,Friede | depart,turn right,arrive |
|
| e,k | Perle,Friede,Friede | depart,turn right,arrive | true:270;false:90 true:180 true:270 true:345;true:153 |
|
||||||
| e,g | Perle,Heide,Heide | depart,turn left,arrive |
|
| e,g | Perle,Heide,Heide | depart,turn left,arrive | true:270;false:90 true:180 true:270 true:345;true:18 |
|
||||||
| h,k | Heide,Friede,Friede | depart,new name slight left,arrive |
|
| h,k | Heide,Friede,Friede | depart,new name straight,arrive | true:16;true:90 true:180 true:270 true:345;true:153 |
|
||||||
| h,e | Heide,Perle,Perle | depart,turn right,arrive |
|
| h,e | Heide,Perle,Perle | depart,turn right,arrive | true:16;true:90 true:180 true:270 true:345;true:270 |
|
||||||
| h,a | Heide,Perle,Perle | depart,turn left,arrive |
|
| h,a | Heide,Perle,Perle | depart,turn left,arrive | true:16;true:90 true:180 true:270 true:345;true:90 |
|
||||||
|
|
||||||
#http://www.openstreetmap.org/#map=19/52.53293/13.32956
|
#http://www.openstreetmap.org/#map=19/52.53293/13.32956
|
||||||
Scenario: Curved Exit from Curved Road
|
Scenario: Curved Exit from Curved Road
|
||||||
@ -930,6 +954,36 @@ Feature: Simple Turns
|
|||||||
. . .
|
. . .
|
||||||
. . .
|
. . .
|
||||||
i . .
|
i . .
|
||||||
|
. .
|
||||||
|
. .
|
||||||
|
. .
|
||||||
|
. .
|
||||||
|
. .
|
||||||
|
. .
|
||||||
|
. .
|
||||||
|
. .
|
||||||
|
. .
|
||||||
|
. .
|
||||||
|
. .
|
||||||
|
. .
|
||||||
|
. .
|
||||||
|
. .
|
||||||
|
. .
|
||||||
|
. .
|
||||||
|
. .
|
||||||
|
. .
|
||||||
|
. .
|
||||||
|
. .
|
||||||
|
. .
|
||||||
|
. .
|
||||||
|
. .
|
||||||
|
. .
|
||||||
|
. .
|
||||||
|
. .
|
||||||
|
. .
|
||||||
|
. .
|
||||||
|
. .
|
||||||
|
. .
|
||||||
e a
|
e a
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -599,22 +599,21 @@ Feature: Turn Lane Guidance
|
|||||||
Scenario: Segregated Intersection Merges With Lanes
|
Scenario: Segregated Intersection Merges With Lanes
|
||||||
Given the node map
|
Given the node map
|
||||||
"""
|
"""
|
||||||
f
|
a e
|
||||||
|
| |
|
||||||
e d
|
| |
|
||||||
c g
|
b d
|
||||||
a b
|
h c
|
||||||
|
' -- g - - f
|
||||||
h
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
And the ways
|
And the ways
|
||||||
| nodes | name | turn:lanes:forward | oneway | highway |
|
| nodes | name | turn:lanes:forward | oneway | highway | lanes |
|
||||||
| abc | road | left\|left\|left\|through\|through | yes | primary |
|
| abc | road | left\|left\|left\|through\|through | yes | primary | 5 |
|
||||||
| cde | road | | yes | primary |
|
| cde | road | | yes | primary | 3 |
|
||||||
| hc | cross | | yes | secondary |
|
| hc | cross | | yes | secondary | |
|
||||||
| cg | straight | | no | tertiary |
|
| cg | straight | | no | tertiary | |
|
||||||
| cf | left | | yes | primary |
|
| cf | left | | yes | primary | |
|
||||||
|
|
||||||
When I route I should get
|
When I route I should get
|
||||||
| waypoints | route | turns | lanes |
|
| waypoints | route | turns | lanes |
|
||||||
|
@ -278,52 +278,6 @@ Feature: Simple Turns
|
|||||||
| x | z | xy,yz,yz | depart,turn right,arrive |
|
| x | z | xy,yz,yz | depart,turn right,arrive |
|
||||||
| z | x | yz,xy,xy | depart,turn left,arrive |
|
| z | x | yz,xy,xy | depart,turn left,arrive |
|
||||||
|
|
||||||
Scenario: Four Way Intersection Double Through Street Segregated
|
|
||||||
Given the node map
|
|
||||||
"""
|
|
||||||
q p
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
b c
|
|
||||||
j i d o
|
|
||||||
a
|
|
||||||
k h e n
|
|
||||||
g f
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
l m
|
|
||||||
"""
|
|
||||||
|
|
||||||
And the ways
|
|
||||||
| nodes | highway | oneway | name |
|
|
||||||
| khaij | primary | yes | first |
|
|
||||||
| odaen | primary | yes | first |
|
|
||||||
| qbacp | primary | yes | second |
|
|
||||||
| mfagl | primary | yes | second |
|
|
||||||
|
|
||||||
When I route I should get
|
|
||||||
| waypoints | route | turns |
|
|
||||||
| f,e | second,first,first | depart,turn right,arrive |
|
|
||||||
| f,c | second,second | depart,arrive |
|
|
||||||
| f,i | second,first,first | depart,turn left,arrive |
|
|
||||||
| f,g | second,second,second | depart,continue uturn,arrive |
|
|
||||||
| d,c | first,second,second | depart,turn right,arrive |
|
|
||||||
| d,i | first,first | depart,arrive |
|
|
||||||
| d,g | first,second,second | depart,turn left,arrive |
|
|
||||||
| d,e | first,first,first | depart,continue uturn,arrive |
|
|
||||||
| b,i | second,first,first | depart,turn right,arrive |
|
|
||||||
| b,g | second,second | depart,arrive |
|
|
||||||
| b,e | second,first,first | depart,turn left,arrive |
|
|
||||||
| b,c | second,second,second | depart,continue uturn,arrive |
|
|
||||||
| h,g | first,second,second | depart,turn right,arrive |
|
|
||||||
| h,e | first,first | depart,arrive |
|
|
||||||
| h,c | first,second,second | depart,turn left,arrive |
|
|
||||||
| h,i | first,first,first | depart,continue uturn,arrive |
|
|
||||||
|
|
||||||
Scenario: Three Way Similar Sharp Turns
|
Scenario: Three Way Similar Sharp Turns
|
||||||
Given the node map
|
Given the node map
|
||||||
"""
|
"""
|
||||||
@ -1080,100 +1034,6 @@ Feature: Simple Turns
|
|||||||
| a,d | depart,new name straight,arrive | Molkenmarkt,Stralauer Str,Stralauer Str |
|
| a,d | depart,new name straight,arrive | Molkenmarkt,Stralauer Str,Stralauer Str |
|
||||||
| e,d | depart,new name slight left,arrive | Molkenmarkt,Stralauer Str,Stralauer Str |
|
| e,d | depart,new name slight left,arrive | Molkenmarkt,Stralauer Str,Stralauer Str |
|
||||||
|
|
||||||
# https://www.mapillary.com/app/?lat=52.466483333333336&lng=13.431908333333332&z=17&focus=photo&pKey=LWXnKqoGqUNLnG0lofiO0Q
|
|
||||||
# http://www.openstreetmap.org/#map=19/52.46750/13.43171
|
|
||||||
Scenario: Collapse Turn Instruction, Issue #2725
|
|
||||||
Given the node map
|
|
||||||
"""
|
|
||||||
f
|
|
||||||
e
|
|
||||||
|
|
||||||
|
|
||||||
g d
|
|
||||||
|
|
||||||
|
|
||||||
h c
|
|
||||||
|
|
||||||
|
|
||||||
b
|
|
||||||
a
|
|
||||||
|
|
||||||
|
|
||||||
r x s
|
|
||||||
y
|
|
||||||
"""
|
|
||||||
|
|
||||||
And the ways
|
|
||||||
| nodes | name | highway | oneway |
|
|
||||||
| ab | Hermannstr | secondary | |
|
|
||||||
| bc | Hermannstr | secondary | yes |
|
|
||||||
| cd | Hermannbruecke | secondary | yes |
|
|
||||||
| de | Hermannstr | secondary | yes |
|
|
||||||
| ef | Hermannstr | secondary | |
|
|
||||||
| eg | Hermannstr | secondary | yes |
|
|
||||||
| gh | Hermannbruecke | secondary | yes |
|
|
||||||
| hb | Hermannstr | secondary | yes |
|
|
||||||
| xa | Hermannstr | secondary | |
|
|
||||||
| yx | Hermannstr | secondary | |
|
|
||||||
| rxs | Silbersteinstr | tertiary | |
|
|
||||||
|
|
||||||
And the nodes
|
|
||||||
| node | highway |
|
|
||||||
| x | traffic_signals |
|
|
||||||
|
|
||||||
When I route I should get
|
|
||||||
| waypoints | turns | route |
|
|
||||||
| a,f | depart,arrive | Hermannstr,Hermannstr |
|
|
||||||
| f,a | depart,arrive | Hermannstr,Hermannstr |
|
|
||||||
| y,f | depart,arrive | Hermannstr,Hermannstr |
|
|
||||||
| f,y | depart,arrive | Hermannstr,Hermannstr |
|
|
||||||
|
|
||||||
Scenario: Collapse Turn Instruction, Issue #2725 - not trivially mergable at e
|
|
||||||
# https://www.mapillary.com/app/?lat=52.466483333333336&lng=13.431908333333332&z=17&focus=photo&pKey=LWXnKqoGqUNLnG0lofiO0Q
|
|
||||||
# http://www.openstreetmap.org/#map=19/52.46750/13.43171
|
|
||||||
Given the node map
|
|
||||||
"""
|
|
||||||
f
|
|
||||||
e
|
|
||||||
g d
|
|
||||||
|
|
||||||
|
|
||||||
h c
|
|
||||||
|
|
||||||
|
|
||||||
b
|
|
||||||
a
|
|
||||||
|
|
||||||
|
|
||||||
r x s
|
|
||||||
y
|
|
||||||
"""
|
|
||||||
|
|
||||||
And the ways
|
|
||||||
| nodes | name | highway | oneway |
|
|
||||||
| ab | Hermannstr | secondary | |
|
|
||||||
| bc | Hermannstr | secondary | yes |
|
|
||||||
| cd | Hermannbruecke | secondary | yes |
|
|
||||||
| de | Hermannstr | secondary | yes |
|
|
||||||
| ef | Hermannstr | secondary | |
|
|
||||||
| eg | Hermannstr | secondary | yes |
|
|
||||||
| gh | Hermannbruecke | secondary | yes |
|
|
||||||
| hb | Hermannstr | secondary | yes |
|
|
||||||
| xa | Hermannstr | secondary | |
|
|
||||||
| yx | Hermannstr | secondary | |
|
|
||||||
| rxs | Silbersteinstr | tertiary | |
|
|
||||||
|
|
||||||
And the nodes
|
|
||||||
| node | highway |
|
|
||||||
| x | traffic_signals |
|
|
||||||
|
|
||||||
When I route I should get
|
|
||||||
| waypoints | turns | route |
|
|
||||||
| a,f | depart,arrive | Hermannstr,Hermannstr |
|
|
||||||
| f,a | depart,arrive | Hermannstr,Hermannstr |
|
|
||||||
| y,f | depart,arrive | Hermannstr,Hermannstr |
|
|
||||||
| f,y | depart,arrive | Hermannstr,Hermannstr |
|
|
||||||
|
|
||||||
# http://www.openstreetmap.org/#map=18/39.28158/-76.62291
|
# http://www.openstreetmap.org/#map=18/39.28158/-76.62291
|
||||||
@3002
|
@3002
|
||||||
Scenario: Obvious Index wigh very narrow turn to the right
|
Scenario: Obvious Index wigh very narrow turn to the right
|
||||||
|
@ -154,7 +154,7 @@ inline std::vector<RouteStep> assembleSteps(const datafacade::BaseDataFacade &fa
|
|||||||
intersection.entry.push_back(entry_class.allowsEntry(idx));
|
intersection.entry.push_back(entry_class.allowsEntry(idx));
|
||||||
}
|
}
|
||||||
std::int16_t bearing_in_driving_direction =
|
std::int16_t bearing_in_driving_direction =
|
||||||
util::reverseBearing(std::round(bearings.first));
|
util::bearing::reverse(std::round(bearings.first));
|
||||||
maneuver = {intersection.location,
|
maneuver = {intersection.location,
|
||||||
bearing_in_driving_direction,
|
bearing_in_driving_direction,
|
||||||
bearings.second,
|
bearings.second,
|
||||||
@ -214,8 +214,9 @@ inline std::vector<RouteStep> assembleSteps(const datafacade::BaseDataFacade &fa
|
|||||||
BOOST_ASSERT(segment_index == number_of_segments - 1);
|
BOOST_ASSERT(segment_index == number_of_segments - 1);
|
||||||
bearings = detail::getArriveBearings(leg_geometry);
|
bearings = detail::getArriveBearings(leg_geometry);
|
||||||
|
|
||||||
intersection = {target_node.location,
|
intersection = {
|
||||||
std::vector<short>({static_cast<short>(util::reverseBearing(bearings.first))}),
|
target_node.location,
|
||||||
|
std::vector<short>({static_cast<short>(util::bearing::reverse(bearings.first))}),
|
||||||
std::vector<bool>({true}),
|
std::vector<bool>({true}),
|
||||||
0,
|
0,
|
||||||
IntermediateIntersection::NO_INDEX,
|
IntermediateIntersection::NO_INDEX,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#ifndef OSRM_EXTRACTOR_GEOJSON_DEBUG_POLICIES
|
#ifndef OSRM_EXTRACTOR_GEOJSON_DEBUG_POLICIES
|
||||||
#define OSRM_EXTRACTOR_GEOJSON_DEBUG_POLICIES
|
#define OSRM_EXTRACTOR_GEOJSON_DEBUG_POLICIES
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "extractor/query_node.hpp"
|
#include "extractor/query_node.hpp"
|
||||||
@ -11,6 +12,8 @@
|
|||||||
|
|
||||||
#include "extractor/guidance/coordinate_extractor.hpp"
|
#include "extractor/guidance/coordinate_extractor.hpp"
|
||||||
#include "extractor/guidance/intersection.hpp"
|
#include "extractor/guidance/intersection.hpp"
|
||||||
|
#include "util/coordinate.hpp"
|
||||||
|
#include "util/geojson_debug_policy_toolkit.hpp"
|
||||||
|
|
||||||
#include <boost/optional.hpp>
|
#include <boost/optional.hpp>
|
||||||
|
|
||||||
@ -18,8 +21,9 @@ namespace osrm
|
|||||||
{
|
{
|
||||||
namespace extractor
|
namespace extractor
|
||||||
{
|
{
|
||||||
|
|
||||||
// generate a visualisation of an intersection, printing the coordinates used for angle calculation
|
// generate a visualisation of an intersection, printing the coordinates used for angle calculation
|
||||||
struct IntersectionPrinter
|
template <typename IntersectionType> struct IntersectionPrinter
|
||||||
{
|
{
|
||||||
IntersectionPrinter(const util::NodeBasedDynamicGraph &node_based_graph,
|
IntersectionPrinter(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||||
const std::vector<extractor::QueryNode> &node_coordinates,
|
const std::vector<extractor::QueryNode> &node_coordinates,
|
||||||
@ -28,7 +32,7 @@ struct IntersectionPrinter
|
|||||||
// renders the used coordinate locations for all entries/as well as the resulting
|
// renders the used coordinate locations for all entries/as well as the resulting
|
||||||
// intersection-classification
|
// intersection-classification
|
||||||
util::json::Array operator()(const NodeID intersection_node,
|
util::json::Array operator()(const NodeID intersection_node,
|
||||||
const extractor::guidance::Intersection &intersection,
|
const IntersectionType &intersection,
|
||||||
const boost::optional<util::json::Object> &node_style = {},
|
const boost::optional<util::json::Object> &node_style = {},
|
||||||
const boost::optional<util::json::Object> &way_style = {}) const;
|
const boost::optional<util::json::Object> &way_style = {}) const;
|
||||||
|
|
||||||
@ -37,6 +41,66 @@ struct IntersectionPrinter
|
|||||||
const extractor::guidance::CoordinateExtractor &coordinate_extractor;
|
const extractor::guidance::CoordinateExtractor &coordinate_extractor;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// IMPLEMENTATION
|
||||||
|
template <typename IntersectionType>
|
||||||
|
IntersectionPrinter<IntersectionType>::IntersectionPrinter(
|
||||||
|
const util::NodeBasedDynamicGraph &node_based_graph,
|
||||||
|
const std::vector<extractor::QueryNode> &node_coordinates,
|
||||||
|
const extractor::guidance::CoordinateExtractor &coordinate_extractor)
|
||||||
|
: node_based_graph(node_based_graph), node_coordinates(node_coordinates),
|
||||||
|
coordinate_extractor(coordinate_extractor)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IntersectionType>
|
||||||
|
util::json::Array IntersectionPrinter<IntersectionType>::
|
||||||
|
operator()(const NodeID intersection_node,
|
||||||
|
const IntersectionType &intersection,
|
||||||
|
const boost::optional<util::json::Object> &node_style,
|
||||||
|
const boost::optional<util::json::Object> &way_style) const
|
||||||
|
{
|
||||||
|
// request the number of lanes. This process needs to be in sync with what happens over at
|
||||||
|
// intersection_generator
|
||||||
|
const auto intersection_lanes =
|
||||||
|
intersection.FindMaximum(guidance::makeExtractLanesForRoad(node_based_graph));
|
||||||
|
|
||||||
|
std::vector<util::Coordinate> coordinates;
|
||||||
|
coordinates.reserve(intersection.size());
|
||||||
|
coordinates.push_back(node_coordinates[intersection_node]);
|
||||||
|
|
||||||
|
const auto road_to_coordinate = [&](const auto &road) {
|
||||||
|
const constexpr auto FORWARD = false;
|
||||||
|
const auto to_node = node_based_graph.GetTarget(road.eid);
|
||||||
|
return coordinate_extractor.GetCoordinateAlongRoad(
|
||||||
|
intersection_node, road.eid, FORWARD, to_node, intersection_lanes);
|
||||||
|
};
|
||||||
|
|
||||||
|
std::transform(intersection.begin(),
|
||||||
|
intersection.end(),
|
||||||
|
std::back_inserter(coordinates),
|
||||||
|
road_to_coordinate);
|
||||||
|
|
||||||
|
util::json::Array features;
|
||||||
|
features.values.push_back(
|
||||||
|
util::makeFeature("MultiPoint", makeJsonArray(coordinates), node_style));
|
||||||
|
|
||||||
|
if (coordinates.size() > 1)
|
||||||
|
{
|
||||||
|
std::vector<util::Coordinate> line_coordinates(2);
|
||||||
|
line_coordinates[0] = coordinates.front();
|
||||||
|
const auto coordinate_to_line = [&](const util::Coordinate coordinate) {
|
||||||
|
line_coordinates[1] = coordinate;
|
||||||
|
return util::makeFeature("LineString", makeJsonArray(line_coordinates), way_style);
|
||||||
|
};
|
||||||
|
|
||||||
|
std::transform(std::next(coordinates.begin()),
|
||||||
|
coordinates.end(),
|
||||||
|
std::back_inserter(features.values),
|
||||||
|
coordinate_to_line);
|
||||||
|
}
|
||||||
|
return features;
|
||||||
|
}
|
||||||
|
|
||||||
} /* namespace extractor */
|
} /* namespace extractor */
|
||||||
} /* namespace osrm */
|
} /* namespace osrm */
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ const bool constexpr INVERT = true;
|
|||||||
|
|
||||||
// what angle is interpreted as going straight
|
// what angle is interpreted as going straight
|
||||||
const double constexpr STRAIGHT_ANGLE = 180.;
|
const double constexpr STRAIGHT_ANGLE = 180.;
|
||||||
|
const double constexpr ORTHOGONAL_ANGLE = 90.;
|
||||||
// if a turn deviates this much from going straight, it will be kept straight
|
// if a turn deviates this much from going straight, it will be kept straight
|
||||||
const double constexpr MAXIMAL_ALLOWED_NO_TURN_DEVIATION = 3.;
|
const double constexpr MAXIMAL_ALLOWED_NO_TURN_DEVIATION = 3.;
|
||||||
// angle that lies between two nearly indistinguishable roads
|
// angle that lies between two nearly indistinguishable roads
|
||||||
@ -36,6 +37,12 @@ const int constexpr MAX_SLIPROAD_THRESHOLD = 250;
|
|||||||
// category).
|
// category).
|
||||||
const double constexpr PRIORITY_DISTINCTION_FACTOR = 1.75;
|
const double constexpr PRIORITY_DISTINCTION_FACTOR = 1.75;
|
||||||
|
|
||||||
|
// the lane width we assume for a single lane
|
||||||
|
const auto constexpr ASSUMED_LANE_WIDTH = 3.25;
|
||||||
|
|
||||||
|
// how far apart can roads be at the most, when thinking about merging them?
|
||||||
|
const auto constexpr MERGABLE_ANGLE_DIFFERENCE = 95.0;
|
||||||
|
|
||||||
} // namespace guidance
|
} // namespace guidance
|
||||||
} // namespace extractor
|
} // namespace extractor
|
||||||
} // namespace osrm
|
} // namespace osrm
|
||||||
|
@ -8,15 +8,15 @@
|
|||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "extractor/guidance/turn_instruction.hpp"
|
|
||||||
#include "util/bearing.hpp"
|
#include "util/bearing.hpp"
|
||||||
#include "util/node_based_graph.hpp"
|
#include "util/node_based_graph.hpp"
|
||||||
#include "util/typedefs.hpp" // EdgeID
|
#include "util/typedefs.hpp" // EdgeID
|
||||||
|
|
||||||
#include <boost/range/adaptor/transformed.hpp>
|
#include "extractor/guidance/turn_instruction.hpp"
|
||||||
#include <boost/range/algorithm/find_if.hpp>
|
|
||||||
|
|
||||||
#include <boost/assert.hpp>
|
#include <boost/range/algorithm/min_element.hpp>
|
||||||
|
#include <boost/range/algorithm/find_if.hpp>
|
||||||
|
#include <boost/range/algorithm/count_if.hpp>
|
||||||
|
|
||||||
namespace osrm
|
namespace osrm
|
||||||
{
|
{
|
||||||
@ -36,8 +36,8 @@ struct IntersectionShapeData
|
|||||||
inline auto makeCompareShapeDataByBearing(const double base_bearing)
|
inline auto makeCompareShapeDataByBearing(const double base_bearing)
|
||||||
{
|
{
|
||||||
return [base_bearing](const auto &lhs, const auto &rhs) {
|
return [base_bearing](const auto &lhs, const auto &rhs) {
|
||||||
return util::angleBetweenBearings(base_bearing, lhs.bearing) <
|
return util::bearing::angleBetween(lhs.bearing, base_bearing) <
|
||||||
util::angleBetweenBearings(base_bearing, rhs.bearing);
|
util::bearing::angleBetween(rhs.bearing, base_bearing);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,6 +48,13 @@ inline auto makeCompareAngularDeviation(const double angle)
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline auto makeExtractLanesForRoad(const util::NodeBasedDynamicGraph &node_based_graph)
|
||||||
|
{
|
||||||
|
return [&node_based_graph](const auto &road) {
|
||||||
|
return node_based_graph.GetEdgeData(road.eid).road_classification.GetNumberOfLanes();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// When viewing an intersection from an incoming edge, we can transform a shape into a view which
|
// When viewing an intersection from an incoming edge, we can transform a shape into a view which
|
||||||
// gives additional information on angles and whether a turn is allowed
|
// gives additional information on angles and whether a turn is allowed
|
||||||
struct IntersectionViewData : IntersectionShapeData
|
struct IntersectionViewData : IntersectionShapeData
|
||||||
@ -108,11 +115,60 @@ struct ConnectedRoad final : IntersectionViewData
|
|||||||
};
|
};
|
||||||
|
|
||||||
// small helper function to print the content of a connected road
|
// small helper function to print the content of a connected road
|
||||||
|
std::string toString(const IntersectionShapeData &shape);
|
||||||
|
std::string toString(const IntersectionViewData &view);
|
||||||
std::string toString(const ConnectedRoad &road);
|
std::string toString(const ConnectedRoad &road);
|
||||||
|
|
||||||
// Intersections are sorted roads: [0] being the UTurn road, then from sharp right to sharp left.
|
// Intersections are sorted roads: [0] being the UTurn road, then from sharp right to sharp left.
|
||||||
|
// common operations shared amongst all intersection types
|
||||||
|
template <typename Self> struct EnableShapeOps
|
||||||
|
{
|
||||||
|
// same as closest turn, but for bearings
|
||||||
|
auto FindClosestBearing(double bearing) const
|
||||||
|
{
|
||||||
|
auto comp = makeCompareShapeDataByBearing(bearing);
|
||||||
|
return std::min_element(self()->begin(), self()->end(), comp);
|
||||||
|
}
|
||||||
|
|
||||||
using IntersectionShape = std::vector<IntersectionShapeData>;
|
// search a given eid in the intersection
|
||||||
|
auto FindEid(const EdgeID eid) const
|
||||||
|
{
|
||||||
|
return boost::range::find_if(
|
||||||
|
*self(), [eid](const auto &road) { return road.eid == eid; });
|
||||||
|
}
|
||||||
|
|
||||||
|
// find the maximum value based on a conversion operator
|
||||||
|
template <typename UnaryProjection> auto FindMaximum(UnaryProjection converter) const
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(!self()->empty());
|
||||||
|
auto initial = converter(self()->front());
|
||||||
|
|
||||||
|
const auto extract_maximal_value = [&initial, converter](const auto &road) {
|
||||||
|
initial = std::max(initial, converter(road));
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
boost::range::find_if(*self(), extract_maximal_value);
|
||||||
|
return initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find the maximum value based on a conversion operator and a predefined initial value
|
||||||
|
template <typename UnaryPredicate> auto Count(UnaryPredicate detector) const
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(!self()->empty());
|
||||||
|
return boost::range::count_if(*self(), detector);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
auto self() { return static_cast<Self *>(this); }
|
||||||
|
auto self() const { return static_cast<const Self *>(this); }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct IntersectionShape final : std::vector<IntersectionShapeData>, //
|
||||||
|
EnableShapeOps<IntersectionShape> //
|
||||||
|
{
|
||||||
|
using Base = std::vector<IntersectionShapeData>;
|
||||||
|
};
|
||||||
|
|
||||||
// Common operations shared among IntersectionView and Intersections.
|
// Common operations shared among IntersectionView and Intersections.
|
||||||
// Inherit to enable those operations on your compatible type. CRTP pattern.
|
// Inherit to enable those operations on your compatible type. CRTP pattern.
|
||||||
@ -123,12 +179,13 @@ template <typename Self> struct EnableIntersectionOps
|
|||||||
auto findClosestTurn(double angle) const
|
auto findClosestTurn(double angle) const
|
||||||
{
|
{
|
||||||
auto comp = makeCompareAngularDeviation(angle);
|
auto comp = makeCompareAngularDeviation(angle);
|
||||||
return std::min_element(self()->begin(), self()->end(), comp);
|
return boost::range::min_element(*self(), comp);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check validity of the intersection object. We assume a few basic properties every set of
|
/* Check validity of the intersection object. We assume a few basic properties every set of
|
||||||
// connected roads should follow throughout guidance pre-processing. This utility function
|
* connected roads should follow throughout guidance pre-processing. This utility function
|
||||||
// allows checking intersections for validity
|
* allows checking intersections for validity
|
||||||
|
*/
|
||||||
auto valid() const
|
auto valid() const
|
||||||
{
|
{
|
||||||
if (self()->empty())
|
if (self()->empty())
|
||||||
@ -149,26 +206,6 @@ template <typename Self> struct EnableIntersectionOps
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Given all possible turns which is the highest connected number of lanes per turn.
|
|
||||||
// This value is used for example during generation of intersections.
|
|
||||||
auto getHighestConnectedLaneCount(const util::NodeBasedDynamicGraph &graph) const
|
|
||||||
{
|
|
||||||
const std::function<std::uint8_t(const ConnectedRoad &)> to_lane_count =
|
|
||||||
[&](const ConnectedRoad &road) {
|
|
||||||
return graph.GetEdgeData(road.eid).road_classification.GetNumberOfLanes();
|
|
||||||
};
|
|
||||||
|
|
||||||
std::uint8_t max_lanes = 0;
|
|
||||||
const auto extract_maximal_value = [&max_lanes](std::uint8_t value) {
|
|
||||||
max_lanes = std::max(max_lanes, value);
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto view = *self() | boost::adaptors::transformed(to_lane_count);
|
|
||||||
boost::range::find_if(view, extract_maximal_value);
|
|
||||||
return max_lanes;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the UTurn road we took to arrive at this intersection.
|
// Returns the UTurn road we took to arrive at this intersection.
|
||||||
const auto &getUTurnRoad() const { return self()->operator[](0); }
|
const auto &getUTurnRoad() const { return self()->operator[](0); }
|
||||||
|
|
||||||
@ -191,31 +228,52 @@ template <typename Self> struct EnableIntersectionOps
|
|||||||
auto isDeadEnd() const
|
auto isDeadEnd() const
|
||||||
{
|
{
|
||||||
auto pred = [](const auto &road) { return road.entry_allowed; };
|
auto pred = [](const auto &road) { return road.entry_allowed; };
|
||||||
return !std::any_of(self()->begin() + 1, self()->end(), pred);
|
return std::none_of(self()->begin() + 1, self()->end(), pred);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the number of roads we can enter at this intersection, respectively.
|
// Returns the number of roads we can enter at this intersection, respectively.
|
||||||
auto countEnterable() const
|
auto countEnterable() const
|
||||||
{
|
{
|
||||||
auto pred = [](const auto &road) { return road.entry_allowed; };
|
auto pred = [](const auto &road) { return road.entry_allowed; };
|
||||||
return std::count_if(self()->begin(), self()->end(), pred);
|
return boost::range::count_if(*self(), pred);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the number of roads we can not enter at this intersection, respectively.
|
// Returns the number of roads we can not enter at this intersection, respectively.
|
||||||
auto countNonEnterable() const { return self()->size() - self()->countEnterable(); }
|
auto countNonEnterable() const { return self()->size() - self()->countEnterable(); }
|
||||||
|
|
||||||
|
// same as find closests turn but with an additional predicate to allow filtering
|
||||||
|
// the filter has to return `true` for elements that should be ignored
|
||||||
|
template <typename UnaryPredicate>
|
||||||
|
auto findClosestTurn(const double angle, const UnaryPredicate filter) const
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(!self()->empty());
|
||||||
|
const auto candidate = boost::range::min_element(
|
||||||
|
*self(), [angle, &filter](const auto &lhs, const auto &rhs) {
|
||||||
|
const auto filtered_lhs = filter(lhs), filtered_rhs = filter(rhs);
|
||||||
|
const auto deviation_lhs = util::angularDeviation(lhs.angle, angle),
|
||||||
|
deviation_rhs = util::angularDeviation(rhs.angle, angle);
|
||||||
|
return std::tie(filtered_lhs, deviation_lhs) <
|
||||||
|
std::tie(filtered_rhs, deviation_rhs);
|
||||||
|
});
|
||||||
|
|
||||||
|
// make sure only to return valid elements
|
||||||
|
return filter(*candidate) ? self()->end() : candidate;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
auto self() { return static_cast<Self *>(this); }
|
auto self() { return static_cast<Self *>(this); }
|
||||||
auto self() const { return static_cast<const Self *>(this); }
|
auto self() const { return static_cast<const Self *>(this); }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct IntersectionView final : std::vector<IntersectionViewData>, //
|
struct IntersectionView final : std::vector<IntersectionViewData>, //
|
||||||
|
EnableShapeOps<IntersectionView>, //
|
||||||
EnableIntersectionOps<IntersectionView> //
|
EnableIntersectionOps<IntersectionView> //
|
||||||
{
|
{
|
||||||
using Base = std::vector<IntersectionViewData>;
|
using Base = std::vector<IntersectionViewData>;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Intersection final : std::vector<ConnectedRoad>, //
|
struct Intersection final : std::vector<ConnectedRoad>, //
|
||||||
|
EnableShapeOps<Intersection>, //
|
||||||
EnableIntersectionOps<Intersection> //
|
EnableIntersectionOps<Intersection> //
|
||||||
{
|
{
|
||||||
using Base = std::vector<ConnectedRoad>;
|
using Base = std::vector<ConnectedRoad>;
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include "extractor/compressed_edge_container.hpp"
|
#include "extractor/compressed_edge_container.hpp"
|
||||||
#include "extractor/guidance/coordinate_extractor.hpp"
|
#include "extractor/guidance/coordinate_extractor.hpp"
|
||||||
#include "extractor/guidance/intersection.hpp"
|
#include "extractor/guidance/intersection.hpp"
|
||||||
|
#include "extractor/guidance/intersection_normalization_operation.hpp"
|
||||||
#include "extractor/query_node.hpp"
|
#include "extractor/query_node.hpp"
|
||||||
#include "extractor/restriction_map.hpp"
|
#include "extractor/restriction_map.hpp"
|
||||||
#include "util/attributes.hpp"
|
#include "util/attributes.hpp"
|
||||||
@ -22,6 +23,13 @@ namespace extractor
|
|||||||
{
|
{
|
||||||
namespace guidance
|
namespace guidance
|
||||||
{
|
{
|
||||||
|
|
||||||
|
struct IntersectionGenerationParameters
|
||||||
|
{
|
||||||
|
NodeID nid;
|
||||||
|
EdgeID via_eid;
|
||||||
|
};
|
||||||
|
|
||||||
// The Intersection Generator is given a turn location and generates an intersection representation
|
// The Intersection Generator is given a turn location and generates an intersection representation
|
||||||
// from it. For this all turn possibilities are analysed.
|
// from it. For this all turn possibilities are analysed.
|
||||||
// We consider turn restrictions to indicate possible turns. U-turns are generated based on profile
|
// We consider turn restrictions to indicate possible turns. U-turns are generated based on profile
|
||||||
@ -63,7 +71,7 @@ class IntersectionGenerator
|
|||||||
// more than a single next road. This function skips over degree two nodes to find coorect input
|
// more than a single next road. This function skips over degree two nodes to find coorect input
|
||||||
// for GetConnectedRoads.
|
// for GetConnectedRoads.
|
||||||
OSRM_ATTR_WARN_UNUSED
|
OSRM_ATTR_WARN_UNUSED
|
||||||
std::pair<NodeID, EdgeID> SkipDegreeTwoNodes(const NodeID starting_node,
|
IntersectionGenerationParameters SkipDegreeTwoNodes(const NodeID starting_node,
|
||||||
const EdgeID via_edge) const;
|
const EdgeID via_edge) const;
|
||||||
|
|
||||||
// Allow access to the coordinate extractor for all owners
|
// Allow access to the coordinate extractor for all owners
|
||||||
@ -73,7 +81,7 @@ class IntersectionGenerator
|
|||||||
// the node reached from `from_node` via `via_eid`. The resulting candidates have to be analysed
|
// the node reached from `from_node` via `via_eid`. The resulting candidates have to be analysed
|
||||||
// for their actual instructions later on.
|
// for their actual instructions later on.
|
||||||
// The switch for `use_low_precision_angles` enables a faster mode that will procude less
|
// The switch for `use_low_precision_angles` enables a faster mode that will procude less
|
||||||
// accurate coordinates. It should be good enough to check order of turns, find striaghtmost
|
// accurate coordinates. It should be good enough to check order of turns, find straightmost
|
||||||
// turns. Even good enough to do some simple angle verifications. It is mostly available to
|
// turns. Even good enough to do some simple angle verifications. It is mostly available to
|
||||||
// allow for faster graph traversal in the extraction phase.
|
// allow for faster graph traversal in the extraction phase.
|
||||||
OSRM_ATTR_WARN_UNUSED
|
OSRM_ATTR_WARN_UNUSED
|
||||||
@ -98,7 +106,7 @@ class IntersectionGenerator
|
|||||||
const EdgeID entering_via_edge,
|
const EdgeID entering_via_edge,
|
||||||
const IntersectionShape &normalised_intersection,
|
const IntersectionShape &normalised_intersection,
|
||||||
const IntersectionShape &intersection,
|
const IntersectionShape &intersection,
|
||||||
const std::vector<std::pair<EdgeID, EdgeID>> &merging_map) const;
|
const std::vector<IntersectionNormalizationOperation> &merging_map) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const util::NodeBasedDynamicGraph &node_based_graph;
|
const util::NodeBasedDynamicGraph &node_based_graph;
|
||||||
|
@ -508,15 +508,15 @@ std::size_t IntersectionHandler::findObviousTurn(const EdgeID via_edge,
|
|||||||
// even reverse the direction. Since we don't want to compute actual turns but simply
|
// even reverse the direction. Since we don't want to compute actual turns but simply
|
||||||
// try to find whether there is a turn going to the opposite direction of our obvious
|
// try to find whether there is a turn going to the opposite direction of our obvious
|
||||||
// turn, this should be alright.
|
// turn, this should be alright.
|
||||||
NodeID new_node;
|
const auto previous_intersection = [&]() -> IntersectionView {
|
||||||
const auto previous_intersection = [&]() {
|
const auto parameters = intersection_generator.SkipDegreeTwoNodes(
|
||||||
EdgeID turn_edge;
|
|
||||||
std::tie(new_node, turn_edge) = intersection_generator.SkipDegreeTwoNodes(
|
|
||||||
node_at_intersection, intersection[0].eid);
|
node_at_intersection, intersection[0].eid);
|
||||||
return intersection_generator.GetConnectedRoads(new_node, turn_edge);
|
if (node_based_graph.GetTarget(parameters.via_eid) == node_at_intersection)
|
||||||
|
return {};
|
||||||
|
return intersection_generator.GetConnectedRoads(parameters.nid, parameters.via_eid);
|
||||||
}();
|
}();
|
||||||
|
|
||||||
if (new_node != node_at_intersection)
|
if (!previous_intersection.empty())
|
||||||
{
|
{
|
||||||
const auto continue_road = intersection[best_continue];
|
const auto continue_road = intersection[best_continue];
|
||||||
for (const auto &comparison_road : previous_intersection)
|
for (const auto &comparison_road : previous_intersection)
|
||||||
|
@ -0,0 +1,25 @@
|
|||||||
|
#ifndef OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_NORMALIZATION_OPERATION_HPP_
|
||||||
|
#define OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_NORMALIZATION_OPERATION_HPP_
|
||||||
|
|
||||||
|
#include "util/typedefs.hpp"
|
||||||
|
|
||||||
|
namespace osrm
|
||||||
|
{
|
||||||
|
namespace extractor
|
||||||
|
{
|
||||||
|
namespace guidance
|
||||||
|
{
|
||||||
|
|
||||||
|
struct IntersectionNormalizationOperation
|
||||||
|
{
|
||||||
|
// the source of the merge, not part of the intersection after the merge is performed.
|
||||||
|
EdgeID merged_eid;
|
||||||
|
// the edge that is covering the `merged_eid`
|
||||||
|
EdgeID into_eid;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace guidance
|
||||||
|
} // namespace extractor
|
||||||
|
} // namespace osrm
|
||||||
|
|
||||||
|
#endif /*OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_NORMALIZATION_OPERATION_HPP_*/
|
@ -1,17 +1,17 @@
|
|||||||
#ifndef OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_NORMALIZER_HPP_
|
#ifndef OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_NORMALIZER_HPP_
|
||||||
#define OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_NORMALIZER_HPP_
|
#define OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_NORMALIZER_HPP_
|
||||||
|
|
||||||
#include "util/typedefs.hpp"
|
|
||||||
|
|
||||||
#include "util/attributes.hpp"
|
#include "util/attributes.hpp"
|
||||||
|
#include "util/name_table.hpp"
|
||||||
|
#include "util/typedefs.hpp"
|
||||||
|
|
||||||
#include "extractor/guidance/coordinate_extractor.hpp"
|
#include "extractor/guidance/coordinate_extractor.hpp"
|
||||||
#include "extractor/guidance/intersection.hpp"
|
#include "extractor/guidance/intersection.hpp"
|
||||||
#include "extractor/guidance/intersection_generator.hpp"
|
#include "extractor/guidance/intersection_generator.hpp"
|
||||||
|
#include "extractor/guidance/intersection_normalization_operation.hpp"
|
||||||
|
#include "extractor/guidance/mergable_road_detector.hpp"
|
||||||
#include "extractor/query_node.hpp"
|
#include "extractor/query_node.hpp"
|
||||||
|
|
||||||
#include "extractor/suffix_table.hpp"
|
#include "extractor/suffix_table.hpp"
|
||||||
#include "util/name_table.hpp"
|
|
||||||
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -37,6 +37,11 @@ namespace guidance
|
|||||||
class IntersectionNormalizer
|
class IntersectionNormalizer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
struct NormalizationResult
|
||||||
|
{
|
||||||
|
IntersectionShape normalized_shape;
|
||||||
|
std::vector<IntersectionNormalizationOperation> performed_merges;
|
||||||
|
};
|
||||||
IntersectionNormalizer(const util::NodeBasedDynamicGraph &node_based_graph,
|
IntersectionNormalizer(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||||
const std::vector<extractor::QueryNode> &node_coordinates,
|
const std::vector<extractor::QueryNode> &node_coordinates,
|
||||||
const util::NameTable &name_table,
|
const util::NameTable &name_table,
|
||||||
@ -46,16 +51,13 @@ class IntersectionNormalizer
|
|||||||
// The function takes an intersection an converts it to a `perceived` intersection which closer
|
// The function takes an intersection an converts it to a `perceived` intersection which closer
|
||||||
// represents how a human might experience the intersection
|
// represents how a human might experience the intersection
|
||||||
OSRM_ATTR_WARN_UNUSED
|
OSRM_ATTR_WARN_UNUSED
|
||||||
std::pair<IntersectionShape, std::vector<std::pair<EdgeID, EdgeID>>>
|
NormalizationResult operator()(const NodeID node_at_intersection,
|
||||||
operator()(const NodeID node_at_intersection, IntersectionShape intersection) const;
|
IntersectionShape intersection) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const util::NodeBasedDynamicGraph &node_based_graph;
|
const util::NodeBasedDynamicGraph &node_based_graph;
|
||||||
const std::vector<extractor::QueryNode> &node_coordinates;
|
|
||||||
const util::NameTable &name_table;
|
|
||||||
const SuffixTable &street_name_suffix_table;
|
|
||||||
|
|
||||||
const IntersectionGenerator &intersection_generator;
|
const IntersectionGenerator &intersection_generator;
|
||||||
|
const MergableRoadDetector mergable_road_detector;
|
||||||
|
|
||||||
/* check if two indices in an intersection can be seen as a single road in the perceived
|
/* check if two indices in an intersection can be seen as a single road in the perceived
|
||||||
* intersection representation. See below for an example. Utility function for
|
* intersection representation. See below for an example. Utility function for
|
||||||
@ -73,12 +75,15 @@ class IntersectionNormalizer
|
|||||||
std::size_t first_index,
|
std::size_t first_index,
|
||||||
std::size_t second_index) const;
|
std::size_t second_index) const;
|
||||||
|
|
||||||
// A tool called by CanMerge. It checks whether two indices can be merged, not concerned without
|
// Perform an Actual Merge
|
||||||
// remaining parts of the intersection.
|
IntersectionNormalizationOperation
|
||||||
bool InnerCanMerge(const NodeID intersection_node,
|
DetermineMergeDirection(const IntersectionShapeData &lhs,
|
||||||
const IntersectionShape &intersection,
|
const IntersectionShapeData &rhs) const;
|
||||||
std::size_t first_index,
|
IntersectionShapeData MergeRoads(const IntersectionShapeData &destination,
|
||||||
std::size_t second_index) const;
|
const IntersectionShapeData &source) const;
|
||||||
|
IntersectionShapeData MergeRoads(const IntersectionNormalizationOperation direction,
|
||||||
|
const IntersectionShapeData &lhs,
|
||||||
|
const IntersectionShapeData &rhs) const;
|
||||||
|
|
||||||
// Merge segregated roads to omit invalid turns in favor of treating segregated roads as
|
// Merge segregated roads to omit invalid turns in favor of treating segregated roads as
|
||||||
// one.
|
// one.
|
||||||
@ -92,8 +97,8 @@ class IntersectionNormalizer
|
|||||||
// The treatment results in a straight turn angle of 180º rather than a turn angle of approx
|
// The treatment results in a straight turn angle of 180º rather than a turn angle of approx
|
||||||
// 160
|
// 160
|
||||||
OSRM_ATTR_WARN_UNUSED
|
OSRM_ATTR_WARN_UNUSED
|
||||||
std::pair<IntersectionShape, std::vector<std::pair<EdgeID, EdgeID>>>
|
NormalizationResult MergeSegregatedRoads(const NodeID intersection_node,
|
||||||
MergeSegregatedRoads(const NodeID intersection_node, IntersectionShape intersection) const;
|
IntersectionShape intersection) const;
|
||||||
|
|
||||||
// The counterpiece to mergeSegregatedRoads. While we can adjust roads that split up at the
|
// The counterpiece to mergeSegregatedRoads. While we can adjust roads that split up at the
|
||||||
// intersection itself, it can also happen that intersections are connected to joining roads.
|
// intersection itself, it can also happen that intersections are connected to joining roads.
|
||||||
|
155
include/extractor/guidance/mergable_road_detector.hpp
Normal file
155
include/extractor/guidance/mergable_road_detector.hpp
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
#ifndef OSRM_EXTRACTOR_GUIDANCE_MERGEABLE_ROADS
|
||||||
|
#define OSRM_EXTRACTOR_GUIDANCE_MERGEABLE_ROADS
|
||||||
|
|
||||||
|
#include "extractor/guidance/intersection.hpp"
|
||||||
|
#include "util/node_based_graph.hpp"
|
||||||
|
#include "util/typedefs.hpp"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <functional>
|
||||||
|
#include <limits>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace osrm
|
||||||
|
{
|
||||||
|
|
||||||
|
//FWD declarations
|
||||||
|
namespace util
|
||||||
|
{
|
||||||
|
class NameTable;
|
||||||
|
} // namespace util
|
||||||
|
|
||||||
|
namespace extractor
|
||||||
|
{
|
||||||
|
|
||||||
|
struct QueryNode;
|
||||||
|
class SuffixTable;
|
||||||
|
|
||||||
|
namespace guidance
|
||||||
|
{
|
||||||
|
class IntersectionGenerator;
|
||||||
|
class CoordinateExtractor;
|
||||||
|
|
||||||
|
|
||||||
|
class MergableRoadDetector
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// in case we have to change the mode we are operating on
|
||||||
|
using MergableRoadData = IntersectionShapeData;
|
||||||
|
|
||||||
|
MergableRoadDetector(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||||
|
const std::vector<QueryNode> &node_coordinates,
|
||||||
|
const IntersectionGenerator &intersection_generator,
|
||||||
|
const CoordinateExtractor &coordinate_extractor,
|
||||||
|
const util::NameTable &name_table,
|
||||||
|
const SuffixTable &street_name_suffix_table);
|
||||||
|
|
||||||
|
// OSM ways tend to be modelled as separate ways for different directions. This is often due to
|
||||||
|
// small gras strips in the middle between the two directions or due to pedestrian islands at
|
||||||
|
// intersections.
|
||||||
|
//
|
||||||
|
// To reduce unnecessary information due to these artificial intersections (which are not
|
||||||
|
// actually perceived as such) we try and merge these for our internal representation to both
|
||||||
|
// get better perceived turn angles and get a better reprsentation of our intersections in
|
||||||
|
// general.
|
||||||
|
//
|
||||||
|
// i h i,h
|
||||||
|
// | | |
|
||||||
|
// | | |
|
||||||
|
// b - - - v - - - g |
|
||||||
|
// > a < is transformed into: b,c - - - a - - - g,f
|
||||||
|
// c - - - ^ - - - f |
|
||||||
|
// | | |
|
||||||
|
// | | |
|
||||||
|
// d e d,e
|
||||||
|
bool CanMergeRoad(const NodeID intersection_node,
|
||||||
|
const MergableRoadData &lhs,
|
||||||
|
const MergableRoadData &rhs) const;
|
||||||
|
|
||||||
|
// check if a road cannot influence the merging of the other. This is necessary to prevent
|
||||||
|
// situations with more than two roads that could participate in a merge
|
||||||
|
bool IsDistinctFrom(const MergableRoadData &lhs, const MergableRoadData &rhs) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// check if two name ids can be seen as identical (in presence of refs/others)
|
||||||
|
// in our case this translates into no name announcement in either direction (lhs->rhs and
|
||||||
|
// rhs->lhs)
|
||||||
|
bool HaveIdenticalNames(const NameID lhs, const NameID rhs) const;
|
||||||
|
|
||||||
|
// When it comes to merging roads, we need to find out if two ways actually represent the
|
||||||
|
// same road. This check tries to identify roads which are the same road in opposite directions
|
||||||
|
bool EdgeDataSupportsMerge(const util::NodeBasedEdgeData &lhs_edge_data,
|
||||||
|
const util::NodeBasedEdgeData &rhs_edge_data) const;
|
||||||
|
|
||||||
|
// Detect traffic loops.
|
||||||
|
// Since OSRM cannot handle loop edges, we cannot directly see a connection between a node and
|
||||||
|
// itself. We need to skip at least a single node in between.
|
||||||
|
bool IsTrafficLoop(const NodeID intersection_node, const MergableRoadData &road) const;
|
||||||
|
|
||||||
|
// Detector to check if we are looking at roads splitting up just prior to entering an
|
||||||
|
// intersection:
|
||||||
|
//
|
||||||
|
// c
|
||||||
|
// / |
|
||||||
|
// a -< |
|
||||||
|
// \ |
|
||||||
|
// b
|
||||||
|
//
|
||||||
|
// A common scheme in OSRM is that roads spit up in separate ways when approaching an
|
||||||
|
// intersection. This detector tries to detect these narrow triangles which usually just offer a
|
||||||
|
// small island for pedestrians in the middle.
|
||||||
|
bool IsNarrowTriangle(const NodeID intersection_node,
|
||||||
|
const MergableRoadData &lhs,
|
||||||
|
const MergableRoadData &rhs) const;
|
||||||
|
|
||||||
|
// Detector to check for whether two roads are following the same direction.
|
||||||
|
// If roads don't end up right at a connected intersection, we could look at a situation like
|
||||||
|
//
|
||||||
|
// __________________________
|
||||||
|
// /
|
||||||
|
// ---
|
||||||
|
// \__________________________
|
||||||
|
//
|
||||||
|
// This detector tries to find out about whether two roads are parallel after the separation
|
||||||
|
bool HaveSameDirection(const NodeID intersection_node,
|
||||||
|
const MergableRoadData &lhs,
|
||||||
|
const MergableRoadData &rhs) const;
|
||||||
|
|
||||||
|
// Detector for small traffic islands. If a road is splitting up, just to connect again later,
|
||||||
|
// we don't wan't to have this information within our list of intersections/possible turn
|
||||||
|
// locations.
|
||||||
|
//
|
||||||
|
// ___________
|
||||||
|
// ---<___________>-----
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Would feel just like a single straight road to a driver and should be represented as such in
|
||||||
|
// our engine
|
||||||
|
bool IsTrafficIsland(const NodeID intersection_node,
|
||||||
|
const MergableRoadData &lhs,
|
||||||
|
const MergableRoadData &rhs) const;
|
||||||
|
|
||||||
|
// A negative detector, preventing a merge, trying to detect link roads between two main roads.
|
||||||
|
//
|
||||||
|
// d - - - - - - - - e - f
|
||||||
|
// . / '
|
||||||
|
// a - - - b - - - - - - c
|
||||||
|
//
|
||||||
|
// The detector wants to prevent merges that are connected to `b-e`
|
||||||
|
bool IsLinkRoad(const NodeID intersection_node, const MergableRoadData &road) const;
|
||||||
|
|
||||||
|
const util::NodeBasedDynamicGraph &node_based_graph;
|
||||||
|
const std::vector<QueryNode> &node_coordinates;
|
||||||
|
const IntersectionGenerator &intersection_generator;
|
||||||
|
const CoordinateExtractor &coordinate_extractor;
|
||||||
|
|
||||||
|
// name detection
|
||||||
|
const util::NameTable &name_table;
|
||||||
|
const SuffixTable &street_name_suffix_table;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace guidance
|
||||||
|
} // namespace extractor
|
||||||
|
} // namespace osrm
|
||||||
|
|
||||||
|
#endif
|
@ -57,7 +57,6 @@ struct LengthLimitedCoordinateAccumulator
|
|||||||
{
|
{
|
||||||
LengthLimitedCoordinateAccumulator(
|
LengthLimitedCoordinateAccumulator(
|
||||||
const extractor::guidance::CoordinateExtractor &coordinate_extractor,
|
const extractor::guidance::CoordinateExtractor &coordinate_extractor,
|
||||||
const util::NodeBasedDynamicGraph &node_based_graph,
|
|
||||||
const double max_length);
|
const double max_length);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -78,11 +77,12 @@ struct LengthLimitedCoordinateAccumulator
|
|||||||
*/
|
*/
|
||||||
void update(const NodeID from_node, const EdgeID via_edge, const NodeID to_node);
|
void update(const NodeID from_node, const EdgeID via_edge, const NodeID to_node);
|
||||||
|
|
||||||
const extractor::guidance::CoordinateExtractor &coordinate_extractor;
|
double accumulated_length = 0;
|
||||||
const util::NodeBasedDynamicGraph &node_based_graph;
|
|
||||||
const double max_length;
|
|
||||||
double accumulated_length;
|
|
||||||
std::vector<util::Coordinate> coordinates;
|
std::vector<util::Coordinate> coordinates;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const extractor::guidance::CoordinateExtractor &coordinate_extractor;
|
||||||
|
const double max_length;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -105,13 +105,40 @@ struct SelectRoadByNameOnlyChoiceAndStraightness
|
|||||||
*/
|
*/
|
||||||
boost::optional<EdgeID> operator()(const NodeID nid,
|
boost::optional<EdgeID> operator()(const NodeID nid,
|
||||||
const EdgeID via_edge_id,
|
const EdgeID via_edge_id,
|
||||||
const Intersection &intersection,
|
const IntersectionView &intersection,
|
||||||
const util::NodeBasedDynamicGraph &node_based_graph) const;
|
const util::NodeBasedDynamicGraph &node_based_graph) const;
|
||||||
|
|
||||||
|
private:
|
||||||
const NameID desired_name_id;
|
const NameID desired_name_id;
|
||||||
const bool requires_entry;
|
const bool requires_entry;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Following only a straight road
|
||||||
|
* Follow only the straightmost turn, as long as its the only choice or has the desired name
|
||||||
|
*/
|
||||||
|
struct SelectStraightmostRoadByNameAndOnlyChoice
|
||||||
|
{
|
||||||
|
SelectStraightmostRoadByNameAndOnlyChoice(const NameID desired_name_id,
|
||||||
|
const double initial_bearing,
|
||||||
|
const bool requires_entry);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* !! REQUIRED - Function for the use of TraverseRoad in the graph walker.
|
||||||
|
* The operator() needs to return (if any is found) the next road to continue in the graph
|
||||||
|
* traversal. If no such edge is found, return {} is allowed. Usually you want to choose some
|
||||||
|
* form of obious turn to follow.
|
||||||
|
*/
|
||||||
|
boost::optional<EdgeID> operator()(const NodeID nid,
|
||||||
|
const EdgeID via_edge_id,
|
||||||
|
const IntersectionView &intersection,
|
||||||
|
const util::NodeBasedDynamicGraph &node_based_graph) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const NameID desired_name_id;
|
||||||
|
const double initial_bearing;
|
||||||
|
const bool requires_entry;
|
||||||
|
};
|
||||||
|
|
||||||
// find the next intersection given a hop limit
|
// find the next intersection given a hop limit
|
||||||
struct IntersectionFinderAccumulator
|
struct IntersectionFinderAccumulator
|
||||||
{
|
{
|
||||||
@ -166,8 +193,9 @@ NodeBasedGraphWalker::TraverseRoad(NodeID current_node_id,
|
|||||||
return {};
|
return {};
|
||||||
|
|
||||||
// look at the next intersection
|
// look at the next intersection
|
||||||
const auto next_intersection =
|
const constexpr auto LOW_PRECISION = true;
|
||||||
intersection_generator.GetConnectedRoads(current_node_id, current_edge_id);
|
const auto next_intersection = intersection_generator.GetConnectedRoads(
|
||||||
|
current_node_id, current_edge_id, LOW_PRECISION);
|
||||||
|
|
||||||
// don't follow u-turns or go past our initial intersection
|
// don't follow u-turns or go past our initial intersection
|
||||||
if (next_intersection.size() <= 1)
|
if (next_intersection.size() <= 1)
|
||||||
@ -235,7 +263,7 @@ struct DistanceToNextIntersectionAccumulator
|
|||||||
using namespace util::coordinate_calculation;
|
using namespace util::coordinate_calculation;
|
||||||
|
|
||||||
const auto coords = extractor.GetForwardCoordinatesAlongRoad(start, onto);
|
const auto coords = extractor.GetForwardCoordinatesAlongRoad(start, onto);
|
||||||
distance += getLength(coords, &haversineDistance);
|
distance += getLength(coords.begin(), coords.end(), &haversineDistance);
|
||||||
}
|
}
|
||||||
|
|
||||||
const extractor::guidance::CoordinateExtractor &extractor;
|
const extractor::guidance::CoordinateExtractor &extractor;
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include "extractor/compressed_edge_container.hpp"
|
#include "extractor/compressed_edge_container.hpp"
|
||||||
#include "extractor/guidance/intersection.hpp"
|
#include "extractor/guidance/intersection.hpp"
|
||||||
#include "extractor/guidance/intersection_generator.hpp"
|
#include "extractor/guidance/intersection_generator.hpp"
|
||||||
|
#include "extractor/guidance/intersection_normalization_operation.hpp"
|
||||||
#include "extractor/guidance/intersection_normalizer.hpp"
|
#include "extractor/guidance/intersection_normalizer.hpp"
|
||||||
#include "extractor/guidance/motorway_handler.hpp"
|
#include "extractor/guidance/motorway_handler.hpp"
|
||||||
#include "extractor/guidance/roundabout_handler.hpp"
|
#include "extractor/guidance/roundabout_handler.hpp"
|
||||||
@ -62,11 +63,9 @@ class TurnAnalysis
|
|||||||
{
|
{
|
||||||
// the basic shape, containing all turns
|
// the basic shape, containing all turns
|
||||||
IntersectionShape intersection_shape;
|
IntersectionShape intersection_shape;
|
||||||
// normalised shape, merged some roads into others, adjusted bearings
|
// normalized shape, merged some roads into others, adjusted bearings
|
||||||
// see intersection_normaliser for further explanations
|
// see intersection_normalizer for further explanations
|
||||||
IntersectionShape normalised_intersection_shape;
|
IntersectionNormalizer::NormalizationResult annotated_normalized_shape;
|
||||||
// map containing information about which road was merged into which
|
|
||||||
std::vector<std::pair<EdgeID, EdgeID>> merging_map;
|
|
||||||
};
|
};
|
||||||
OSRM_ATTR_WARN_UNUSED
|
OSRM_ATTR_WARN_UNUSED
|
||||||
ShapeResult ComputeIntersectionShapes(const NodeID node_at_center_of_intersection) const;
|
ShapeResult ComputeIntersectionShapes(const NodeID node_at_center_of_intersection) const;
|
||||||
|
@ -91,9 +91,8 @@ inline bool CheckInBounds(const int A, const int B, const int range)
|
|||||||
return normalized_B - range <= normalized_A && normalized_A <= normalized_B + range;
|
return normalized_B - range <= normalized_A && normalized_A <= normalized_B + range;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // namespace bearing
|
|
||||||
|
|
||||||
inline double reverseBearing(const double bearing)
|
inline double reverse(const double bearing)
|
||||||
{
|
{
|
||||||
if (bearing >= 180)
|
if (bearing >= 180)
|
||||||
return bearing - 180.;
|
return bearing - 180.;
|
||||||
@ -119,8 +118,9 @@ inline double reverseBearing(const double bearing)
|
|||||||
// % 360;
|
// % 360;
|
||||||
// All other cases are handled by first rotating both bearings to an
|
// All other cases are handled by first rotating both bearings to an
|
||||||
// entry_bearing of 0.
|
// entry_bearing of 0.
|
||||||
inline double angleBetweenBearings(const double entry_bearing, const double exit_bearing)
|
inline double angleBetween(const double entry_bearing, const double exit_bearing)
|
||||||
{
|
{
|
||||||
|
// transform bearing from cw into ccw order
|
||||||
const double offset = 360 - entry_bearing;
|
const double offset = 360 - entry_bearing;
|
||||||
const double rotated_exit = [](double bearing, const double offset) {
|
const double rotated_exit = [](double bearing, const double offset) {
|
||||||
bearing += offset;
|
bearing += offset;
|
||||||
@ -131,13 +131,39 @@ inline double angleBetweenBearings(const double entry_bearing, const double exit
|
|||||||
return angle >= 360 ? angle - 360 : angle;
|
return angle >= 360 ? angle - 360 : angle;
|
||||||
}
|
}
|
||||||
|
|
||||||
// minimal difference between two angles/bearings going left or right
|
} // namespace bearing
|
||||||
inline double angularDeviation(const double angle, const double from)
|
|
||||||
|
// compute the minimum distance in degree between two angles/bearings
|
||||||
|
inline double angularDeviation(const double angle_or_bearing, const double from)
|
||||||
{
|
{
|
||||||
const double deviation = std::abs(angle - from);
|
const double deviation = std::abs(angle_or_bearing - from);
|
||||||
return std::min(360 - deviation, deviation);
|
return std::min(360 - deviation, deviation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Angles in OSRM are expressed in the range of [0,360). During calculations, we might violate
|
||||||
|
* this range via offsets. This function helps to ensure the range is kept. */
|
||||||
|
inline double restrictAngleToValidRange(const double angle)
|
||||||
|
{
|
||||||
|
if (angle < 0)
|
||||||
|
return restrictAngleToValidRange(angle + 360.);
|
||||||
|
else if (angle > 360)
|
||||||
|
return restrictAngleToValidRange(angle - 360.);
|
||||||
|
else
|
||||||
|
return angle;
|
||||||
|
}
|
||||||
|
|
||||||
|
// finds the angle between two angles, based on the minum difference between the two
|
||||||
|
inline double angleBetween(const double lhs, const double rhs)
|
||||||
|
{
|
||||||
|
const auto difference = std::abs(lhs - rhs);
|
||||||
|
const auto is_clockwise_difference = difference <= 180;
|
||||||
|
const auto angle_between_candidate = .5 * (lhs + rhs);
|
||||||
|
if (is_clockwise_difference)
|
||||||
|
return angle_between_candidate;
|
||||||
|
else
|
||||||
|
return restrictAngleToValidRange(angle_between_candidate + 180);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace util
|
} // namespace util
|
||||||
} // namespace osrm
|
} // namespace osrm
|
||||||
|
|
||||||
|
@ -3,9 +3,12 @@
|
|||||||
|
|
||||||
#include "util/coordinate.hpp"
|
#include "util/coordinate.hpp"
|
||||||
|
|
||||||
|
#include <boost/math/constants/constants.hpp>
|
||||||
#include <boost/optional.hpp>
|
#include <boost/optional.hpp>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
#include <numeric>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@ -23,6 +26,18 @@ const constexpr long double RAD_TO_DEGREE = 1. / DEGREE_TO_RAD;
|
|||||||
// earth radius varies between 6,356.750-6,378.135 km (3,949.901-3,963.189mi)
|
// earth radius varies between 6,356.750-6,378.135 km (3,949.901-3,963.189mi)
|
||||||
// The IUGG value for the equatorial radius is 6378.137 km (3963.19 miles)
|
// The IUGG value for the equatorial radius is 6378.137 km (3963.19 miles)
|
||||||
const constexpr long double EARTH_RADIUS = 6372797.560856;
|
const constexpr long double EARTH_RADIUS = 6372797.560856;
|
||||||
|
|
||||||
|
inline double degToRad(const double degree)
|
||||||
|
{
|
||||||
|
using namespace boost::math::constants;
|
||||||
|
return degree * (pi<double>() / 180.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline double radToDeg(const double radian)
|
||||||
|
{
|
||||||
|
using namespace boost::math::constants;
|
||||||
|
return radian * (180.0 * (1. / pi<double>()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Takes the squared euclidean distance of the input coordinates. Does not return meters!
|
//! Takes the squared euclidean distance of the input coordinates. Does not return meters!
|
||||||
@ -33,22 +48,8 @@ double haversineDistance(const Coordinate first_coordinate, const Coordinate sec
|
|||||||
double greatCircleDistance(const Coordinate first_coordinate, const Coordinate second_coordinate);
|
double greatCircleDistance(const Coordinate first_coordinate, const Coordinate second_coordinate);
|
||||||
|
|
||||||
// get the length of a full coordinate vector, using one of our basic functions to compute distances
|
// get the length of a full coordinate vector, using one of our basic functions to compute distances
|
||||||
template <class BinaryOperation>
|
template <class BinaryOperation, typename iterator_type>
|
||||||
double getLength(const std::vector<Coordinate> &coordinates, BinaryOperation op)
|
double getLength(iterator_type begin, const iterator_type end, BinaryOperation op);
|
||||||
{
|
|
||||||
if (coordinates.empty())
|
|
||||||
return 0.;
|
|
||||||
|
|
||||||
double result = 0;
|
|
||||||
const auto functor = [&result, op](const Coordinate lhs, const Coordinate rhs) {
|
|
||||||
result += op(lhs, rhs);
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
// side-effect find adding up distances
|
|
||||||
std::adjacent_find(coordinates.begin(), coordinates.end(), functor);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the closest distance and location between coordinate and the line connecting source and
|
// Find the closest distance and location between coordinate and the line connecting source and
|
||||||
// target:
|
// target:
|
||||||
@ -59,41 +60,35 @@ double getLength(const std::vector<Coordinate> &coordinates, BinaryOperation op)
|
|||||||
// returns x as well as the distance between source and x as ratio ([0,1])
|
// returns x as well as the distance between source and x as ratio ([0,1])
|
||||||
inline std::pair<double, FloatCoordinate> projectPointOnSegment(const FloatCoordinate &source,
|
inline std::pair<double, FloatCoordinate> projectPointOnSegment(const FloatCoordinate &source,
|
||||||
const FloatCoordinate &target,
|
const FloatCoordinate &target,
|
||||||
const FloatCoordinate &coordinate)
|
const FloatCoordinate &coordinate);
|
||||||
{
|
|
||||||
const FloatCoordinate slope_vector{target.lon - source.lon, target.lat - source.lat};
|
|
||||||
const FloatCoordinate rel_coordinate{coordinate.lon - source.lon, coordinate.lat - source.lat};
|
|
||||||
// dot product of two un-normed vectors
|
|
||||||
const auto unnormed_ratio = static_cast<double>(slope_vector.lon * rel_coordinate.lon) +
|
|
||||||
static_cast<double>(slope_vector.lat * rel_coordinate.lat);
|
|
||||||
// squared length of the slope vector
|
|
||||||
const auto squared_length = static_cast<double>(slope_vector.lon * slope_vector.lon) +
|
|
||||||
static_cast<double>(slope_vector.lat * slope_vector.lat);
|
|
||||||
|
|
||||||
if (squared_length < std::numeric_limits<double>::epsilon())
|
// find the closest distance between a coordinate and a segment
|
||||||
{
|
// O(1)
|
||||||
return {0, source};
|
double findClosestDistance(const Coordinate coordinate,
|
||||||
}
|
const Coordinate segment_begin,
|
||||||
|
const Coordinate segment_end);
|
||||||
|
|
||||||
const double normed_ratio = unnormed_ratio / squared_length;
|
// find the closest distance between a coordinate and a set of coordinates
|
||||||
double clamped_ratio = normed_ratio;
|
// O(|coordinates|)
|
||||||
if (clamped_ratio > 1.)
|
template <typename iterator_type>
|
||||||
{
|
double findClosestDistance(const Coordinate coordinate,
|
||||||
clamped_ratio = 1.;
|
const iterator_type begin,
|
||||||
}
|
const iterator_type end);
|
||||||
else if (clamped_ratio < 0.)
|
|
||||||
{
|
|
||||||
clamped_ratio = 0.;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {clamped_ratio,
|
// find the closes distance between two sets of coordinates
|
||||||
{
|
// O(|lhs| * |rhs|)
|
||||||
FloatLongitude{1.0 - clamped_ratio} * source.lon +
|
template <typename iterator_type>
|
||||||
target.lon * FloatLongitude{clamped_ratio},
|
double findClosestDistance(const iterator_type lhs_begin,
|
||||||
FloatLatitude{1.0 - clamped_ratio} * source.lat +
|
const iterator_type lhs_end,
|
||||||
target.lat * FloatLatitude{clamped_ratio},
|
const iterator_type rhs_begin,
|
||||||
}};
|
const iterator_type rhs_end);
|
||||||
}
|
|
||||||
|
// checks if two sets of coordinates describe a parallel set of ways
|
||||||
|
template <typename iterator_type>
|
||||||
|
bool areParallel(const iterator_type lhs_begin,
|
||||||
|
const iterator_type lhs_end,
|
||||||
|
const iterator_type rhs_begin,
|
||||||
|
const iterator_type rhs_end);
|
||||||
|
|
||||||
double perpendicularDistance(const Coordinate segment_source,
|
double perpendicularDistance(const Coordinate segment_source,
|
||||||
const Coordinate segment_target,
|
const Coordinate segment_target,
|
||||||
@ -136,8 +131,252 @@ bool isCCW(const Coordinate first_coordinate,
|
|||||||
const Coordinate second_coordinate,
|
const Coordinate second_coordinate,
|
||||||
const Coordinate third_coordinate);
|
const Coordinate third_coordinate);
|
||||||
|
|
||||||
std::pair<util::Coordinate, util::Coordinate>
|
template <typename iterator_type>
|
||||||
leastSquareRegression(const std::vector<util::Coordinate> &coordinates);
|
std::pair<Coordinate, Coordinate> leastSquareRegression(const iterator_type begin,
|
||||||
|
const iterator_type end);
|
||||||
|
|
||||||
|
// rotates a coordinate around the point (0,0). This function can be used to normalise a few
|
||||||
|
// computations around regression vectors
|
||||||
|
Coordinate rotateCCWAroundZero(Coordinate coordinate, double angle_in_radians);
|
||||||
|
|
||||||
|
// compute the difference vector of two coordinates lhs - rhs
|
||||||
|
Coordinate difference(const Coordinate lhs, const Coordinate rhs);
|
||||||
|
|
||||||
|
// TEMPLATE/INLINE DEFINITIONS
|
||||||
|
inline std::pair<double, FloatCoordinate> projectPointOnSegment(const FloatCoordinate &source,
|
||||||
|
const FloatCoordinate &target,
|
||||||
|
const FloatCoordinate &coordinate)
|
||||||
|
{
|
||||||
|
const FloatCoordinate slope_vector{target.lon - source.lon, target.lat - source.lat};
|
||||||
|
const FloatCoordinate rel_coordinate{coordinate.lon - source.lon, coordinate.lat - source.lat};
|
||||||
|
// dot product of two un-normed vectors
|
||||||
|
const auto unnormed_ratio = static_cast<double>(slope_vector.lon * rel_coordinate.lon) +
|
||||||
|
static_cast<double>(slope_vector.lat * rel_coordinate.lat);
|
||||||
|
// squared length of the slope vector
|
||||||
|
const auto squared_length = static_cast<double>(slope_vector.lon * slope_vector.lon) +
|
||||||
|
static_cast<double>(slope_vector.lat * slope_vector.lat);
|
||||||
|
|
||||||
|
if (squared_length < std::numeric_limits<double>::epsilon())
|
||||||
|
{
|
||||||
|
return {0, source};
|
||||||
|
}
|
||||||
|
|
||||||
|
const double normed_ratio = unnormed_ratio / squared_length;
|
||||||
|
double clamped_ratio = normed_ratio;
|
||||||
|
if (clamped_ratio > 1.)
|
||||||
|
{
|
||||||
|
clamped_ratio = 1.;
|
||||||
|
}
|
||||||
|
else if (clamped_ratio < 0.)
|
||||||
|
{
|
||||||
|
clamped_ratio = 0.;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {clamped_ratio,
|
||||||
|
{
|
||||||
|
FloatLongitude{1.0 - clamped_ratio} * source.lon +
|
||||||
|
target.lon * FloatLongitude{clamped_ratio},
|
||||||
|
FloatLatitude{1.0 - clamped_ratio} * source.lat +
|
||||||
|
target.lat * FloatLatitude{clamped_ratio},
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class BinaryOperation, typename iterator_type>
|
||||||
|
double getLength(iterator_type begin, const iterator_type end, BinaryOperation op)
|
||||||
|
{
|
||||||
|
double result = 0;
|
||||||
|
const auto functor = [&result, op](const Coordinate lhs, const Coordinate rhs) {
|
||||||
|
result += op(lhs, rhs);
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
// side-effect find adding up distances
|
||||||
|
std::adjacent_find(begin, end, functor);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename iterator_type>
|
||||||
|
double
|
||||||
|
findClosestDistance(const Coordinate coordinate, const iterator_type begin, const iterator_type end)
|
||||||
|
{
|
||||||
|
double current_min = std::numeric_limits<double>::max();
|
||||||
|
|
||||||
|
// comparator updating current_min without ever finding an element
|
||||||
|
const auto compute_minimum_distance = [¤t_min, coordinate](const Coordinate lhs,
|
||||||
|
const Coordinate rhs) {
|
||||||
|
current_min = std::min(current_min, findClosestDistance(coordinate, lhs, rhs));
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::adjacent_find(begin, end, compute_minimum_distance);
|
||||||
|
return current_min;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename iterator_type>
|
||||||
|
double findClosestDistance(const iterator_type lhs_begin,
|
||||||
|
const iterator_type lhs_end,
|
||||||
|
const iterator_type rhs_begin,
|
||||||
|
const iterator_type rhs_end)
|
||||||
|
{
|
||||||
|
double current_min = std::numeric_limits<double>::max();
|
||||||
|
|
||||||
|
const auto compute_minimum_distance_in_rhs = [¤t_min, rhs_begin, rhs_end](
|
||||||
|
const Coordinate coordinate) {
|
||||||
|
current_min = std::min(current_min, findClosestDistance(coordinate, rhs_begin, rhs_end));
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::find_if(lhs_begin, lhs_end, compute_minimum_distance_in_rhs);
|
||||||
|
return current_min;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename iterator_type>
|
||||||
|
std::pair<Coordinate, Coordinate> leastSquareRegression(const iterator_type begin,
|
||||||
|
const iterator_type end)
|
||||||
|
{
|
||||||
|
// following the formulas of https://faculty.elgin.edu/dkernler/statistics/ch04/4-2.html
|
||||||
|
const auto number_of_coordinates = std::distance(begin, end);
|
||||||
|
BOOST_ASSERT(number_of_coordinates >= 2);
|
||||||
|
const auto extract_lon = [](const Coordinate coordinate) {
|
||||||
|
return static_cast<double>(toFloating(coordinate.lon));
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto extract_lat = [](const Coordinate coordinate) {
|
||||||
|
return static_cast<double>(toFloating(coordinate.lat));
|
||||||
|
};
|
||||||
|
|
||||||
|
double min_lon = extract_lon(*begin);
|
||||||
|
double max_lon = extract_lon(*begin);
|
||||||
|
double min_lat = extract_lat(*begin);
|
||||||
|
double max_lat = extract_lat(*begin);
|
||||||
|
|
||||||
|
for (auto coordinate_iterator = begin; coordinate_iterator != end; ++coordinate_iterator)
|
||||||
|
{
|
||||||
|
const auto c = *coordinate_iterator;
|
||||||
|
const auto lon = extract_lon(c);
|
||||||
|
min_lon = std::min(min_lon, lon);
|
||||||
|
max_lon = std::max(max_lon, lon);
|
||||||
|
const auto lat = extract_lat(c);
|
||||||
|
min_lat = std::min(min_lat, lat);
|
||||||
|
max_lat = std::max(max_lat, lat);
|
||||||
|
}
|
||||||
|
// very small difference in longitude -> would result in inaccurate calculation, check if lat is
|
||||||
|
// better
|
||||||
|
if ((max_lat - min_lat) > 2 * (max_lon - min_lon))
|
||||||
|
{
|
||||||
|
std::vector<util::Coordinate> rotated_coordinates(number_of_coordinates);
|
||||||
|
// rotate all coordinates to the right
|
||||||
|
std::transform(begin, end, rotated_coordinates.begin(), [](const auto coordinate) {
|
||||||
|
return rotateCCWAroundZero(coordinate, detail::degToRad(-90));
|
||||||
|
});
|
||||||
|
const auto rotated_regression =
|
||||||
|
leastSquareRegression(rotated_coordinates.begin(), rotated_coordinates.end());
|
||||||
|
return {rotateCCWAroundZero(rotated_regression.first, detail::degToRad(90)),
|
||||||
|
rotateCCWAroundZero(rotated_regression.second, detail::degToRad(90))};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto make_accumulate = [](const auto extraction_function) {
|
||||||
|
return [extraction_function](const double sum_so_far, const Coordinate coordinate) {
|
||||||
|
return sum_so_far + extraction_function(coordinate);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto accumulated_lon = std::accumulate(begin, end, 0., make_accumulate(extract_lon));
|
||||||
|
|
||||||
|
const auto accumulated_lat = std::accumulate(begin, end, 0., make_accumulate(extract_lat));
|
||||||
|
|
||||||
|
const auto mean_lon = accumulated_lon / number_of_coordinates;
|
||||||
|
const auto mean_lat = accumulated_lat / number_of_coordinates;
|
||||||
|
const auto make_variance = [](const auto mean, const auto extraction_function) {
|
||||||
|
return [extraction_function, mean](const double sum_so_far, const Coordinate coordinate) {
|
||||||
|
const auto difference = extraction_function(coordinate) - mean;
|
||||||
|
return sum_so_far + difference * difference;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// using the unbiased version, we divide by num_samples - 1 (see
|
||||||
|
// http://mathworld.wolfram.com/SampleVariance.html)
|
||||||
|
const auto sample_variance_lon =
|
||||||
|
std::sqrt(std::accumulate(begin, end, 0., make_variance(mean_lon, extract_lon)) /
|
||||||
|
(number_of_coordinates - 1));
|
||||||
|
|
||||||
|
// if we don't change longitude, return the vertical line as is
|
||||||
|
if (std::abs(sample_variance_lon) <
|
||||||
|
std::numeric_limits<decltype(sample_variance_lon)>::epsilon())
|
||||||
|
return {*begin, *(end - 1)};
|
||||||
|
|
||||||
|
const auto sample_variance_lat =
|
||||||
|
std::sqrt(std::accumulate(begin, end, 0., make_variance(mean_lat, extract_lat)) /
|
||||||
|
(number_of_coordinates - 1));
|
||||||
|
|
||||||
|
if (std::abs(sample_variance_lat) <
|
||||||
|
std::numeric_limits<decltype(sample_variance_lat)>::epsilon())
|
||||||
|
return {*begin, *(end - 1)};
|
||||||
|
const auto linear_correlation =
|
||||||
|
std::accumulate(begin,
|
||||||
|
end,
|
||||||
|
0.,
|
||||||
|
[&](const auto sum_so_far, const auto current_coordinate) {
|
||||||
|
return sum_so_far +
|
||||||
|
(extract_lon(current_coordinate) - mean_lon) *
|
||||||
|
(extract_lat(current_coordinate) - mean_lat) /
|
||||||
|
(sample_variance_lon * sample_variance_lat);
|
||||||
|
}) /
|
||||||
|
(number_of_coordinates - 1);
|
||||||
|
|
||||||
|
const auto slope = linear_correlation * sample_variance_lat / sample_variance_lon;
|
||||||
|
const auto intercept = mean_lat - slope * mean_lon;
|
||||||
|
|
||||||
|
const auto GetLatAtLon = [intercept,
|
||||||
|
slope](const util::FloatLongitude longitude) -> util::FloatLatitude {
|
||||||
|
return {intercept + slope * static_cast<double>((longitude))};
|
||||||
|
};
|
||||||
|
|
||||||
|
const double offset = 0.00001;
|
||||||
|
const Coordinate regression_first = {
|
||||||
|
toFixed(util::FloatLongitude{min_lon - offset}),
|
||||||
|
toFixed(util::FloatLatitude(GetLatAtLon(util::FloatLongitude{min_lon - offset})))};
|
||||||
|
const Coordinate regression_end = {
|
||||||
|
toFixed(util::FloatLongitude{max_lon + offset}),
|
||||||
|
toFixed(util::FloatLatitude(GetLatAtLon(util::FloatLongitude{max_lon + offset})))};
|
||||||
|
|
||||||
|
return {regression_first, regression_end};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename iterator_type>
|
||||||
|
bool areParallel(const iterator_type lhs_begin,
|
||||||
|
const iterator_type lhs_end,
|
||||||
|
const iterator_type rhs_begin,
|
||||||
|
const iterator_type rhs_end)
|
||||||
|
{
|
||||||
|
const auto regression_lhs = leastSquareRegression(lhs_begin, lhs_end);
|
||||||
|
const auto regression_rhs = leastSquareRegression(rhs_begin, rhs_end);
|
||||||
|
|
||||||
|
const auto null_island = Coordinate(FixedLongitude{0}, FixedLatitude{0});
|
||||||
|
const auto difference_lhs = difference(regression_lhs.first, regression_lhs.second);
|
||||||
|
const auto difference_rhs = difference(regression_rhs.first, regression_rhs.second);
|
||||||
|
|
||||||
|
// we normalise the left slope to be zero, so we rotate the coordinates around 0,0 to match 90
|
||||||
|
// degrees
|
||||||
|
const auto bearing_lhs = bearing(null_island, difference_lhs);
|
||||||
|
|
||||||
|
// we rotate to have one of the lines facing horizontally to the right (bearing 90 degree)
|
||||||
|
const auto rotation_angle_radians = detail::degToRad(bearing_lhs - 90);
|
||||||
|
const auto rotated_difference_rhs = rotateCCWAroundZero(difference_rhs, rotation_angle_radians);
|
||||||
|
|
||||||
|
const auto get_slope = [](const Coordinate from, const Coordinate to) {
|
||||||
|
const auto diff_lat = static_cast<int>(from.lat) - static_cast<int>(to.lat);
|
||||||
|
const auto diff_lon = static_cast<int>(from.lon) - static_cast<int>(to.lon);
|
||||||
|
if (diff_lon == 0)
|
||||||
|
return std::numeric_limits<double>::max();
|
||||||
|
return static_cast<double>(diff_lat) / static_cast<double>(diff_lon);
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto slope_rhs = get_slope(null_island, rotated_difference_rhs);
|
||||||
|
// the left hand side has a slope of `0` after the rotation. We can check the slope of the right
|
||||||
|
// hand side to ensure we only considering slight slopes
|
||||||
|
return std::abs(slope_rhs) < 0.20; // twenty percent incline at the most
|
||||||
|
}
|
||||||
|
|
||||||
} // ns coordinate_calculation
|
} // ns coordinate_calculation
|
||||||
} // ns util
|
} // ns util
|
||||||
|
@ -22,7 +22,6 @@ namespace util
|
|||||||
{
|
{
|
||||||
namespace guidance
|
namespace guidance
|
||||||
{
|
{
|
||||||
|
|
||||||
// Name Change Logic
|
// Name Change Logic
|
||||||
// Used both during Extraction as well as during Post-Processing
|
// Used both during Extraction as well as during Post-Processing
|
||||||
|
|
||||||
@ -157,6 +156,9 @@ inline bool requiresNameAnnounced(const NameID from_name_id,
|
|||||||
const util::NameTable &name_table,
|
const util::NameTable &name_table,
|
||||||
const extractor::SuffixTable &suffix_table)
|
const extractor::SuffixTable &suffix_table)
|
||||||
{
|
{
|
||||||
|
if (from_name_id == to_name_id)
|
||||||
|
return false;
|
||||||
|
else
|
||||||
return requiresNameAnnounced(name_table.GetNameForID(from_name_id),
|
return requiresNameAnnounced(name_table.GetNameForID(from_name_id),
|
||||||
name_table.GetRefForID(from_name_id),
|
name_table.GetRefForID(from_name_id),
|
||||||
name_table.GetPronunciationForID(from_name_id),
|
name_table.GetPronunciationForID(from_name_id),
|
||||||
@ -170,6 +172,9 @@ inline bool requiresNameAnnounced(const NameID from_name_id,
|
|||||||
const NameID to_name_id,
|
const NameID to_name_id,
|
||||||
const util::NameTable &name_table)
|
const util::NameTable &name_table)
|
||||||
{
|
{
|
||||||
|
if (from_name_id == to_name_id)
|
||||||
|
return false;
|
||||||
|
else
|
||||||
return requiresNameAnnounced(name_table.GetNameForID(from_name_id),
|
return requiresNameAnnounced(name_table.GetNameForID(from_name_id),
|
||||||
name_table.GetRefForID(from_name_id),
|
name_table.GetRefForID(from_name_id),
|
||||||
name_table.GetPronunciationForID(from_name_id),
|
name_table.GetPronunciationForID(from_name_id),
|
||||||
|
@ -260,8 +260,8 @@ void closeOffRoundabout(const bool on_roundabout,
|
|||||||
TurnType::EnterRoundaboutIntersectionAtExit)
|
TurnType::EnterRoundaboutIntersectionAtExit)
|
||||||
{
|
{
|
||||||
BOOST_ASSERT(!propagation_step.intersections.empty());
|
BOOST_ASSERT(!propagation_step.intersections.empty());
|
||||||
const double angle = util::angleBetweenBearings(
|
const double angle = util::bearing::angleBetween(
|
||||||
util::reverseBearing(entry_intersection.bearings[entry_intersection.in]),
|
util::bearing::reverse(entry_intersection.bearings[entry_intersection.in]),
|
||||||
exit_bearing);
|
exit_bearing);
|
||||||
|
|
||||||
auto bearings = propagation_step.intersections.front().bearings;
|
auto bearings = propagation_step.intersections.front().bearings;
|
||||||
@ -306,7 +306,7 @@ bool isUTurn(const RouteStep &in_step, const RouteStep &out_step, const RouteSte
|
|||||||
(isLinkroad(in_step) && out_step.name_id != EMPTY_NAMEID &&
|
(isLinkroad(in_step) && out_step.name_id != EMPTY_NAMEID &&
|
||||||
pre_in_step.name_id != EMPTY_NAMEID && !isNoticeableNameChange(pre_in_step, out_step));
|
pre_in_step.name_id != EMPTY_NAMEID && !isNoticeableNameChange(pre_in_step, out_step));
|
||||||
const bool takes_u_turn = bearingsAreReversed(
|
const bool takes_u_turn = bearingsAreReversed(
|
||||||
util::reverseBearing(
|
util::bearing::reverse(
|
||||||
in_step.intersections.front().bearings[in_step.intersections.front().in]),
|
in_step.intersections.front().bearings[in_step.intersections.front().in]),
|
||||||
out_step.intersections.front().bearings[out_step.intersections.front().out]);
|
out_step.intersections.front().bearings[out_step.intersections.front().out]);
|
||||||
|
|
||||||
@ -318,20 +318,20 @@ double findTotalTurnAngle(const RouteStep &entry_step, const RouteStep &exit_ste
|
|||||||
const auto exit_intersection = exit_step.intersections.front();
|
const auto exit_intersection = exit_step.intersections.front();
|
||||||
const auto exit_step_exit_bearing = exit_intersection.bearings[exit_intersection.out];
|
const auto exit_step_exit_bearing = exit_intersection.bearings[exit_intersection.out];
|
||||||
const auto exit_step_entry_bearing =
|
const auto exit_step_entry_bearing =
|
||||||
util::reverseBearing(exit_intersection.bearings[exit_intersection.in]);
|
util::bearing::reverse(exit_intersection.bearings[exit_intersection.in]);
|
||||||
|
|
||||||
const auto entry_intersection = entry_step.intersections.front();
|
const auto entry_intersection = entry_step.intersections.front();
|
||||||
const auto entry_step_entry_bearing =
|
const auto entry_step_entry_bearing =
|
||||||
util::reverseBearing(entry_intersection.bearings[entry_intersection.in]);
|
util::bearing::reverse(entry_intersection.bearings[entry_intersection.in]);
|
||||||
const auto entry_step_exit_bearing = entry_intersection.bearings[entry_intersection.out];
|
const auto entry_step_exit_bearing = entry_intersection.bearings[entry_intersection.out];
|
||||||
|
|
||||||
const auto exit_angle =
|
const auto exit_angle =
|
||||||
util::angleBetweenBearings(exit_step_entry_bearing, exit_step_exit_bearing);
|
util::bearing::angleBetween(exit_step_entry_bearing, exit_step_exit_bearing);
|
||||||
const auto entry_angle =
|
const auto entry_angle =
|
||||||
util::angleBetweenBearings(entry_step_entry_bearing, entry_step_exit_bearing);
|
util::bearing::angleBetween(entry_step_entry_bearing, entry_step_exit_bearing);
|
||||||
|
|
||||||
const double total_angle =
|
const double total_angle =
|
||||||
util::angleBetweenBearings(entry_step_entry_bearing, exit_step_exit_bearing);
|
util::bearing::angleBetween(entry_step_entry_bearing, exit_step_exit_bearing);
|
||||||
// We allow for minor deviations from a straight line
|
// We allow for minor deviations from a straight line
|
||||||
if (((entry_step.distance < MAX_COLLAPSE_DISTANCE && exit_step.intersections.size() == 1) ||
|
if (((entry_step.distance < MAX_COLLAPSE_DISTANCE && exit_step.intersections.size() == 1) ||
|
||||||
(entry_angle <= 185 && exit_angle <= 185) || (entry_angle >= 175 && exit_angle >= 175)) &&
|
(entry_angle <= 185 && exit_angle <= 185) || (entry_angle >= 175 && exit_angle >= 175)) &&
|
||||||
@ -391,7 +391,7 @@ void collapseUTurn(std::vector<RouteStep> &steps,
|
|||||||
const bool direct_u_turn = !isNoticeableNameChange(steps[two_back_index], current_step);
|
const bool direct_u_turn = !isNoticeableNameChange(steps[two_back_index], current_step);
|
||||||
|
|
||||||
// however, we might also deal with a dual-collapse scenario in which we have to
|
// however, we might also deal with a dual-collapse scenario in which we have to
|
||||||
// additionall collapse a name-change as welll
|
// additionall collapse a name-change as well
|
||||||
const auto next_step_index = step_index + 1;
|
const auto next_step_index = step_index + 1;
|
||||||
const bool continues_with_name_change =
|
const bool continues_with_name_change =
|
||||||
(next_step_index < steps.size()) && compatible(steps[step_index], steps[next_step_index]) &&
|
(next_step_index < steps.size()) && compatible(steps[step_index], steps[next_step_index]) &&
|
||||||
@ -531,18 +531,18 @@ void collapseTurnAt(std::vector<RouteStep> &steps,
|
|||||||
if (continue_or_suppressed || turning_name)
|
if (continue_or_suppressed || turning_name)
|
||||||
{
|
{
|
||||||
const auto in_bearing = [](const RouteStep &step) {
|
const auto in_bearing = [](const RouteStep &step) {
|
||||||
return util::reverseBearing(
|
return util::bearing::reverse(
|
||||||
step.intersections.front().bearings[step.intersections.front().in]);
|
step.intersections.front().bearings[step.intersections.front().in]);
|
||||||
};
|
};
|
||||||
const auto out_bearing = [](const RouteStep &step) {
|
const auto out_bearing = [](const RouteStep &step) {
|
||||||
return step.intersections.front().bearings[step.intersections.front().out];
|
return step.intersections.front().bearings[step.intersections.front().out];
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto first_angle = util::angleBetweenBearings(in_bearing(one_back_step),
|
const auto first_angle = util::bearing::angleBetween(in_bearing(one_back_step),
|
||||||
out_bearing(one_back_step));
|
out_bearing(one_back_step));
|
||||||
const auto second_angle =
|
const auto second_angle = util::bearing::angleBetween(in_bearing(current_step),
|
||||||
util::angleBetweenBearings(in_bearing(current_step), out_bearing(current_step));
|
out_bearing(current_step));
|
||||||
const auto bearing_turn_angle = util::angleBetweenBearings(
|
const auto bearing_turn_angle = util::bearing::angleBetween(
|
||||||
in_bearing(one_back_step), out_bearing(current_step));
|
in_bearing(one_back_step), out_bearing(current_step));
|
||||||
|
|
||||||
// When looking at an intersection, some angles, even though present, feel more like
|
// When looking at an intersection, some angles, even though present, feel more like
|
||||||
@ -676,7 +676,7 @@ void collapseTurnAt(std::vector<RouteStep> &steps,
|
|||||||
};
|
};
|
||||||
|
|
||||||
// If we Merge onto the same street, we end up with a u-turn in some cases
|
// If we Merge onto the same street, we end up with a u-turn in some cases
|
||||||
if (bearingsAreReversed(util::reverseBearing(getBearing(true, one_back_step)),
|
if (bearingsAreReversed(util::bearing::reverse(getBearing(true, one_back_step)),
|
||||||
getBearing(false, current_step)))
|
getBearing(false, current_step)))
|
||||||
{
|
{
|
||||||
steps[one_back_index].maneuver.instruction.direction_modifier =
|
steps[one_back_index].maneuver.instruction.direction_modifier =
|
||||||
@ -760,7 +760,7 @@ bool isStaggeredIntersection(const std::vector<RouteStep> &steps,
|
|||||||
const auto &intersection = step.intersections.front();
|
const auto &intersection = step.intersections.front();
|
||||||
const auto entry_bearing = intersection.bearings[intersection.in];
|
const auto entry_bearing = intersection.bearings[intersection.in];
|
||||||
const auto exit_bearing = intersection.bearings[intersection.out];
|
const auto exit_bearing = intersection.bearings[intersection.out];
|
||||||
return util::angleBetweenBearings(entry_bearing, exit_bearing);
|
return util::bearing::angleBetween(entry_bearing, exit_bearing);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Instead of using turn modifiers (e.g. as in isRightTurn) we want to be more strict here.
|
// Instead of using turn modifiers (e.g. as in isRightTurn) we want to be more strict here.
|
||||||
@ -915,7 +915,7 @@ std::vector<RouteStep> postProcess(std::vector<RouteStep> steps)
|
|||||||
|
|
||||||
// unterminated roundabout
|
// unterminated roundabout
|
||||||
// Move backwards through the instructions until the start and remove the exit number
|
// Move backwards through the instructions until the start and remove the exit number
|
||||||
// A roundabout without exit translates to enter-roundabout.
|
// A roundabout without exit translates to enter-roundabout
|
||||||
if (has_entered_roundabout || on_roundabout)
|
if (has_entered_roundabout || on_roundabout)
|
||||||
{
|
{
|
||||||
fixFinalRoundabout(steps);
|
fixFinalRoundabout(steps);
|
||||||
@ -1238,10 +1238,8 @@ void trimShortSegments(std::vector<RouteStep> &steps, LegGeometry &geometry)
|
|||||||
if (zero_length_step)
|
if (zero_length_step)
|
||||||
{
|
{
|
||||||
// since we are not only checking for epsilon but for a full meter, we can have multiple
|
// since we are not only checking for epsilon but for a full meter, we can have multiple
|
||||||
// coordinates here.
|
// coordinates here. Move all offsets to the front and reduce by one. (This is an
|
||||||
// move offsets to front
|
// inplace forward one and reduce by one)
|
||||||
// geometry offsets have to be adjusted. Move all offsets to the front and reduce by
|
|
||||||
// one. (This is an inplace forward one and reduce by one)
|
|
||||||
std::transform(geometry.segment_offsets.begin() + 1,
|
std::transform(geometry.segment_offsets.begin() + 1,
|
||||||
geometry.segment_offsets.end(),
|
geometry.segment_offsets.end(),
|
||||||
geometry.segment_offsets.begin(),
|
geometry.segment_offsets.begin(),
|
||||||
@ -1378,7 +1376,7 @@ void trimShortSegments(std::vector<RouteStep> &steps, LegGeometry &geometry)
|
|||||||
geometry.locations[next_to_last_step.geometry_end - 2],
|
geometry.locations[next_to_last_step.geometry_end - 2],
|
||||||
geometry.locations[last_step.geometry_begin]));
|
geometry.locations[last_step.geometry_begin]));
|
||||||
last_step.maneuver.bearing_before = bearing;
|
last_step.maneuver.bearing_before = bearing;
|
||||||
last_step.intersections.front().bearings.front() = util::reverseBearing(bearing);
|
last_step.intersections.front().bearings.front() = util::bearing::reverse(bearing);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_ASSERT(steps.back().geometry_end == geometry.locations.size());
|
BOOST_ASSERT(steps.back().geometry_end == geometry.locations.size());
|
||||||
|
@ -415,9 +415,9 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
|||||||
turn_analysis.GetIntersectionGenerator().TransformIntersectionShapeIntoView(
|
turn_analysis.GetIntersectionGenerator().TransformIntersectionShapeIntoView(
|
||||||
node_along_road_entering,
|
node_along_road_entering,
|
||||||
incoming_edge,
|
incoming_edge,
|
||||||
shape_result.normalised_intersection_shape,
|
shape_result.annotated_normalized_shape.normalized_shape,
|
||||||
shape_result.intersection_shape,
|
shape_result.intersection_shape,
|
||||||
shape_result.merging_map);
|
shape_result.annotated_normalized_shape.performed_merges);
|
||||||
|
|
||||||
auto intersection = turn_analysis.AssignTurnTypes(
|
auto intersection = turn_analysis.AssignTurnTypes(
|
||||||
node_along_road_entering, incoming_edge, intersection_with_flags_and_angles);
|
node_along_road_entering, incoming_edge, intersection_with_flags_and_angles);
|
||||||
|
@ -251,6 +251,7 @@ int Extractor::run(ScriptingEnvironment &scripting_environment)
|
|||||||
std::vector<bool> node_is_startpoint;
|
std::vector<bool> node_is_startpoint;
|
||||||
std::vector<EdgeWeight> edge_based_node_weights;
|
std::vector<EdgeWeight> edge_based_node_weights;
|
||||||
std::vector<QueryNode> internal_to_external_node_map;
|
std::vector<QueryNode> internal_to_external_node_map;
|
||||||
|
|
||||||
auto graph_size = BuildEdgeExpandedGraph(scripting_environment,
|
auto graph_size = BuildEdgeExpandedGraph(scripting_environment,
|
||||||
internal_to_external_node_map,
|
internal_to_external_node_map,
|
||||||
edge_based_node_list,
|
edge_based_node_list,
|
||||||
|
@ -139,8 +139,6 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME this need to be moved into the profiles
|
|
||||||
const guidance::RoadClassification road_classification = parsed_way.road_classification;
|
|
||||||
const auto laneStringToDescription = [](const std::string &lane_string) -> TurnLaneDescription {
|
const auto laneStringToDescription = [](const std::string &lane_string) -> TurnLaneDescription {
|
||||||
if (lane_string.empty())
|
if (lane_string.empty())
|
||||||
return {};
|
return {};
|
||||||
@ -237,6 +235,8 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti
|
|||||||
const auto turn_lane_id_forward = requestId(parsed_way.turn_lanes_forward);
|
const auto turn_lane_id_forward = requestId(parsed_way.turn_lanes_forward);
|
||||||
const auto turn_lane_id_backward = requestId(parsed_way.turn_lanes_backward);
|
const auto turn_lane_id_backward = requestId(parsed_way.turn_lanes_backward);
|
||||||
|
|
||||||
|
const auto road_classification = parsed_way.road_classification;
|
||||||
|
|
||||||
const constexpr auto MAX_STRING_LENGTH = 255u;
|
const constexpr auto MAX_STRING_LENGTH = 255u;
|
||||||
// Get the unique identifier for the street name, destination, and ref
|
// Get the unique identifier for the street name, destination, and ref
|
||||||
const auto name_iterator = string_map.find(
|
const auto name_iterator = string_map.find(
|
||||||
|
@ -1,69 +0,0 @@
|
|||||||
#include "extractor/geojson_debug_policies.hpp"
|
|
||||||
#include "util/coordinate.hpp"
|
|
||||||
#include "util/geojson_debug_policy_toolkit.hpp"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
namespace osrm
|
|
||||||
{
|
|
||||||
namespace extractor
|
|
||||||
{
|
|
||||||
|
|
||||||
IntersectionPrinter::IntersectionPrinter(
|
|
||||||
const util::NodeBasedDynamicGraph &node_based_graph,
|
|
||||||
const std::vector<extractor::QueryNode> &node_coordinates,
|
|
||||||
const extractor::guidance::CoordinateExtractor &coordinate_extractor)
|
|
||||||
: node_based_graph(node_based_graph), node_coordinates(node_coordinates),
|
|
||||||
coordinate_extractor(coordinate_extractor)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
util::json::Array IntersectionPrinter::
|
|
||||||
operator()(const NodeID intersection_node,
|
|
||||||
const extractor::guidance::Intersection &intersection,
|
|
||||||
const boost::optional<util::json::Object> &node_style,
|
|
||||||
const boost::optional<util::json::Object> &way_style) const
|
|
||||||
{
|
|
||||||
// request the number of lanes. This process needs to be in sync with what happens over at
|
|
||||||
// intersection_generator
|
|
||||||
const auto intersection_lanes = intersection.getHighestConnectedLaneCount(node_based_graph);
|
|
||||||
|
|
||||||
std::vector<util::Coordinate> coordinates;
|
|
||||||
coordinates.reserve(intersection.size());
|
|
||||||
coordinates.push_back(node_coordinates[intersection_node]);
|
|
||||||
|
|
||||||
const auto road_to_coordinate = [&](const extractor::guidance::ConnectedRoad &connected_road) {
|
|
||||||
const constexpr auto FORWARD = false;
|
|
||||||
const auto to_node = node_based_graph.GetTarget(connected_road.eid);
|
|
||||||
return coordinate_extractor.GetCoordinateAlongRoad(
|
|
||||||
intersection_node, connected_road.eid, FORWARD, to_node, intersection_lanes);
|
|
||||||
};
|
|
||||||
|
|
||||||
std::transform(intersection.begin(),
|
|
||||||
intersection.end(),
|
|
||||||
std::back_inserter(coordinates),
|
|
||||||
road_to_coordinate);
|
|
||||||
|
|
||||||
util::json::Array features;
|
|
||||||
features.values.push_back(
|
|
||||||
util::makeFeature("MultiPoint", makeJsonArray(coordinates), node_style));
|
|
||||||
|
|
||||||
if (coordinates.size() > 1)
|
|
||||||
{
|
|
||||||
std::vector<util::Coordinate> line_coordinates(2);
|
|
||||||
line_coordinates[0] = coordinates.front();
|
|
||||||
const auto coordinate_to_line = [&](const util::Coordinate coordinate) {
|
|
||||||
line_coordinates[1] = coordinate;
|
|
||||||
return util::makeFeature("LineString", makeJsonArray(line_coordinates), way_style);
|
|
||||||
};
|
|
||||||
|
|
||||||
std::transform(std::next(coordinates.begin()),
|
|
||||||
coordinates.end(),
|
|
||||||
std::back_inserter(features.values),
|
|
||||||
coordinate_to_line);
|
|
||||||
}
|
|
||||||
return features;
|
|
||||||
}
|
|
||||||
|
|
||||||
} /* namespace extractor */
|
|
||||||
} /* namespace osrm */
|
|
@ -31,7 +31,6 @@ const constexpr double LOOKAHEAD_DISTANCE_WITHOUT_LANES = 10.0;
|
|||||||
// The standard with of a interstate highway is 3.7 meters. Local roads have
|
// The standard with of a interstate highway is 3.7 meters. Local roads have
|
||||||
// smaller widths, ranging from 2.5 to 3.25 meters. As a compromise, we use
|
// smaller widths, ranging from 2.5 to 3.25 meters. As a compromise, we use
|
||||||
// the 3.25 here for our angle calculations
|
// the 3.25 here for our angle calculations
|
||||||
const constexpr double ASSUMED_LANE_WIDTH = 3.25;
|
|
||||||
const constexpr double FAR_LOOKAHEAD_DISTANCE = 40.0;
|
const constexpr double FAR_LOOKAHEAD_DISTANCE = 40.0;
|
||||||
|
|
||||||
// The count of lanes assumed when no lanes are present. Since most roads will have lanes for both
|
// The count of lanes assumed when no lanes are present. Since most roads will have lanes for both
|
||||||
@ -650,7 +649,7 @@ bool CoordinateExtractor::IsCurve(const std::vector<util::Coordinate> &coordinat
|
|||||||
const auto end_bearing = util::coordinate_calculation::bearing(
|
const auto end_bearing = util::coordinate_calculation::bearing(
|
||||||
coordinates[coordinates.size() - 2], coordinates[coordinates.size() - 1]);
|
coordinates[coordinates.size() - 2], coordinates[coordinates.size() - 1]);
|
||||||
|
|
||||||
const auto total_angle = angularDeviation(begin_bearing, end_bearing);
|
const auto total_angle = util::angularDeviation(begin_bearing, end_bearing);
|
||||||
return total_angle > 0.5 * NARROW_TURN_ANGLE;
|
return total_angle > 0.5 * NARROW_TURN_ANGLE;
|
||||||
}();
|
}();
|
||||||
|
|
||||||
@ -754,8 +753,10 @@ bool CoordinateExtractor::IsCurve(const std::vector<util::Coordinate> &coordinat
|
|||||||
return turn_angles;
|
return turn_angles;
|
||||||
}();
|
}();
|
||||||
|
|
||||||
const bool curve_is_valid =
|
const bool curve_is_valid = [&turn_angles,
|
||||||
[&turn_angles, &segment_distances, &segment_length, &considered_lane_width]() {
|
&segment_distances,
|
||||||
|
&segment_length,
|
||||||
|
&considered_lane_width]() {
|
||||||
// internal state for our lamdae
|
// internal state for our lamdae
|
||||||
bool last_was_straight = false;
|
bool last_was_straight = false;
|
||||||
// a turn angle represents two segments between three coordinates. We initialize the
|
// a turn angle represents two segments between three coordinates. We initialize the
|
||||||
@ -769,8 +770,8 @@ bool CoordinateExtractor::IsCurve(const std::vector<util::Coordinate> &coordinat
|
|||||||
const auto detect_invalid_curve = [&](const double previous_angle,
|
const auto detect_invalid_curve = [&](const double previous_angle,
|
||||||
const double current_angle) {
|
const double current_angle) {
|
||||||
const auto both_actually_turn =
|
const auto both_actually_turn =
|
||||||
(angularDeviation(previous_angle, STRAIGHT_ANGLE) > FUZZY_ANGLE_DIFFERENCE) &&
|
(util::angularDeviation(previous_angle, STRAIGHT_ANGLE) > FUZZY_ANGLE_DIFFERENCE) &&
|
||||||
(angularDeviation(current_angle, STRAIGHT_ANGLE) > FUZZY_ANGLE_DIFFERENCE);
|
(util::angularDeviation(current_angle, STRAIGHT_ANGLE) > FUZZY_ANGLE_DIFFERENCE);
|
||||||
// they cannot be straight, since they differ at least by FUZZY_ANGLE_DIFFERENCE
|
// they cannot be straight, since they differ at least by FUZZY_ANGLE_DIFFERENCE
|
||||||
const auto turn_direction_switches =
|
const auto turn_direction_switches =
|
||||||
(previous_angle > STRAIGHT_ANGLE) == (current_angle < STRAIGHT_ANGLE);
|
(previous_angle > STRAIGHT_ANGLE) == (current_angle < STRAIGHT_ANGLE);
|
||||||
@ -779,7 +780,7 @@ bool CoordinateExtractor::IsCurve(const std::vector<util::Coordinate> &coordinat
|
|||||||
if (both_actually_turn && turn_direction_switches)
|
if (both_actually_turn && turn_direction_switches)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
const bool is_straight = angularDeviation(current_angle, STRAIGHT_ANGLE) < 5;
|
const bool is_straight = util::angularDeviation(current_angle, STRAIGHT_ANGLE) < 5;
|
||||||
++distance_itr;
|
++distance_itr;
|
||||||
if (is_straight)
|
if (is_straight)
|
||||||
{
|
{
|
||||||
@ -1147,8 +1148,8 @@ CoordinateExtractor::RegressionLine(const std::vector<util::Coordinate> &coordin
|
|||||||
return {coordinates.front(), coordinates.back()};
|
return {coordinates.front(), coordinates.back()};
|
||||||
|
|
||||||
// compute the regression vector based on the sum of least squares
|
// compute the regression vector based on the sum of least squares
|
||||||
const auto regression_line =
|
const auto regression_line = util::coordinate_calculation::leastSquareRegression(
|
||||||
util::coordinate_calculation::leastSquareRegression(sampled_coordinates);
|
sampled_coordinates.begin(), sampled_coordinates.end());
|
||||||
const auto coord_between_front =
|
const auto coord_between_front =
|
||||||
util::coordinate_calculation::projectPointOnSegment(
|
util::coordinate_calculation::projectPointOnSegment(
|
||||||
regression_line.first, regression_line.second, coordinates.front())
|
regression_line.first, regression_line.second, coordinates.front())
|
||||||
|
@ -34,7 +34,7 @@ void ConnectedRoad::mirror()
|
|||||||
DirectionModifier::MaxDirectionModifier,
|
DirectionModifier::MaxDirectionModifier,
|
||||||
"The list of mirrored modifiers needs to match the available modifiers in size.");
|
"The list of mirrored modifiers needs to match the available modifiers in size.");
|
||||||
|
|
||||||
if (angularDeviation(angle, 0) > std::numeric_limits<double>::epsilon())
|
if (util::angularDeviation(angle, 0) > std::numeric_limits<double>::epsilon())
|
||||||
{
|
{
|
||||||
angle = 360 - angle;
|
angle = 360 - angle;
|
||||||
instruction.direction_modifier = mirrored_modifiers[instruction.direction_modifier];
|
instruction.direction_modifier = mirrored_modifiers[instruction.direction_modifier];
|
||||||
@ -48,6 +48,26 @@ ConnectedRoad ConnectedRoad::getMirroredCopy() const
|
|||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string toString(const IntersectionShapeData &shape)
|
||||||
|
{
|
||||||
|
std::string result =
|
||||||
|
"[shape] " + std::to_string(shape.eid) + " bearing: " + std::to_string(shape.bearing);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string toString(const IntersectionViewData &view)
|
||||||
|
{
|
||||||
|
std::string result = "[view] ";
|
||||||
|
result += std::to_string(view.eid);
|
||||||
|
result += " allows entry: ";
|
||||||
|
result += std::to_string(view.entry_allowed);
|
||||||
|
result += " angle: ";
|
||||||
|
result += std::to_string(view.angle);
|
||||||
|
result += " bearing: ";
|
||||||
|
result += std::to_string(view.bearing);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
std::string toString(const ConnectedRoad &road)
|
std::string toString(const ConnectedRoad &road)
|
||||||
{
|
{
|
||||||
std::string result = "[connection] ";
|
std::string result = "[connection] ";
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
#include "extractor/guidance/intersection_generator.hpp"
|
#include "extractor/guidance/intersection_generator.hpp"
|
||||||
|
|
||||||
|
#include "extractor/geojson_debug_policies.hpp"
|
||||||
|
#include "util/geojson_debug_logger.hpp"
|
||||||
|
|
||||||
#include "util/bearing.hpp"
|
#include "util/bearing.hpp"
|
||||||
#include "util/coordinate_calculation.hpp"
|
#include "util/coordinate_calculation.hpp"
|
||||||
|
|
||||||
@ -76,7 +79,9 @@ IntersectionGenerator::ComputeIntersectionShape(const NodeID node_at_center_of_i
|
|||||||
node_at_center_of_intersection, edge_connected_to_intersection, !INVERT, to_node);
|
node_at_center_of_intersection, edge_connected_to_intersection, !INVERT, to_node);
|
||||||
|
|
||||||
const auto segment_length = util::coordinate_calculation::getLength(
|
const auto segment_length = util::coordinate_calculation::getLength(
|
||||||
coordinates, util::coordinate_calculation::haversineDistance);
|
coordinates.begin(),
|
||||||
|
coordinates.end(),
|
||||||
|
util::coordinate_calculation::haversineDistance);
|
||||||
|
|
||||||
const auto extract_coordinate = [&](const NodeID from_node,
|
const auto extract_coordinate = [&](const NodeID from_node,
|
||||||
const EdgeID via_eid,
|
const EdgeID via_eid,
|
||||||
@ -116,9 +121,9 @@ IntersectionGenerator::ComputeIntersectionShape(const NodeID node_at_center_of_i
|
|||||||
return node_based_graph.GetTarget(data.eid) == *sorting_base;
|
return node_based_graph.GetTarget(data.eid) == *sorting_base;
|
||||||
});
|
});
|
||||||
if (itr != intersection.end())
|
if (itr != intersection.end())
|
||||||
return util::reverseBearing(itr->bearing);
|
return util::bearing::reverse(itr->bearing);
|
||||||
}
|
}
|
||||||
return util::reverseBearing(intersection.begin()->bearing);
|
return util::bearing::reverse(intersection.begin()->bearing);
|
||||||
}();
|
}();
|
||||||
std::sort(
|
std::sort(
|
||||||
intersection.begin(), intersection.end(), makeCompareShapeDataByBearing(base_bearing));
|
intersection.begin(), intersection.end(), makeCompareShapeDataByBearing(base_bearing));
|
||||||
@ -154,8 +159,8 @@ IntersectionView IntersectionGenerator::GetConnectedRoads(const NodeID from_node
|
|||||||
return TransformIntersectionShapeIntoView(from_node, via_eid, std::move(intersection));
|
return TransformIntersectionShapeIntoView(from_node, via_eid, std::move(intersection));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<NodeID, EdgeID> IntersectionGenerator::SkipDegreeTwoNodes(const NodeID starting_node,
|
IntersectionGenerationParameters
|
||||||
const EdgeID via_edge) const
|
IntersectionGenerator::SkipDegreeTwoNodes(const NodeID starting_node, const EdgeID via_edge) const
|
||||||
{
|
{
|
||||||
NodeID query_node = starting_node;
|
NodeID query_node = starting_node;
|
||||||
EdgeID query_edge = via_edge;
|
EdgeID query_edge = via_edge;
|
||||||
@ -177,16 +182,17 @@ std::pair<NodeID, EdgeID> IntersectionGenerator::SkipDegreeTwoNodes(const NodeID
|
|||||||
visited_nodes.insert(query_node);
|
visited_nodes.insert(query_node);
|
||||||
const auto next_node = node_based_graph.GetTarget(query_edge);
|
const auto next_node = node_based_graph.GetTarget(query_edge);
|
||||||
const auto next_edge = get_next_edge(query_node, query_edge);
|
const auto next_edge = get_next_edge(query_node, query_edge);
|
||||||
|
|
||||||
|
query_node = next_node;
|
||||||
|
query_edge = next_edge;
|
||||||
|
|
||||||
if (!node_based_graph.GetEdgeData(query_edge)
|
if (!node_based_graph.GetEdgeData(query_edge)
|
||||||
.IsCompatibleTo(node_based_graph.GetEdgeData(next_edge)) ||
|
.IsCompatibleTo(node_based_graph.GetEdgeData(next_edge)) ||
|
||||||
node_based_graph.GetTarget(next_edge) == starting_node)
|
node_based_graph.GetTarget(next_edge) == starting_node)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
query_node = next_node;
|
|
||||||
query_edge = next_edge;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::make_pair(query_node, query_edge);
|
return {query_node, query_edge};
|
||||||
}
|
}
|
||||||
|
|
||||||
IntersectionView IntersectionGenerator::TransformIntersectionShapeIntoView(
|
IntersectionView IntersectionGenerator::TransformIntersectionShapeIntoView(
|
||||||
@ -205,9 +211,9 @@ IntersectionView IntersectionGenerator::TransformIntersectionShapeIntoView(
|
|||||||
IntersectionView IntersectionGenerator::TransformIntersectionShapeIntoView(
|
IntersectionView IntersectionGenerator::TransformIntersectionShapeIntoView(
|
||||||
const NodeID previous_node,
|
const NodeID previous_node,
|
||||||
const EdgeID entering_via_edge,
|
const EdgeID entering_via_edge,
|
||||||
const IntersectionShape &normalised_intersection,
|
const IntersectionShape &normalized_intersection,
|
||||||
const IntersectionShape &intersection,
|
const IntersectionShape &intersection,
|
||||||
const std::vector<std::pair<EdgeID, EdgeID>> &performed_merges) const
|
const std::vector<IntersectionNormalizationOperation> &performed_merges) const
|
||||||
{
|
{
|
||||||
const auto node_at_intersection = node_based_graph.GetTarget(entering_via_edge);
|
const auto node_at_intersection = node_based_graph.GetTarget(entering_via_edge);
|
||||||
|
|
||||||
@ -259,40 +265,40 @@ IntersectionView IntersectionGenerator::TransformIntersectionShapeIntoView(
|
|||||||
const auto uturn_bearing = [&]() {
|
const auto uturn_bearing = [&]() {
|
||||||
const auto merge_entry = std::find_if(
|
const auto merge_entry = std::find_if(
|
||||||
performed_merges.begin(), performed_merges.end(), [&uturn_edge_itr](const auto entry) {
|
performed_merges.begin(), performed_merges.end(), [&uturn_edge_itr](const auto entry) {
|
||||||
return entry.first == uturn_edge_itr->eid;
|
return entry.merged_eid == uturn_edge_itr->eid;
|
||||||
});
|
});
|
||||||
if (merge_entry != performed_merges.end())
|
if (merge_entry != performed_merges.end())
|
||||||
{
|
{
|
||||||
const auto merged_into_id = merge_entry->second;
|
const auto merged_into_id = merge_entry->into_eid;
|
||||||
const auto merged_u_turn = std::find_if(
|
const auto merged_u_turn = std::find_if(
|
||||||
normalised_intersection.begin(),
|
normalized_intersection.begin(),
|
||||||
normalised_intersection.end(),
|
normalized_intersection.end(),
|
||||||
[&](const IntersectionShapeData &road) { return road.eid == merged_into_id; });
|
[&](const IntersectionShapeData &road) { return road.eid == merged_into_id; });
|
||||||
BOOST_ASSERT(merged_u_turn != normalised_intersection.end());
|
BOOST_ASSERT(merged_u_turn != normalized_intersection.end());
|
||||||
return util::reverseBearing(merged_u_turn->bearing);
|
return util::bearing::reverse(merged_u_turn->bearing);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const auto uturn_edge_at_normalised_intersection_itr =
|
const auto uturn_edge_at_normalized_intersection_itr =
|
||||||
std::find_if(normalised_intersection.begin(),
|
std::find_if(normalized_intersection.begin(),
|
||||||
normalised_intersection.end(),
|
normalized_intersection.end(),
|
||||||
connect_to_previous_node);
|
connect_to_previous_node);
|
||||||
BOOST_ASSERT(uturn_edge_at_normalised_intersection_itr !=
|
BOOST_ASSERT(uturn_edge_at_normalized_intersection_itr !=
|
||||||
normalised_intersection.end());
|
normalized_intersection.end());
|
||||||
return util::reverseBearing(uturn_edge_at_normalised_intersection_itr->bearing);
|
return util::bearing::reverse(uturn_edge_at_normalized_intersection_itr->bearing);
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
|
|
||||||
IntersectionView intersection_view;
|
IntersectionView intersection_view;
|
||||||
intersection_view.reserve(normalised_intersection.size());
|
intersection_view.reserve(normalized_intersection.size());
|
||||||
std::transform(normalised_intersection.begin(),
|
std::transform(normalized_intersection.begin(),
|
||||||
normalised_intersection.end(),
|
normalized_intersection.end(),
|
||||||
std::back_inserter(intersection_view),
|
std::back_inserter(intersection_view),
|
||||||
[&](const IntersectionShapeData &road) {
|
[&](const IntersectionShapeData &road) {
|
||||||
return IntersectionViewData(
|
return IntersectionViewData(
|
||||||
road,
|
road,
|
||||||
is_allowed_turn(road),
|
is_allowed_turn(road),
|
||||||
util::angleBetweenBearings(uturn_bearing, road.bearing));
|
util::bearing::angleBetween(uturn_bearing, road.bearing));
|
||||||
});
|
});
|
||||||
|
|
||||||
const auto uturn_edge_at_intersection_view_itr =
|
const auto uturn_edge_at_intersection_view_itr =
|
||||||
|
@ -5,10 +5,14 @@
|
|||||||
#include "util/guidance/name_announcements.hpp"
|
#include "util/guidance/name_announcements.hpp"
|
||||||
#include "util/log.hpp"
|
#include "util/log.hpp"
|
||||||
|
|
||||||
|
#include "util/bearing.hpp"
|
||||||
|
#include "util/coordinate_calculation.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
|
||||||
using EdgeData = osrm::util::NodeBasedDynamicGraph::EdgeData;
|
using EdgeData = osrm::util::NodeBasedDynamicGraph::EdgeData;
|
||||||
|
using osrm::extractor::guidance::getTurnDirection;
|
||||||
using osrm::util::angularDeviation;
|
using osrm::util::angularDeviation;
|
||||||
|
|
||||||
namespace osrm
|
namespace osrm
|
||||||
@ -384,19 +388,17 @@ IntersectionHandler::getNextIntersection(const NodeID at, const EdgeID via) cons
|
|||||||
// Starting at node `a` via edge `e0` the intersection generator returns the intersection at `c`
|
// Starting at node `a` via edge `e0` the intersection generator returns the intersection at `c`
|
||||||
// writing `tl` (traffic signal) node and the edge `e1` which has the intersection as target.
|
// writing `tl` (traffic signal) node and the edge `e1` which has the intersection as target.
|
||||||
|
|
||||||
NodeID node = SPECIAL_NODEID;
|
const auto intersection_parameters = intersection_generator.SkipDegreeTwoNodes(at, via);
|
||||||
EdgeID edge = SPECIAL_EDGEID;
|
|
||||||
|
|
||||||
std::tie(node, edge) = intersection_generator.SkipDegreeTwoNodes(at, via);
|
|
||||||
auto intersection = intersection_generator(node, edge);
|
|
||||||
|
|
||||||
// This should never happen, guard against nevertheless
|
// This should never happen, guard against nevertheless
|
||||||
if (node == SPECIAL_NODEID || edge == SPECIAL_EDGEID)
|
if (intersection_parameters.nid == SPECIAL_NODEID ||
|
||||||
|
intersection_parameters.via_eid == SPECIAL_EDGEID)
|
||||||
{
|
{
|
||||||
return boost::none;
|
return boost::none;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto intersection_node = node_based_graph.GetTarget(edge);
|
auto intersection =
|
||||||
|
intersection_generator(intersection_parameters.nid, intersection_parameters.via_eid);
|
||||||
|
auto intersection_node = node_based_graph.GetTarget(intersection_parameters.via_eid);
|
||||||
|
|
||||||
if (intersection.size() <= 2 || intersection.isTrafficSignalOrBarrier())
|
if (intersection.size() <= 2 || intersection.isTrafficSignalOrBarrier())
|
||||||
{
|
{
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#include "extractor/guidance/intersection_normalizer.hpp"
|
#include "extractor/guidance/intersection_normalizer.hpp"
|
||||||
#include "util/bearing.hpp"
|
#include "util/bearing.hpp"
|
||||||
#include "util/coordinate_calculation.hpp"
|
#include "util/coordinate_calculation.hpp"
|
||||||
#include "util/guidance/name_announcements.hpp"
|
|
||||||
|
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
@ -21,34 +20,40 @@ IntersectionNormalizer::IntersectionNormalizer(
|
|||||||
const util::NameTable &name_table,
|
const util::NameTable &name_table,
|
||||||
const SuffixTable &street_name_suffix_table,
|
const SuffixTable &street_name_suffix_table,
|
||||||
const IntersectionGenerator &intersection_generator)
|
const IntersectionGenerator &intersection_generator)
|
||||||
: node_based_graph(node_based_graph), node_coordinates(node_coordinates),
|
: node_based_graph(node_based_graph), intersection_generator(intersection_generator),
|
||||||
name_table(name_table), street_name_suffix_table(street_name_suffix_table),
|
mergable_road_detector(node_based_graph,
|
||||||
intersection_generator(intersection_generator)
|
node_coordinates,
|
||||||
|
intersection_generator,
|
||||||
|
intersection_generator.GetCoordinateExtractor(),
|
||||||
|
name_table,
|
||||||
|
street_name_suffix_table)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<IntersectionShape, std::vector<std::pair<EdgeID, EdgeID>>> IntersectionNormalizer::
|
IntersectionNormalizer::NormalizationResult IntersectionNormalizer::
|
||||||
operator()(const NodeID node_at_intersection, IntersectionShape intersection) const
|
operator()(const NodeID node_at_intersection, IntersectionShape intersection) const
|
||||||
{
|
{
|
||||||
|
const auto intersection_copy = intersection;
|
||||||
auto merged_shape_and_merges =
|
auto merged_shape_and_merges =
|
||||||
MergeSegregatedRoads(node_at_intersection, std::move(intersection));
|
MergeSegregatedRoads(node_at_intersection, std::move(intersection));
|
||||||
merged_shape_and_merges.first = AdjustBearingsForMergeAtDestination(
|
merged_shape_and_merges.normalized_shape = AdjustBearingsForMergeAtDestination(
|
||||||
node_at_intersection, std::move(merged_shape_and_merges.first));
|
node_at_intersection, std::move(merged_shape_and_merges.normalized_shape));
|
||||||
return merged_shape_and_merges;
|
return merged_shape_and_merges;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IntersectionNormalizer::CanMerge(const NodeID intersection_node,
|
bool IntersectionNormalizer::CanMerge(const NodeID intersection_node,
|
||||||
const IntersectionShape &intersection,
|
const IntersectionShape &intersection,
|
||||||
std::size_t first_index,
|
std::size_t fist_index_in_ccw,
|
||||||
std::size_t second_index) const
|
std::size_t second_index_in_ccw) const
|
||||||
{
|
{
|
||||||
BOOST_ASSERT(((first_index + 1) % intersection.size()) == second_index);
|
BOOST_ASSERT(((fist_index_in_ccw + 1) % intersection.size()) == second_index_in_ccw);
|
||||||
|
|
||||||
// call wrapper to capture intersection_node and intersection
|
// don't merge on degree two, since it's most likely a bollard/traffic light or a round way
|
||||||
const auto mergable = [this, intersection_node, &intersection](const std::size_t left_index,
|
if (intersection.size() <= 2)
|
||||||
const std::size_t right_index) {
|
return false;
|
||||||
return InnerCanMerge(intersection_node, intersection, left_index, right_index);
|
|
||||||
};
|
const auto can_merge = mergable_road_detector.CanMergeRoad(
|
||||||
|
intersection_node, intersection[fist_index_in_ccw], intersection[second_index_in_ccw]);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Merging should never depend on order/never merge more than two roads. To ensure that we don't
|
* Merging should never depend on order/never merge more than two roads. To ensure that we don't
|
||||||
@ -56,143 +61,71 @@ bool IntersectionNormalizer::CanMerge(const NodeID intersection_node,
|
|||||||
* parking lots/border checkpoints), we check if the neigboring roads would be merged as well.
|
* parking lots/border checkpoints), we check if the neigboring roads would be merged as well.
|
||||||
* In that case, we cannot merge, since we would end up merging multiple items together
|
* In that case, we cannot merge, since we would end up merging multiple items together
|
||||||
*/
|
*/
|
||||||
if (mergable(first_index, second_index))
|
const auto is_distinct = [&]() {
|
||||||
{
|
const auto next_index_in_ccw = (second_index_in_ccw + 1) % intersection.size();
|
||||||
const auto is_distinct_merge =
|
const auto distinct_to_next_in_ccw = mergable_road_detector.IsDistinctFrom(
|
||||||
!mergable(second_index, (second_index + 1) % intersection.size()) &&
|
intersection[second_index_in_ccw], intersection[next_index_in_ccw]);
|
||||||
!mergable((first_index + intersection.size() - 1) % intersection.size(), first_index) &&
|
const auto prev_index_in_ccw =
|
||||||
!mergable(second_index,
|
(fist_index_in_ccw + intersection.size() - 1) % intersection.size();
|
||||||
(first_index + intersection.size() - 1) % intersection.size()) &&
|
const auto distinct_to_prev_in_ccw = mergable_road_detector.IsDistinctFrom(
|
||||||
!mergable(first_index, (second_index + 1) % intersection.size());
|
intersection[prev_index_in_ccw], intersection[fist_index_in_ccw]);
|
||||||
return is_distinct_merge;
|
return distinct_to_next_in_ccw && distinct_to_prev_in_ccw;
|
||||||
}
|
};
|
||||||
else
|
|
||||||
return false;
|
// use lazy evaluation to check only if mergable
|
||||||
|
return can_merge && is_distinct();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks for mergability of two ways that represent the same intersection. For further
|
IntersectionNormalizationOperation
|
||||||
// information see interface documentation in header.
|
IntersectionNormalizer::DetermineMergeDirection(const IntersectionShapeData &lhs,
|
||||||
bool IntersectionNormalizer::InnerCanMerge(const NodeID node_at_intersection,
|
const IntersectionShapeData &rhs) const
|
||||||
const IntersectionShape &intersection,
|
|
||||||
std::size_t first_index,
|
|
||||||
std::size_t second_index) const
|
|
||||||
{
|
{
|
||||||
const auto &first_data = node_based_graph.GetEdgeData(intersection[first_index].eid);
|
if (node_based_graph.GetEdgeData(lhs.eid).reversed)
|
||||||
const auto &second_data = node_based_graph.GetEdgeData(intersection[second_index].eid);
|
return {lhs.eid, rhs.eid};
|
||||||
|
|
||||||
// only merge named ids
|
|
||||||
if (first_data.name_id == EMPTY_NAMEID || second_data.name_id == EMPTY_NAMEID)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// need to be same name
|
|
||||||
if (util::guidance::requiresNameAnnounced(
|
|
||||||
first_data.name_id, second_data.name_id, name_table, street_name_suffix_table))
|
|
||||||
return false;
|
|
||||||
// needs to be symmetrical for names
|
|
||||||
if (util::guidance::requiresNameAnnounced(
|
|
||||||
second_data.name_id, first_data.name_id, name_table, street_name_suffix_table))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// compatibility is required
|
|
||||||
if (first_data.travel_mode != second_data.travel_mode)
|
|
||||||
return false;
|
|
||||||
if (first_data.road_classification != second_data.road_classification)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// may not be on a roundabout
|
|
||||||
if (first_data.roundabout || second_data.roundabout || first_data.circular ||
|
|
||||||
second_data.circular)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// exactly one of them has to be reversed
|
|
||||||
if (first_data.reversed == second_data.reversed)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// mergeable if the angle is not too big
|
|
||||||
const auto angle_between =
|
|
||||||
angularDeviation(intersection[first_index].bearing, intersection[second_index].bearing);
|
|
||||||
|
|
||||||
const auto coordinate_at_intersection = node_coordinates[node_at_intersection];
|
|
||||||
|
|
||||||
if (angle_between >= 120)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const auto isValidYArm = [this, intersection, coordinate_at_intersection, node_at_intersection](
|
|
||||||
const std::size_t index, const std::size_t other_index) {
|
|
||||||
const auto GetActualTarget = [&](const std::size_t index) {
|
|
||||||
EdgeID edge_id;
|
|
||||||
std::tie(std::ignore, edge_id) = intersection_generator.SkipDegreeTwoNodes(
|
|
||||||
node_at_intersection, intersection[index].eid);
|
|
||||||
return node_based_graph.GetTarget(edge_id);
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto target_id = GetActualTarget(index);
|
|
||||||
const auto other_target_id = GetActualTarget(other_index);
|
|
||||||
if (target_id == node_at_intersection || other_target_id == node_at_intersection)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const auto coordinate_at_target = node_coordinates[target_id];
|
|
||||||
const auto coordinate_at_other_target = node_coordinates[other_target_id];
|
|
||||||
|
|
||||||
const auto turn_bearing =
|
|
||||||
util::coordinate_calculation::bearing(coordinate_at_intersection, coordinate_at_target);
|
|
||||||
const auto other_turn_bearing = util::coordinate_calculation::bearing(
|
|
||||||
coordinate_at_intersection, coordinate_at_other_target);
|
|
||||||
|
|
||||||
// fuzzy becomes narrower due to minor differences in angle computations, yay floating point
|
|
||||||
const bool becomes_narrower =
|
|
||||||
angularDeviation(turn_bearing, other_turn_bearing) < NARROW_TURN_ANGLE &&
|
|
||||||
angularDeviation(turn_bearing, other_turn_bearing) <=
|
|
||||||
angularDeviation(intersection[index].bearing, intersection[other_index].bearing) +
|
|
||||||
MAXIMAL_ALLOWED_NO_TURN_DEVIATION;
|
|
||||||
|
|
||||||
return becomes_narrower;
|
|
||||||
};
|
|
||||||
|
|
||||||
const bool is_y_arm_first = isValidYArm(first_index, second_index);
|
|
||||||
const bool is_y_arm_second = isValidYArm(second_index, first_index);
|
|
||||||
|
|
||||||
// Only merge valid y-arms
|
|
||||||
if (!is_y_arm_first || !is_y_arm_second)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (angle_between < 60)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Finally, we also allow merging if all streets offer the same name, it is only three roads and
|
|
||||||
// the angle is not fully extreme:
|
|
||||||
if (intersection.size() != 3)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// since we have an intersection of size three now, there is only one index we are not looking
|
|
||||||
// at right now. The final index in the intersection is calculated next:
|
|
||||||
const std::size_t third_index = [first_index, second_index]() {
|
|
||||||
if (first_index == 0)
|
|
||||||
return second_index == 2 ? 1 : 2;
|
|
||||||
else if (first_index == 1)
|
|
||||||
return second_index == 2 ? 0 : 2;
|
|
||||||
else
|
else
|
||||||
return second_index == 1 ? 0 : 1;
|
return {rhs.eid, lhs.eid};
|
||||||
}();
|
}
|
||||||
|
|
||||||
// needs to be same road coming in
|
IntersectionShapeData IntersectionNormalizer::MergeRoads(const IntersectionShapeData &into,
|
||||||
const auto &third_data = node_based_graph.GetEdgeData(intersection[third_index].eid);
|
const IntersectionShapeData &from) const
|
||||||
|
{
|
||||||
|
// we only merge small angles. If the difference between both is large, we are looking at a
|
||||||
|
// bearing leading north. Such a bearing cannot be handled via the basic average. In this
|
||||||
|
// case we actually need to shift the bearing by half the difference.
|
||||||
|
const auto aroundZero = [](const double first, const double second) {
|
||||||
|
return (std::max(first, second) - std::min(first, second)) >= 180;
|
||||||
|
};
|
||||||
|
|
||||||
if (third_data.name_id != EMPTY_NAMEID &&
|
// find the angle between two other angles
|
||||||
util::guidance::requiresNameAnnounced(
|
const auto combineAngles = [aroundZero](const double first, const double second) {
|
||||||
third_data.name_id, first_data.name_id, name_table, street_name_suffix_table))
|
if (!aroundZero(first, second))
|
||||||
return false;
|
return .5 * (first + second);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const auto offset = angularDeviation(first, second);
|
||||||
|
auto new_angle = std::max(first, second) + .5 * offset;
|
||||||
|
if (new_angle >= 360)
|
||||||
|
return new_angle - 360;
|
||||||
|
return new_angle;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// we only allow collapsing of a Y like fork. So the angle to the third index has to be
|
auto result = into;
|
||||||
// roughly equal:
|
BOOST_ASSERT(!node_based_graph.GetEdgeData(into.eid).reversed);
|
||||||
const auto y_angle_difference = angularDeviation(
|
result.bearing = combineAngles(into.bearing, from.bearing);
|
||||||
angularDeviation(intersection[third_index].bearing, intersection[first_index].bearing),
|
BOOST_ASSERT(0 <= result.bearing && result.bearing < 360.0);
|
||||||
angularDeviation(intersection[third_index].bearing, intersection[second_index].bearing));
|
return result;
|
||||||
// Allow larger angles if its three roads only of the same name
|
}
|
||||||
// This is a heuristic and might need to be revised.
|
|
||||||
const bool assume_y_intersection =
|
IntersectionShapeData
|
||||||
angle_between < 100 && y_angle_difference < FUZZY_ANGLE_DIFFERENCE;
|
IntersectionNormalizer::MergeRoads(const IntersectionNormalizationOperation direction,
|
||||||
return assume_y_intersection;
|
const IntersectionShapeData &lhs,
|
||||||
|
const IntersectionShapeData &rhs) const
|
||||||
|
{
|
||||||
|
if (direction.merged_eid == lhs.eid)
|
||||||
|
return MergeRoads(rhs, lhs);
|
||||||
|
else
|
||||||
|
return MergeRoads(lhs, rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -218,7 +151,7 @@ bool IntersectionNormalizer::InnerCanMerge(const NodeID node_at_intersection,
|
|||||||
* Anything containing the first u-turn in a merge affects all other angles
|
* Anything containing the first u-turn in a merge affects all other angles
|
||||||
* and is handled separately from all others.
|
* and is handled separately from all others.
|
||||||
*/
|
*/
|
||||||
std::pair<IntersectionShape, std::vector<std::pair<EdgeID, EdgeID>>>
|
IntersectionNormalizer::NormalizationResult
|
||||||
IntersectionNormalizer::MergeSegregatedRoads(const NodeID intersection_node,
|
IntersectionNormalizer::MergeSegregatedRoads(const NodeID intersection_node,
|
||||||
IntersectionShape intersection) const
|
IntersectionShape intersection) const
|
||||||
{
|
{
|
||||||
@ -226,50 +159,25 @@ IntersectionNormalizer::MergeSegregatedRoads(const NodeID intersection_node,
|
|||||||
return (index + intersection.size() - 1) % intersection.size();
|
return (index + intersection.size() - 1) % intersection.size();
|
||||||
};
|
};
|
||||||
|
|
||||||
// we only merge small angles. If the difference between both is large, we are looking at a
|
|
||||||
// bearing leading north. Such a bearing cannot be handled via the basic average. In this
|
|
||||||
// case we actually need to shift the bearing by half the difference.
|
|
||||||
const auto aroundZero = [](const double first, const double second) {
|
|
||||||
return (std::max(first, second) - std::min(first, second)) >= 180;
|
|
||||||
};
|
|
||||||
|
|
||||||
// find the angle between two other angles
|
|
||||||
const auto combineAngles = [aroundZero](const double first, const double second) {
|
|
||||||
if (!aroundZero(first, second))
|
|
||||||
return .5 * (first + second);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const auto offset = angularDeviation(first, second);
|
|
||||||
auto new_angle = std::max(first, second) + .5 * offset;
|
|
||||||
if (new_angle >= 360)
|
|
||||||
return new_angle - 360;
|
|
||||||
return new_angle;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// This map stores for all edges that participated in a merging operation in which edge id they
|
// This map stores for all edges that participated in a merging operation in which edge id they
|
||||||
// end up in the end. We only store what we have merged into other edges.
|
// end up in the end. We only store what we have merged into other edges.
|
||||||
std::vector<std::pair<EdgeID, EdgeID>> merging_map;
|
std::vector<IntersectionNormalizationOperation> merging_map;
|
||||||
|
const auto merge = [this, &merging_map](const IntersectionShapeData &first,
|
||||||
const auto merge = [this, combineAngles, &merging_map](const IntersectionShapeData &first,
|
|
||||||
const IntersectionShapeData &second) {
|
const IntersectionShapeData &second) {
|
||||||
IntersectionShapeData result =
|
|
||||||
!node_based_graph.GetEdgeData(first.eid).reversed ? first : second;
|
const auto direction = DetermineMergeDirection(first, second);
|
||||||
result.bearing = combineAngles(first.bearing, second.bearing);
|
|
||||||
BOOST_ASSERT(0 <= result.bearing && result.bearing < 360.0);
|
|
||||||
// the other ID
|
|
||||||
const auto merged_from = result.eid == first.eid ? second.eid : first.eid;
|
|
||||||
BOOST_ASSERT(
|
BOOST_ASSERT(
|
||||||
std::find_if(merging_map.begin(), merging_map.end(), [merged_from](const auto pair) {
|
std::find_if(merging_map.begin(), merging_map.end(), [direction](const auto pair) {
|
||||||
return pair.first == merged_from;
|
return pair.merged_eid == direction.merged_eid;
|
||||||
}) == merging_map.end());
|
}) == merging_map.end());
|
||||||
merging_map.push_back(std::make_pair(merged_from, result.eid));
|
merging_map.push_back(direction);
|
||||||
return result;
|
return MergeRoads(direction, first, second);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (intersection.size() <= 1)
|
if (intersection.size() <= 1)
|
||||||
return std::make_pair(intersection, merging_map);
|
return {intersection, merging_map};
|
||||||
|
|
||||||
|
const auto intersection_copy = intersection;
|
||||||
// check for merges including the basic u-turn
|
// check for merges including the basic u-turn
|
||||||
// these result in an adjustment of all other angles. This is due to how these angles are
|
// these result in an adjustment of all other angles. This is due to how these angles are
|
||||||
// perceived. Considering the following example:
|
// perceived. Considering the following example:
|
||||||
@ -301,11 +209,9 @@ IntersectionNormalizer::MergeSegregatedRoads(const NodeID intersection_node,
|
|||||||
// If the merge occurs at the u-turn edge, we need to adjust all angles, though, since they are
|
// If the merge occurs at the u-turn edge, we need to adjust all angles, though, since they are
|
||||||
// with respect to the now changed perceived location of a. If we move (a) to the left, we add
|
// with respect to the now changed perceived location of a. If we move (a) to the left, we add
|
||||||
// the difference to all angles. Otherwise we subtract it.
|
// the difference to all angles. Otherwise we subtract it.
|
||||||
bool merged_first = false;
|
|
||||||
// these result in an adjustment of all other angles
|
// these result in an adjustment of all other angles
|
||||||
if (CanMerge(intersection_node, intersection, intersection.size() - 1, 0))
|
if (CanMerge(intersection_node, intersection, intersection.size() - 1, 0))
|
||||||
{
|
{
|
||||||
merged_first = true;
|
|
||||||
// moving `a` to the left
|
// moving `a` to the left
|
||||||
intersection[0] = merge(intersection.front(), intersection.back());
|
intersection[0] = merge(intersection.front(), intersection.back());
|
||||||
// FIXME if we have a left-sided country, we need to switch this off and enable it
|
// FIXME if we have a left-sided country, we need to switch this off and enable it
|
||||||
@ -314,7 +220,6 @@ IntersectionNormalizer::MergeSegregatedRoads(const NodeID intersection_node,
|
|||||||
}
|
}
|
||||||
else if (CanMerge(intersection_node, intersection, 0, 1))
|
else if (CanMerge(intersection_node, intersection, 0, 1))
|
||||||
{
|
{
|
||||||
merged_first = true;
|
|
||||||
intersection[0] = merge(intersection.front(), intersection[1]);
|
intersection[0] = merge(intersection.front(), intersection[1]);
|
||||||
intersection.erase(intersection.begin() + 1);
|
intersection.erase(intersection.begin() + 1);
|
||||||
}
|
}
|
||||||
@ -331,8 +236,7 @@ IntersectionNormalizer::MergeSegregatedRoads(const NodeID intersection_node,
|
|||||||
--index;
|
--index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return {intersection, merging_map};
|
||||||
return std::make_pair(intersection, merging_map);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// OSM can have some very steep angles for joining roads. Considering the following intersection:
|
// OSM can have some very steep angles for joining roads. Considering the following intersection:
|
||||||
@ -432,7 +336,7 @@ IntersectionNormalizer::AdjustBearingsForMergeAtDestination(const NodeID node_at
|
|||||||
intersection[(intersection.size() + index - 1) % intersection.size()]);
|
intersection[(intersection.size() + index - 1) % intersection.size()]);
|
||||||
// at the target intersection, we merge to the right, so we need to shift the current
|
// at the target intersection, we merge to the right, so we need to shift the current
|
||||||
// angle to the left
|
// angle to the left
|
||||||
road.bearing = adjustAngle(road.bearing, -corrected_offset);
|
road.bearing = adjustAngle(road.bearing, corrected_offset);
|
||||||
}
|
}
|
||||||
else if (CanMerge(node_at_next_intersection,
|
else if (CanMerge(node_at_next_intersection,
|
||||||
next_intersection_along_road,
|
next_intersection_along_road,
|
||||||
@ -447,7 +351,7 @@ IntersectionNormalizer::AdjustBearingsForMergeAtDestination(const NodeID node_at
|
|||||||
get_corrected_offset(offset, road, intersection[(index + 1) % intersection.size()]);
|
get_corrected_offset(offset, road, intersection[(index + 1) % intersection.size()]);
|
||||||
// at the target intersection, we merge to the left, so we need to shift the current
|
// at the target intersection, we merge to the left, so we need to shift the current
|
||||||
// angle to the right
|
// angle to the right
|
||||||
road.bearing = adjustAngle(road.bearing, corrected_offset);
|
road.bearing = adjustAngle(road.bearing, -corrected_offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return intersection;
|
return intersection;
|
||||||
|
469
src/extractor/guidance/mergable_road_detector.cpp
Normal file
469
src/extractor/guidance/mergable_road_detector.cpp
Normal file
@ -0,0 +1,469 @@
|
|||||||
|
#include "extractor/guidance/mergable_road_detector.hpp"
|
||||||
|
#include "extractor/guidance/constants.hpp"
|
||||||
|
#include "extractor/guidance/coordinate_extractor.hpp"
|
||||||
|
#include "extractor/guidance/intersection_generator.hpp"
|
||||||
|
#include "extractor/guidance/node_based_graph_walker.hpp"
|
||||||
|
#include "extractor/query_node.hpp"
|
||||||
|
#include "extractor/suffix_table.hpp"
|
||||||
|
|
||||||
|
#include "util/bearing.hpp"
|
||||||
|
#include "util/coordinate_calculation.hpp"
|
||||||
|
#include "util/guidance/name_announcements.hpp"
|
||||||
|
#include "util/name_table.hpp"
|
||||||
|
|
||||||
|
using osrm::util::angularDeviation;
|
||||||
|
|
||||||
|
namespace osrm
|
||||||
|
{
|
||||||
|
namespace extractor
|
||||||
|
{
|
||||||
|
namespace guidance
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
// check a connected road for equality of a name
|
||||||
|
inline auto makeCheckRoadForName(const NameID name_id,
|
||||||
|
const util::NodeBasedDynamicGraph &node_based_graph,
|
||||||
|
const util::NameTable &name_table,
|
||||||
|
const SuffixTable &suffix_table)
|
||||||
|
{
|
||||||
|
return [name_id, &node_based_graph, &name_table, &suffix_table](
|
||||||
|
const MergableRoadDetector::MergableRoadData &road) {
|
||||||
|
// since we filter here, we don't want any other name than the one we are looking for
|
||||||
|
const auto road_name = node_based_graph.GetEdgeData(road.eid).name_id;
|
||||||
|
if (name_id == EMPTY_NAMEID || road_name == EMPTY_NAMEID)
|
||||||
|
return true;
|
||||||
|
const auto requires_announcement =
|
||||||
|
util::guidance::requiresNameAnnounced(name_id, road_name, name_table, suffix_table) ||
|
||||||
|
util::guidance::requiresNameAnnounced(road_name, name_id, name_table, suffix_table);
|
||||||
|
|
||||||
|
return requires_announcement;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MergableRoadDetector::MergableRoadDetector(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||||
|
const std::vector<QueryNode> &node_coordinates,
|
||||||
|
const IntersectionGenerator &intersection_generator,
|
||||||
|
const CoordinateExtractor &coordinate_extractor,
|
||||||
|
const util::NameTable &name_table,
|
||||||
|
const SuffixTable &street_name_suffix_table)
|
||||||
|
: node_based_graph(node_based_graph), node_coordinates(node_coordinates),
|
||||||
|
intersection_generator(intersection_generator), coordinate_extractor(coordinate_extractor),
|
||||||
|
name_table(name_table), street_name_suffix_table(street_name_suffix_table)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MergableRoadDetector::CanMergeRoad(const NodeID intersection_node,
|
||||||
|
const IntersectionShapeData &lhs,
|
||||||
|
const IntersectionShapeData &rhs) const
|
||||||
|
{
|
||||||
|
// roads should be somewhat close
|
||||||
|
if (angularDeviation(lhs.bearing, rhs.bearing) > MERGABLE_ANGLE_DIFFERENCE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const auto &lhs_edge_data = node_based_graph.GetEdgeData(lhs.eid);
|
||||||
|
const auto &rhs_edge_data = node_based_graph.GetEdgeData(rhs.eid);
|
||||||
|
|
||||||
|
// and they need to describe the same road
|
||||||
|
if (!EdgeDataSupportsMerge(lhs_edge_data, rhs_edge_data))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* don't use any circular links, since they mess up detection we jump out early.
|
||||||
|
*
|
||||||
|
* / -- \
|
||||||
|
* a ---- b - - /
|
||||||
|
*/
|
||||||
|
const auto road_target = [this](const MergableRoadData &road) {
|
||||||
|
return node_based_graph.GetTarget(road.eid);
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO might have to skip over trivial intersections
|
||||||
|
if (road_target(lhs) == intersection_node || road_target(lhs) == intersection_node)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Don't merge turning circles/traffic loops
|
||||||
|
if (IsTrafficLoop(intersection_node, lhs) || IsTrafficLoop(intersection_node, rhs))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// needs to be checked prior to link roads, since connections can seem like links
|
||||||
|
if (IsTrafficIsland(intersection_node, lhs, rhs))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Don't merge link roads
|
||||||
|
if (IsLinkRoad(intersection_node, lhs) || IsLinkRoad(intersection_node, rhs))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// check if we simply split up prior to an intersection
|
||||||
|
if (IsNarrowTriangle(intersection_node, lhs, rhs))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// finally check if two roads describe the direction
|
||||||
|
return HaveSameDirection(intersection_node, lhs, rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MergableRoadDetector::HaveIdenticalNames(const NameID lhs, const NameID rhs) const
|
||||||
|
{
|
||||||
|
const auto non_empty = (lhs != EMPTY_NAMEID) && (rhs != EMPTY_NAMEID);
|
||||||
|
|
||||||
|
// symmetrical check for announcements
|
||||||
|
return non_empty &&
|
||||||
|
!util::guidance::requiresNameAnnounced(lhs, rhs, name_table, street_name_suffix_table) &&
|
||||||
|
!util::guidance::requiresNameAnnounced(rhs, lhs, name_table, street_name_suffix_table);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MergableRoadDetector::IsDistinctFrom(const MergableRoadData &lhs,
|
||||||
|
const MergableRoadData &rhs) const
|
||||||
|
{
|
||||||
|
// needs to be far away
|
||||||
|
if (angularDeviation(lhs.bearing, rhs.bearing) > MERGABLE_ANGLE_DIFFERENCE)
|
||||||
|
return true;
|
||||||
|
else // or it cannot have the same name
|
||||||
|
return !HaveIdenticalNames(node_based_graph.GetEdgeData(lhs.eid).name_id,
|
||||||
|
node_based_graph.GetEdgeData(rhs.eid).name_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MergableRoadDetector::EdgeDataSupportsMerge(const util::NodeBasedEdgeData &lhs_edge_data,
|
||||||
|
const util::NodeBasedEdgeData &rhs_edge_data) const
|
||||||
|
{
|
||||||
|
// roundabouts are special, simply don't hurt them. We might not want to bear the
|
||||||
|
// consequences
|
||||||
|
if (lhs_edge_data.roundabout || rhs_edge_data.roundabout)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* to describe the same road, but in opposite directions (which is what we require for a
|
||||||
|
* merge), the roads have to feature one reversed and one non-reversed edge
|
||||||
|
*/
|
||||||
|
if (lhs_edge_data.reversed == rhs_edge_data.reversed)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* The travel mode should be the same for both roads. If we were to merge different travel
|
||||||
|
* modes, we would hide information/run the risk of loosing valid choices (e.g. short period
|
||||||
|
* of pushing)
|
||||||
|
*/
|
||||||
|
if (lhs_edge_data.travel_mode != rhs_edge_data.travel_mode)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// we require valid names
|
||||||
|
if (!HaveIdenticalNames(lhs_edge_data.name_id, rhs_edge_data.name_id))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return lhs_edge_data.road_classification == rhs_edge_data.road_classification;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MergableRoadDetector::IsTrafficLoop(const NodeID intersection_node,
|
||||||
|
const MergableRoadData &road) const
|
||||||
|
{
|
||||||
|
const auto connection = intersection_generator.SkipDegreeTwoNodes(intersection_node, road.eid);
|
||||||
|
return intersection_node == node_based_graph.GetTarget(connection.via_eid);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MergableRoadDetector::IsNarrowTriangle(const NodeID intersection_node,
|
||||||
|
const MergableRoadData &lhs,
|
||||||
|
const MergableRoadData &rhs) const
|
||||||
|
{
|
||||||
|
// selection data to the right and left
|
||||||
|
const auto constexpr SMALL_RANDOM_HOPLIMIT = 5;
|
||||||
|
IntersectionFinderAccumulator left_accumulator(SMALL_RANDOM_HOPLIMIT, intersection_generator),
|
||||||
|
right_accumulator(SMALL_RANDOM_HOPLIMIT, intersection_generator);
|
||||||
|
|
||||||
|
/* Standard following the straightmost road
|
||||||
|
* Since both items have the same id, we can `select` based on any setup
|
||||||
|
*/
|
||||||
|
SelectStraightmostRoadByNameAndOnlyChoice selector(
|
||||||
|
node_based_graph.GetEdgeData(lhs.eid).name_id, lhs.bearing, /*requires entry=*/false);
|
||||||
|
|
||||||
|
NodeBasedGraphWalker graph_walker(node_based_graph, intersection_generator);
|
||||||
|
graph_walker.TraverseRoad(intersection_node, lhs.eid, left_accumulator, selector);
|
||||||
|
/* if the intersection does not have a right turn, we continue onto the next one once
|
||||||
|
* (skipping over a single small side street)
|
||||||
|
*/
|
||||||
|
if (angularDeviation(left_accumulator.intersection.findClosestTurn(ORTHOGONAL_ANGLE)->angle,
|
||||||
|
ORTHOGONAL_ANGLE) > NARROW_TURN_ANGLE)
|
||||||
|
{
|
||||||
|
graph_walker.TraverseRoad(
|
||||||
|
node_based_graph.GetTarget(left_accumulator.via_edge_id),
|
||||||
|
left_accumulator.intersection.findClosestTurn(STRAIGHT_ANGLE)->eid,
|
||||||
|
left_accumulator,
|
||||||
|
selector);
|
||||||
|
}
|
||||||
|
const auto distance_to_triangle = util::coordinate_calculation::haversineDistance(
|
||||||
|
node_coordinates[intersection_node],
|
||||||
|
node_coordinates[node_based_graph.GetTarget(left_accumulator.via_edge_id)]);
|
||||||
|
|
||||||
|
// don't move too far down the road
|
||||||
|
const constexpr auto RANGE_TO_TRIANGLE_LIMIT = 80;
|
||||||
|
if (distance_to_triangle > RANGE_TO_TRIANGLE_LIMIT)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
graph_walker.TraverseRoad(intersection_node, rhs.eid, right_accumulator, selector);
|
||||||
|
if (angularDeviation(right_accumulator.intersection.findClosestTurn(270)->angle, 270) >
|
||||||
|
NARROW_TURN_ANGLE)
|
||||||
|
{
|
||||||
|
graph_walker.TraverseRoad(
|
||||||
|
node_based_graph.GetTarget(right_accumulator.via_edge_id),
|
||||||
|
right_accumulator.intersection.findClosestTurn(STRAIGHT_ANGLE)->eid,
|
||||||
|
right_accumulator,
|
||||||
|
selector);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_ASSERT(!left_accumulator.intersection.empty() && !right_accumulator.intersection.empty());
|
||||||
|
|
||||||
|
// find the closes resembling a right turn
|
||||||
|
const auto connector_turn = left_accumulator.intersection.findClosestTurn(ORTHOGONAL_ANGLE);
|
||||||
|
/* check if that right turn connects to the right_accumulator intersection (i.e. we have a
|
||||||
|
* triangle)
|
||||||
|
* a connection should be somewhat to the right, when looking at the left side of the
|
||||||
|
* triangle
|
||||||
|
*
|
||||||
|
* b ..... c
|
||||||
|
* \ /
|
||||||
|
* \ /
|
||||||
|
* \ /
|
||||||
|
* a
|
||||||
|
*
|
||||||
|
* e.g. here when looking at `a,b`, a narrow triangle should offer a turn to the right, when
|
||||||
|
* we want to connect to c
|
||||||
|
*/
|
||||||
|
if (angularDeviation(connector_turn->angle, ORTHOGONAL_ANGLE) > NARROW_TURN_ANGLE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const auto num_lanes = [this](const MergableRoadData &road) {
|
||||||
|
return std::max<std::uint8_t>(
|
||||||
|
node_based_graph.GetEdgeData(road.eid).road_classification.GetNumberOfLanes(), 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
// the width we can bridge at the intersection
|
||||||
|
const auto assumed_road_width = (num_lanes(lhs) + num_lanes(rhs)) * ASSUMED_LANE_WIDTH;
|
||||||
|
const constexpr auto MAXIMAL_ALLOWED_TRAFFIC_ISLAND_WIDTH = 10;
|
||||||
|
const auto distance_between_triangle_corners = util::coordinate_calculation::haversineDistance(
|
||||||
|
node_coordinates[node_based_graph.GetTarget(left_accumulator.via_edge_id)],
|
||||||
|
node_coordinates[node_based_graph.GetTarget(right_accumulator.via_edge_id)]);
|
||||||
|
if (distance_between_triangle_corners >
|
||||||
|
(assumed_road_width + MAXIMAL_ALLOWED_TRAFFIC_ISLAND_WIDTH))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// check if both intersections are connected
|
||||||
|
IntersectionFinderAccumulator connect_accumulator(SMALL_RANDOM_HOPLIMIT,
|
||||||
|
intersection_generator);
|
||||||
|
graph_walker.TraverseRoad(node_based_graph.GetTarget(left_accumulator.via_edge_id),
|
||||||
|
connector_turn->eid,
|
||||||
|
connect_accumulator,
|
||||||
|
selector);
|
||||||
|
// the if both items are connected
|
||||||
|
return node_based_graph.GetTarget(connect_accumulator.via_edge_id) ==
|
||||||
|
node_based_graph.GetTarget(right_accumulator.via_edge_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MergableRoadDetector::HaveSameDirection(const NodeID intersection_node,
|
||||||
|
const MergableRoadData &lhs,
|
||||||
|
const MergableRoadData &rhs) const
|
||||||
|
{
|
||||||
|
if (angularDeviation(lhs.bearing, rhs.bearing) > MERGABLE_ANGLE_DIFFERENCE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Find a coordinate following a road that is far away
|
||||||
|
NodeBasedGraphWalker graph_walker(node_based_graph, intersection_generator);
|
||||||
|
const auto getCoordinatesAlongWay = [&](const EdgeID edge_id, const double max_length) {
|
||||||
|
LengthLimitedCoordinateAccumulator accumulator(coordinate_extractor, max_length);
|
||||||
|
SelectStraightmostRoadByNameAndOnlyChoice selector(
|
||||||
|
node_based_graph.GetEdgeData(edge_id).name_id, lhs.bearing, /*requires_entry=*/false);
|
||||||
|
graph_walker.TraverseRoad(intersection_node, edge_id, accumulator, selector);
|
||||||
|
|
||||||
|
return std::make_pair(accumulator.accumulated_length, accumulator.coordinates);
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<util::Coordinate> coordinates_to_the_left, coordinates_to_the_right;
|
||||||
|
double distance_traversed_to_the_left, distance_traversed_to_the_right;
|
||||||
|
|
||||||
|
// many roads only do short parallel segments. To get a good impression of how `parallel` two
|
||||||
|
// roads are, we look 100 meters down the road (wich can be quite short for very broad roads).
|
||||||
|
const double constexpr distance_to_extract = 100;
|
||||||
|
|
||||||
|
std::tie(distance_traversed_to_the_left, coordinates_to_the_left) =
|
||||||
|
getCoordinatesAlongWay(lhs.eid, distance_to_extract);
|
||||||
|
|
||||||
|
// tuned parameter, if we didn't get as far as 40 meters, we might barely look past an
|
||||||
|
// intersection.
|
||||||
|
const auto constexpr MINIMUM_LENGTH_FOR_PARALLEL_DETECTION = 40;
|
||||||
|
// quit early if the road is not very long
|
||||||
|
if (distance_traversed_to_the_left <= MINIMUM_LENGTH_FOR_PARALLEL_DETECTION)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::tie(distance_traversed_to_the_right, coordinates_to_the_right) =
|
||||||
|
getCoordinatesAlongWay(rhs.eid, distance_to_extract);
|
||||||
|
|
||||||
|
if (distance_traversed_to_the_right <= MINIMUM_LENGTH_FOR_PARALLEL_DETECTION)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const auto connect_again = (coordinates_to_the_left.back() == coordinates_to_the_right.back());
|
||||||
|
|
||||||
|
// sampling to correctly weight longer segments in regression calculations
|
||||||
|
const auto constexpr SAMPLE_INTERVAL = 5;
|
||||||
|
coordinates_to_the_left = coordinate_extractor.SampleCoordinates(
|
||||||
|
std::move(coordinates_to_the_left), distance_to_extract, SAMPLE_INTERVAL);
|
||||||
|
|
||||||
|
coordinates_to_the_right = coordinate_extractor.SampleCoordinates(
|
||||||
|
std::move(coordinates_to_the_right), distance_to_extract, SAMPLE_INTERVAL);
|
||||||
|
|
||||||
|
/* extract the number of lanes for a road
|
||||||
|
* restricts a vector to the last two thirds of the length
|
||||||
|
*/
|
||||||
|
const auto prune = [](auto &data_vector) {
|
||||||
|
BOOST_ASSERT(data_vector.size() >= 3);
|
||||||
|
//erase the first third of the vector
|
||||||
|
data_vector.erase(data_vector.begin(), data_vector.begin() + data_vector.size() / 3);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* if the coordinates meet up again, e.g. due to a split and join, pruning can have a negative
|
||||||
|
* effect. We therefore only prune away the beginning, if the roads don't meet up again as well.
|
||||||
|
*/
|
||||||
|
if (!connect_again)
|
||||||
|
{
|
||||||
|
prune(coordinates_to_the_left);
|
||||||
|
prune(coordinates_to_the_right);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto are_parallel =
|
||||||
|
util::coordinate_calculation::areParallel(coordinates_to_the_left.begin(),
|
||||||
|
coordinates_to_the_left.end(),
|
||||||
|
coordinates_to_the_right.begin(),
|
||||||
|
coordinates_to_the_right.end());
|
||||||
|
|
||||||
|
if (!are_parallel)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// compare reference distance:
|
||||||
|
const auto distance_between_roads = util::coordinate_calculation::findClosestDistance(
|
||||||
|
coordinates_to_the_left[coordinates_to_the_left.size() / 2],
|
||||||
|
coordinates_to_the_right.begin(),
|
||||||
|
coordinates_to_the_right.end());
|
||||||
|
|
||||||
|
const auto lane_count_lhs = std::max<int>(
|
||||||
|
1, node_based_graph.GetEdgeData(lhs.eid).road_classification.GetNumberOfLanes());
|
||||||
|
const auto lane_count_rhs = std::max<int>(
|
||||||
|
1, node_based_graph.GetEdgeData(rhs.eid).road_classification.GetNumberOfLanes());
|
||||||
|
|
||||||
|
const auto combined_road_width = 0.5 * (lane_count_lhs + lane_count_rhs) * ASSUMED_LANE_WIDTH;
|
||||||
|
const auto constexpr MAXIMAL_ALLOWED_SEPARATION_WIDTH = 8;
|
||||||
|
return distance_between_roads <= combined_road_width + MAXIMAL_ALLOWED_SEPARATION_WIDTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MergableRoadDetector::IsTrafficIsland(const NodeID intersection_node,
|
||||||
|
const MergableRoadData &lhs,
|
||||||
|
const MergableRoadData &rhs) const
|
||||||
|
{
|
||||||
|
/* compute the set of all intersection_nodes along the way of an edge, until it reaches a
|
||||||
|
* location with the same name repeatet at least three times
|
||||||
|
*/
|
||||||
|
const auto left_connection =
|
||||||
|
intersection_generator.SkipDegreeTwoNodes(intersection_node, lhs.eid);
|
||||||
|
const auto right_connection =
|
||||||
|
intersection_generator.SkipDegreeTwoNodes(intersection_node, rhs.eid);
|
||||||
|
|
||||||
|
const auto left_candidate = node_based_graph.GetTarget(left_connection.via_eid);
|
||||||
|
const auto right_candidate = node_based_graph.GetTarget(right_connection.via_eid);
|
||||||
|
|
||||||
|
const auto candidate_is_valid =
|
||||||
|
left_candidate == right_candidate && left_candidate != intersection_node;
|
||||||
|
|
||||||
|
if (!candidate_is_valid)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// check if all entries at the destination or at the source are the same
|
||||||
|
const auto all_same_name_and_degree_three = [this](const NodeID nid) {
|
||||||
|
// check if the intersection found has degree three
|
||||||
|
if (node_based_graph.GetOutDegree(nid) != 3)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// check if all items share a name
|
||||||
|
const auto range = node_based_graph.GetAdjacentEdgeRange(nid);
|
||||||
|
const auto required_name_id = node_based_graph.GetEdgeData(range.front()).name_id;
|
||||||
|
|
||||||
|
const auto has_required_name = [this, required_name_id](const auto edge_id) {
|
||||||
|
const auto road_name = node_based_graph.GetEdgeData(edge_id).name_id;
|
||||||
|
if (required_name_id == EMPTY_NAMEID || road_name == EMPTY_NAMEID)
|
||||||
|
return false;
|
||||||
|
return !util::guidance::requiresNameAnnounced(
|
||||||
|
required_name_id, road_name, name_table, street_name_suffix_table) ||
|
||||||
|
!util::guidance::requiresNameAnnounced(
|
||||||
|
road_name, required_name_id, name_table, street_name_suffix_table);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* the beautiful way would be:
|
||||||
|
* return range.end() == std::find_if_not(range.begin(), range.end(), has_required_name);
|
||||||
|
* but that does not work due to range concepts
|
||||||
|
*/
|
||||||
|
for (const auto eid : range)
|
||||||
|
if (!has_required_name(eid))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto degree_three_connect_in = all_same_name_and_degree_three(intersection_node);
|
||||||
|
const auto degree_three_connect_out = all_same_name_and_degree_three(left_candidate);
|
||||||
|
|
||||||
|
if (!degree_three_connect_in && !degree_three_connect_out)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const auto distance_between_candidates = util::coordinate_calculation::haversineDistance(
|
||||||
|
node_coordinates[intersection_node], node_coordinates[left_candidate]);
|
||||||
|
|
||||||
|
const auto both_split_join = degree_three_connect_in && degree_three_connect_out;
|
||||||
|
|
||||||
|
// allow longer separations if both are joining directly
|
||||||
|
// widths are chosen via tuning on traffic islands
|
||||||
|
return both_split_join ? (distance_between_candidates < 30)
|
||||||
|
: (distance_between_candidates < 15);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MergableRoadDetector::IsLinkRoad(const NodeID intersection_node,
|
||||||
|
const MergableRoadData &road) const
|
||||||
|
{
|
||||||
|
const auto next_intersection_parameters =
|
||||||
|
intersection_generator.SkipDegreeTwoNodes(intersection_node, road.eid);
|
||||||
|
const auto next_intersection_along_road = intersection_generator.GetConnectedRoads(
|
||||||
|
next_intersection_parameters.nid, next_intersection_parameters.via_eid);
|
||||||
|
const auto extract_name_id = [this](const MergableRoadData &road) {
|
||||||
|
return node_based_graph.GetEdgeData(road.eid).name_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto requested_name_id = extract_name_id(road);
|
||||||
|
const auto next_road_along_path = next_intersection_along_road.findClosestTurn(
|
||||||
|
STRAIGHT_ANGLE,
|
||||||
|
makeCheckRoadForName(
|
||||||
|
requested_name_id, node_based_graph, name_table, street_name_suffix_table));
|
||||||
|
|
||||||
|
// we need to have a continuing road to successfully detect a link road
|
||||||
|
if (next_road_along_path == next_intersection_along_road.end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const auto opposite_of_next_road_along_path = next_intersection_along_road.findClosestTurn(
|
||||||
|
util::restrictAngleToValidRange(next_road_along_path->angle + STRAIGHT_ANGLE));
|
||||||
|
|
||||||
|
// we cannot be looking at the same road we came from
|
||||||
|
if (node_based_graph.GetTarget(opposite_of_next_road_along_path->eid) ==
|
||||||
|
next_intersection_parameters.nid)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* check if the opposite of the next road decision was sane. It could have been just as well our
|
||||||
|
* incoming road.
|
||||||
|
*/
|
||||||
|
if (angularDeviation(angularDeviation(next_road_along_path->angle, STRAIGHT_ANGLE),
|
||||||
|
angularDeviation(opposite_of_next_road_along_path->angle, 0)) <
|
||||||
|
FUZZY_ANGLE_DIFFERENCE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// near straight road that continues
|
||||||
|
return angularDeviation(opposite_of_next_road_along_path->angle, next_road_along_path->angle) >=
|
||||||
|
(STRAIGHT_ANGLE - FUZZY_ANGLE_DIFFERENCE) &&
|
||||||
|
EdgeDataSupportsMerge(
|
||||||
|
node_based_graph.GetEdgeData(next_road_along_path->eid),
|
||||||
|
node_based_graph.GetEdgeData(opposite_of_next_road_along_path->eid));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace guidance
|
||||||
|
} // namespace extractor
|
||||||
|
} // namespace osrm
|
@ -1,4 +1,7 @@
|
|||||||
#include "extractor/guidance/node_based_graph_walker.hpp"
|
#include "extractor/guidance/node_based_graph_walker.hpp"
|
||||||
|
#include "util/bearing.hpp"
|
||||||
|
#include "util/coordinate_calculation.hpp"
|
||||||
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
using osrm::util::angularDeviation;
|
using osrm::util::angularDeviation;
|
||||||
@ -18,11 +21,8 @@ NodeBasedGraphWalker::NodeBasedGraphWalker(const util::NodeBasedDynamicGraph &no
|
|||||||
}
|
}
|
||||||
|
|
||||||
LengthLimitedCoordinateAccumulator::LengthLimitedCoordinateAccumulator(
|
LengthLimitedCoordinateAccumulator::LengthLimitedCoordinateAccumulator(
|
||||||
const extractor::guidance::CoordinateExtractor &coordinate_extractor,
|
const extractor::guidance::CoordinateExtractor &coordinate_extractor, const double max_length)
|
||||||
const util::NodeBasedDynamicGraph &node_based_graph,
|
: accumulated_length(0), coordinate_extractor(coordinate_extractor), max_length(max_length)
|
||||||
const double max_length)
|
|
||||||
: coordinate_extractor(coordinate_extractor), node_based_graph(node_based_graph),
|
|
||||||
max_length(max_length), accumulated_length(0)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,8 +37,10 @@ void LengthLimitedCoordinateAccumulator::update(const NodeID from_node,
|
|||||||
auto current_coordinates =
|
auto current_coordinates =
|
||||||
coordinate_extractor.GetForwardCoordinatesAlongRoad(from_node, via_edge);
|
coordinate_extractor.GetForwardCoordinatesAlongRoad(from_node, via_edge);
|
||||||
|
|
||||||
const auto length = util::coordinate_calculation::getLength(
|
const auto length =
|
||||||
current_coordinates, util::coordinate_calculation::haversineDistance);
|
util::coordinate_calculation::getLength(current_coordinates.begin(),
|
||||||
|
current_coordinates.end(),
|
||||||
|
util::coordinate_calculation::haversineDistance);
|
||||||
|
|
||||||
// in case we get too many coordinates, we limit them to our desired length
|
// in case we get too many coordinates, we limit them to our desired length
|
||||||
if (length + accumulated_length > max_length)
|
if (length + accumulated_length > max_length)
|
||||||
@ -48,6 +50,7 @@ void LengthLimitedCoordinateAccumulator::update(const NodeID from_node,
|
|||||||
coordinates.insert(coordinates.end(), current_coordinates.begin(), current_coordinates.end());
|
coordinates.insert(coordinates.end(), current_coordinates.begin(), current_coordinates.end());
|
||||||
|
|
||||||
accumulated_length += length;
|
accumulated_length += length;
|
||||||
|
accumulated_length = std::min(accumulated_length, max_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------
|
||||||
@ -60,17 +63,15 @@ SelectRoadByNameOnlyChoiceAndStraightness::SelectRoadByNameOnlyChoiceAndStraight
|
|||||||
boost::optional<EdgeID> SelectRoadByNameOnlyChoiceAndStraightness::
|
boost::optional<EdgeID> SelectRoadByNameOnlyChoiceAndStraightness::
|
||||||
operator()(const NodeID /*nid*/,
|
operator()(const NodeID /*nid*/,
|
||||||
const EdgeID /*via_edge_id*/,
|
const EdgeID /*via_edge_id*/,
|
||||||
const Intersection &intersection,
|
const IntersectionView &intersection,
|
||||||
const util::NodeBasedDynamicGraph &node_based_graph) const
|
const util::NodeBasedDynamicGraph &node_based_graph) const
|
||||||
{
|
{
|
||||||
BOOST_ASSERT(!intersection.empty());
|
BOOST_ASSERT(!intersection.empty());
|
||||||
const auto comparator = [this, &node_based_graph](const ConnectedRoad &lhs,
|
const auto comparator = [this, &node_based_graph](const IntersectionViewData &lhs,
|
||||||
const ConnectedRoad &rhs) {
|
const IntersectionViewData &rhs) {
|
||||||
// the score of an elemnt results in an ranking preferring valid entries, if required over
|
// the score of an elemnt results in an ranking preferring valid entries, if required over
|
||||||
// invalid
|
// invalid requested name_ids over non-requested narrow deviations over non-narrow
|
||||||
// requested name_ids over non-requested
|
const auto score = [this, &node_based_graph](const IntersectionViewData &road) {
|
||||||
// narrow deviations over non-narrow
|
|
||||||
const auto score = [this, &node_based_graph](const ConnectedRoad &road) {
|
|
||||||
double result_score = 0;
|
double result_score = 0;
|
||||||
// since angular deviation is limited by 0-180, we add 360 for invalid
|
// since angular deviation is limited by 0-180, we add 360 for invalid
|
||||||
if (requires_entry && !road.entry_allowed)
|
if (requires_entry && !road.entry_allowed)
|
||||||
@ -95,6 +96,75 @@ operator()(const NodeID /*nid*/,
|
|||||||
return (*min_element).eid;
|
return (*min_element).eid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------------
|
||||||
|
SelectStraightmostRoadByNameAndOnlyChoice::SelectStraightmostRoadByNameAndOnlyChoice(
|
||||||
|
const NameID desired_name_id, const double initial_bearing, const bool requires_entry)
|
||||||
|
: desired_name_id(desired_name_id), initial_bearing(initial_bearing),
|
||||||
|
requires_entry(requires_entry)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::optional<EdgeID> SelectStraightmostRoadByNameAndOnlyChoice::
|
||||||
|
operator()(const NodeID /*nid*/,
|
||||||
|
const EdgeID /*via_edge_id*/,
|
||||||
|
const IntersectionView &intersection,
|
||||||
|
const util::NodeBasedDynamicGraph &node_based_graph) const
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(!intersection.empty());
|
||||||
|
if (intersection.size() == 1)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
const auto comparator = [this, &node_based_graph](const IntersectionViewData &lhs,
|
||||||
|
const IntersectionViewData &rhs) {
|
||||||
|
// the score of an elemnt results in an ranking preferring valid entries, if required over
|
||||||
|
// invalid requested name_ids over non-requested narrow deviations over non-narrow
|
||||||
|
const auto score = [this, &node_based_graph](const IntersectionViewData &road) {
|
||||||
|
double result_score = 0;
|
||||||
|
// since angular deviation is limited by 0-180, we add 360 for invalid
|
||||||
|
if (requires_entry && !road.entry_allowed)
|
||||||
|
result_score += 360.;
|
||||||
|
|
||||||
|
// 180 for undesired name-ids
|
||||||
|
if (desired_name_id != node_based_graph.GetEdgeData(road.eid).name_id)
|
||||||
|
result_score += 180;
|
||||||
|
|
||||||
|
return result_score + angularDeviation(road.angle, STRAIGHT_ANGLE);
|
||||||
|
};
|
||||||
|
|
||||||
|
return score(lhs) < score(rhs);
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto count_desired_name =
|
||||||
|
std::count_if(std::begin(intersection),
|
||||||
|
std::end(intersection),
|
||||||
|
[this, &node_based_graph](const auto &road) {
|
||||||
|
return node_based_graph.GetEdgeData(road.eid).name_id == desired_name_id;
|
||||||
|
});
|
||||||
|
if (count_desired_name > 2)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
const auto min_element =
|
||||||
|
std::min_element(std::next(std::begin(intersection)), std::end(intersection), comparator);
|
||||||
|
|
||||||
|
const auto is_valid_choice = !requires_entry || min_element->entry_allowed;
|
||||||
|
const auto is_only_choice_with_same_name =
|
||||||
|
count_desired_name <= 2 && // <= in case we come from a bridge
|
||||||
|
node_based_graph.GetEdgeData(min_element->eid).name_id == desired_name_id &&
|
||||||
|
angularDeviation(min_element->angle, STRAIGHT_ANGLE) < 100; // don't do crazy turns
|
||||||
|
const auto has_valid_angle =
|
||||||
|
((intersection.size() == 2 ||
|
||||||
|
intersection.findClosestTurn(STRAIGHT_ANGLE) == min_element) &&
|
||||||
|
angularDeviation(min_element->angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE) &&
|
||||||
|
angularDeviation(initial_bearing, min_element->bearing) < NARROW_TURN_ANGLE;
|
||||||
|
|
||||||
|
// in cases where we have two edges between roads, we can have quite severe angles due to the
|
||||||
|
// random split OSRM does to break up parallel edges at any coordinate
|
||||||
|
if (!is_valid_choice || !(is_only_choice_with_same_name || has_valid_angle))
|
||||||
|
return {};
|
||||||
|
else
|
||||||
|
return (*min_element).eid;
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------
|
||||||
IntersectionFinderAccumulator::IntersectionFinderAccumulator(
|
IntersectionFinderAccumulator::IntersectionFinderAccumulator(
|
||||||
const std::uint8_t hop_limit, const IntersectionGenerator &intersection_generator)
|
const std::uint8_t hop_limit, const IntersectionGenerator &intersection_generator)
|
||||||
@ -123,7 +193,7 @@ void IntersectionFinderAccumulator::update(const NodeID from_node,
|
|||||||
nid = from_node;
|
nid = from_node;
|
||||||
via_edge_id = via_edge;
|
via_edge_id = via_edge;
|
||||||
|
|
||||||
intersection = intersection_generator.GetConnectedRoads(from_node, via_edge);
|
intersection = intersection_generator.GetConnectedRoads(from_node, via_edge, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace guidance
|
} // namespace guidance
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
#include "util/guidance/name_announcements.hpp"
|
#include "util/guidance/name_announcements.hpp"
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
@ -80,13 +80,13 @@ Intersection TurnAnalysis::operator()(const NodeID node_prior_to_intersection,
|
|||||||
TurnAnalysis::ShapeResult shape_result =
|
TurnAnalysis::ShapeResult shape_result =
|
||||||
ComputeIntersectionShapes(node_based_graph.GetTarget(entering_via_edge));
|
ComputeIntersectionShapes(node_based_graph.GetTarget(entering_via_edge));
|
||||||
|
|
||||||
// assign valid flags to normalised_shape
|
// assign valid flags to normalized_shape
|
||||||
const auto intersection_view = intersection_generator.TransformIntersectionShapeIntoView(
|
const auto intersection_view = intersection_generator.TransformIntersectionShapeIntoView(
|
||||||
node_prior_to_intersection,
|
node_prior_to_intersection,
|
||||||
entering_via_edge,
|
entering_via_edge,
|
||||||
shape_result.normalised_intersection_shape,
|
shape_result.annotated_normalized_shape.normalized_shape,
|
||||||
shape_result.intersection_shape,
|
shape_result.intersection_shape,
|
||||||
shape_result.merging_map);
|
shape_result.annotated_normalized_shape.performed_merges);
|
||||||
|
|
||||||
// assign the turn types to the intersection
|
// assign the turn types to the intersection
|
||||||
return AssignTurnTypes(node_prior_to_intersection, entering_via_edge, intersection_view);
|
return AssignTurnTypes(node_prior_to_intersection, entering_via_edge, intersection_view);
|
||||||
@ -171,9 +171,8 @@ TurnAnalysis::ComputeIntersectionShapes(const NodeID node_at_center_of_intersect
|
|||||||
intersection_shape.intersection_shape =
|
intersection_shape.intersection_shape =
|
||||||
intersection_generator.ComputeIntersectionShape(node_at_center_of_intersection);
|
intersection_generator.ComputeIntersectionShape(node_at_center_of_intersection);
|
||||||
|
|
||||||
std::tie(intersection_shape.normalised_intersection_shape, intersection_shape.merging_map) =
|
intersection_shape.annotated_normalized_shape = intersection_normalizer(
|
||||||
intersection_normalizer(node_at_center_of_intersection,
|
node_at_center_of_intersection, intersection_shape.intersection_shape);
|
||||||
intersection_shape.intersection_shape);
|
|
||||||
|
|
||||||
return intersection_shape;
|
return intersection_shape;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include "extractor/guidance/turn_discovery.hpp"
|
#include "extractor/guidance/turn_discovery.hpp"
|
||||||
#include "extractor/guidance/constants.hpp"
|
#include "extractor/guidance/constants.hpp"
|
||||||
|
#include "util/bearing.hpp"
|
||||||
#include "util/coordinate_calculation.hpp"
|
#include "util/coordinate_calculation.hpp"
|
||||||
|
|
||||||
using osrm::util::angularDeviation;
|
using osrm::util::angularDeviation;
|
||||||
@ -43,8 +44,11 @@ bool findPreviousIntersection(const NodeID node_v,
|
|||||||
const constexpr double COMBINE_DISTANCE_CUTOFF = 30;
|
const constexpr double COMBINE_DISTANCE_CUTOFF = 30;
|
||||||
|
|
||||||
const auto coordinate_extractor = intersection_generator.GetCoordinateExtractor();
|
const auto coordinate_extractor = intersection_generator.GetCoordinateExtractor();
|
||||||
const auto via_edge_length = util::coordinate_calculation::getLength(
|
const auto coordinates_along_via_edge =
|
||||||
coordinate_extractor.GetForwardCoordinatesAlongRoad(node_v, via_edge),
|
coordinate_extractor.GetForwardCoordinatesAlongRoad(node_v, via_edge);
|
||||||
|
const auto via_edge_length =
|
||||||
|
util::coordinate_calculation::getLength(coordinates_along_via_edge.begin(),
|
||||||
|
coordinates_along_via_edge.end(),
|
||||||
&util::coordinate_calculation::haversineDistance);
|
&util::coordinate_calculation::haversineDistance);
|
||||||
|
|
||||||
// we check if via-edge is too short. In this case the previous turn cannot influence the turn
|
// we check if via-edge is too short. In this case the previous turn cannot influence the turn
|
||||||
|
@ -10,7 +10,6 @@
|
|||||||
|
|
||||||
#include <boost/assert.hpp>
|
#include <boost/assert.hpp>
|
||||||
|
|
||||||
using EdgeData = osrm::util::NodeBasedDynamicGraph::EdgeData;
|
|
||||||
using osrm::extractor::guidance::getTurnDirection;
|
using osrm::extractor::guidance::getTurnDirection;
|
||||||
using osrm::util::angularDeviation;
|
using osrm::util::angularDeviation;
|
||||||
|
|
||||||
|
@ -5,8 +5,8 @@
|
|||||||
|
|
||||||
#include <boost/assert.hpp>
|
#include <boost/assert.hpp>
|
||||||
|
|
||||||
#include <cmath>
|
#include <algorithm>
|
||||||
|
#include <iterator>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
@ -125,29 +125,17 @@ Coordinate centroid(const Coordinate lhs, const Coordinate rhs)
|
|||||||
return centroid;
|
return centroid;
|
||||||
}
|
}
|
||||||
|
|
||||||
double degToRad(const double degree)
|
|
||||||
{
|
|
||||||
using namespace boost::math::constants;
|
|
||||||
return degree * (pi<double>() / 180.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
double radToDeg(const double radian)
|
|
||||||
{
|
|
||||||
using namespace boost::math::constants;
|
|
||||||
return radian * (180.0 * (1. / pi<double>()));
|
|
||||||
}
|
|
||||||
|
|
||||||
double bearing(const Coordinate first_coordinate, const Coordinate second_coordinate)
|
double bearing(const Coordinate first_coordinate, const Coordinate second_coordinate)
|
||||||
{
|
{
|
||||||
const double lon_diff =
|
const double lon_diff =
|
||||||
static_cast<double>(toFloating(second_coordinate.lon - first_coordinate.lon));
|
static_cast<double>(toFloating(second_coordinate.lon - first_coordinate.lon));
|
||||||
const double lon_delta = degToRad(lon_diff);
|
const double lon_delta = detail::degToRad(lon_diff);
|
||||||
const double lat1 = degToRad(static_cast<double>(toFloating(first_coordinate.lat)));
|
const double lat1 = detail::degToRad(static_cast<double>(toFloating(first_coordinate.lat)));
|
||||||
const double lat2 = degToRad(static_cast<double>(toFloating(second_coordinate.lat)));
|
const double lat2 = detail::degToRad(static_cast<double>(toFloating(second_coordinate.lat)));
|
||||||
const double y = std::sin(lon_delta) * std::cos(lat2);
|
const double y = std::sin(lon_delta) * std::cos(lat2);
|
||||||
const double x =
|
const double x =
|
||||||
std::cos(lat1) * std::sin(lat2) - std::sin(lat1) * std::cos(lat2) * std::cos(lon_delta);
|
std::cos(lat1) * std::sin(lat2) - std::sin(lat1) * std::cos(lat2) * std::cos(lon_delta);
|
||||||
double result = radToDeg(std::atan2(y, x));
|
double result = detail::radToDeg(std::atan2(y, x));
|
||||||
while (result < 0.0)
|
while (result < 0.0)
|
||||||
{
|
{
|
||||||
result += 360.0;
|
result += 360.0;
|
||||||
@ -320,47 +308,72 @@ bool isCCW(const Coordinate first_coordinate,
|
|||||||
return signedArea(first_coordinate, second_coordinate, third_coordinate) > 0;
|
return signedArea(first_coordinate, second_coordinate, third_coordinate) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<util::Coordinate, util::Coordinate>
|
// find the closest distance between a coordinate and a segment
|
||||||
leastSquareRegression(const std::vector<util::Coordinate> &coordinates)
|
double findClosestDistance(const Coordinate coordinate,
|
||||||
|
const Coordinate segment_begin,
|
||||||
|
const Coordinate segment_end)
|
||||||
{
|
{
|
||||||
BOOST_ASSERT(coordinates.size() >= 2);
|
return haversineDistance(coordinate,
|
||||||
double sum_lon = 0, sum_lat = 0, sum_lon_lat = 0, sum_lon_lon = 0;
|
projectPointOnSegment(segment_begin, segment_end, coordinate).second);
|
||||||
double min_lon = static_cast<double>(toFloating(coordinates.front().lon));
|
}
|
||||||
double max_lon = static_cast<double>(toFloating(coordinates.front().lon));
|
|
||||||
for (const auto coord : coordinates)
|
|
||||||
{
|
|
||||||
min_lon = std::min(min_lon, static_cast<double>(toFloating(coord.lon)));
|
|
||||||
max_lon = std::max(max_lon, static_cast<double>(toFloating(coord.lon)));
|
|
||||||
sum_lon += static_cast<double>(toFloating(coord.lon));
|
|
||||||
sum_lon_lon +=
|
|
||||||
static_cast<double>(toFloating(coord.lon)) * static_cast<double>(toFloating(coord.lon));
|
|
||||||
sum_lat += static_cast<double>(toFloating(coord.lat));
|
|
||||||
sum_lon_lat +=
|
|
||||||
static_cast<double>(toFloating(coord.lon)) * static_cast<double>(toFloating(coord.lat));
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto dividend = coordinates.size() * sum_lon_lat - sum_lon * sum_lat;
|
// find the closes distance between two sets of coordinates
|
||||||
const auto divisor = coordinates.size() * sum_lon_lon - sum_lon * sum_lon;
|
double findClosestDistance(const std::vector<Coordinate> &lhs, const std::vector<Coordinate> &rhs)
|
||||||
if (std::abs(divisor) < std::numeric_limits<double>::epsilon())
|
{
|
||||||
return std::make_pair(coordinates.front(), coordinates.back());
|
double current_min = std::numeric_limits<double>::max();
|
||||||
|
|
||||||
// slope of the regression line
|
const auto compute_minimum_distance_in_rhs = [¤t_min, &rhs](const Coordinate coordinate) {
|
||||||
const auto slope = dividend / divisor;
|
current_min =
|
||||||
const auto intercept = (sum_lat - slope * sum_lon) / coordinates.size();
|
std::min(current_min, findClosestDistance(coordinate, rhs.begin(), rhs.end()));
|
||||||
|
return false;
|
||||||
const auto GetLatAtLon = [intercept,
|
|
||||||
slope](const util::FloatLongitude longitude) -> util::FloatLatitude {
|
|
||||||
return {intercept + slope * static_cast<double>((longitude))};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const util::Coordinate regression_first = {
|
std::find_if(std::begin(lhs), std::end(lhs), compute_minimum_distance_in_rhs);
|
||||||
toFixed(util::FloatLongitude{min_lon - 1}),
|
return current_min;
|
||||||
toFixed(util::FloatLatitude(GetLatAtLon(util::FloatLongitude{min_lon - 1})))};
|
}
|
||||||
const util::Coordinate regression_end = {
|
|
||||||
toFixed(util::FloatLongitude{max_lon + 1}),
|
|
||||||
toFixed(util::FloatLatitude(GetLatAtLon(util::FloatLongitude{max_lon + 1})))};
|
|
||||||
|
|
||||||
return {regression_first, regression_end};
|
std::vector<double> getDeviations(const std::vector<Coordinate> &from,
|
||||||
|
const std::vector<Coordinate> &to)
|
||||||
|
{
|
||||||
|
auto find_deviation = [&to](const Coordinate coordinate) {
|
||||||
|
return findClosestDistance(coordinate, to.begin(), to.end());
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<double> deviations_from;
|
||||||
|
deviations_from.reserve(from.size());
|
||||||
|
std::transform(
|
||||||
|
std::begin(from), std::end(from), std::back_inserter(deviations_from), find_deviation);
|
||||||
|
|
||||||
|
return deviations_from;
|
||||||
|
}
|
||||||
|
|
||||||
|
Coordinate rotateCCWAroundZero(Coordinate coordinate, double angle_in_radians)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* a rotation around 0,0 in vector space is defined as
|
||||||
|
*
|
||||||
|
* | cos a -sin a | . | lon |
|
||||||
|
* | sin a cos a | | lat |
|
||||||
|
*
|
||||||
|
* resulting in cos a lon - sin a lon for the new longitude and sin a lon + cos a lat for the
|
||||||
|
* new latitude
|
||||||
|
*/
|
||||||
|
|
||||||
|
const auto cos_alpha = cos(angle_in_radians);
|
||||||
|
const auto sin_alpha = sin(angle_in_radians);
|
||||||
|
|
||||||
|
const auto lon = static_cast<double>(toFloating(coordinate.lon));
|
||||||
|
const auto lat = static_cast<double>(toFloating(coordinate.lat));
|
||||||
|
|
||||||
|
return {util::FloatLongitude{cos_alpha * lon - sin_alpha * lat},
|
||||||
|
util::FloatLatitude{sin_alpha * lon + cos_alpha * lat}};
|
||||||
|
}
|
||||||
|
|
||||||
|
Coordinate difference(const Coordinate lhs, const Coordinate rhs)
|
||||||
|
{
|
||||||
|
const auto lon_diff_int = static_cast<int>(lhs.lon) - static_cast<int>(rhs.lon);
|
||||||
|
const auto lat_diff_int = static_cast<int>(lhs.lat) - static_cast<int>(rhs.lat);
|
||||||
|
return {util::FixedLongitude{lon_diff_int}, util::FixedLatitude{lat_diff_int}};
|
||||||
}
|
}
|
||||||
|
|
||||||
} // ns coordinate_calculation
|
} // ns coordinate_calculation
|
||||||
|
@ -36,6 +36,7 @@ NodeIdVectorToMultiPoint::NodeIdVectorToMultiPoint(
|
|||||||
: node_coordinates(node_coordinates)
|
: node_coordinates(node_coordinates)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
util::json::Object NodeIdVectorToMultiPoint::
|
util::json::Object NodeIdVectorToMultiPoint::
|
||||||
operator()(const std::vector<NodeID> &node_ids,
|
operator()(const std::vector<NodeID> &node_ids,
|
||||||
const boost::optional<json::Object> &properties) const
|
const boost::optional<json::Object> &properties) const
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include <boost/numeric/conversion/cast.hpp>
|
#include <boost/numeric/conversion/cast.hpp>
|
||||||
#include <boost/test/unit_test.hpp>
|
#include <boost/test/unit_test.hpp>
|
||||||
|
|
||||||
|
#include "util/bearing.hpp"
|
||||||
#include "util/coordinate_calculation.hpp"
|
#include "util/coordinate_calculation.hpp"
|
||||||
|
|
||||||
#include <osrm/coordinate.hpp>
|
#include <osrm/coordinate.hpp>
|
||||||
@ -325,4 +326,56 @@ BOOST_AUTO_TEST_CASE(squaredEuclideanDistance)
|
|||||||
BOOST_CHECK_EQUAL(result, 162000000000000000ull);
|
BOOST_CHECK_EQUAL(result, 162000000000000000ull);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(vertical_regression)
|
||||||
|
{
|
||||||
|
// check a vertical line for its bearing
|
||||||
|
std::vector<Coordinate> coordinates;
|
||||||
|
for (std::size_t i = 0; i < 100; ++i)
|
||||||
|
coordinates.push_back(Coordinate(FloatLongitude{0.0}, FloatLatitude{i / 100.0}));
|
||||||
|
|
||||||
|
const auto regression =
|
||||||
|
util::coordinate_calculation::leastSquareRegression(coordinates.begin(), coordinates.end());
|
||||||
|
const auto is_valid =
|
||||||
|
util::angularDeviation(
|
||||||
|
util::coordinate_calculation::bearing(regression.first, regression.second), 0) < 2;
|
||||||
|
BOOST_CHECK(is_valid);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(sinus_curve)
|
||||||
|
{
|
||||||
|
// create a full sinus curve, sampled in 3.6 degree
|
||||||
|
std::vector<Coordinate> coordinates;
|
||||||
|
for (std::size_t i = 0; i < 360; ++i)
|
||||||
|
coordinates.push_back(Coordinate(
|
||||||
|
FloatLongitude{i / 360.0},
|
||||||
|
FloatLatitude{sin(util::coordinate_calculation::detail::degToRad(i / 360.0))}));
|
||||||
|
|
||||||
|
const auto regression =
|
||||||
|
util::coordinate_calculation::leastSquareRegression(coordinates.begin(), coordinates.end());
|
||||||
|
const auto is_valid =
|
||||||
|
util::angularDeviation(
|
||||||
|
util::coordinate_calculation::bearing(regression.first, regression.second), 90) < 2;
|
||||||
|
|
||||||
|
BOOST_CHECK(is_valid);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(parallel_lines_slight_offset)
|
||||||
|
{
|
||||||
|
std::vector<Coordinate> coordinates_lhs;
|
||||||
|
for (std::size_t i = 0; i < 100; ++i)
|
||||||
|
coordinates_lhs.push_back(Coordinate(util::FloatLongitude{(50 - (rand() % 101)) / 100000.0},
|
||||||
|
util::FloatLatitude{i / 100000.0}));
|
||||||
|
std::vector<Coordinate> coordinates_rhs;
|
||||||
|
for (std::size_t i = 0; i < 100; ++i)
|
||||||
|
coordinates_rhs.push_back(
|
||||||
|
Coordinate(util::FloatLongitude{(150 - (rand() % 101)) / 100000.0},
|
||||||
|
util::FloatLatitude{i / 100000.0}));
|
||||||
|
|
||||||
|
const auto are_parallel = util::coordinate_calculation::areParallel(coordinates_lhs.begin(),
|
||||||
|
coordinates_lhs.end(),
|
||||||
|
coordinates_rhs.begin(),
|
||||||
|
coordinates_rhs.end());
|
||||||
|
BOOST_CHECK(are_parallel);
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
Loading…
Reference in New Issue
Block a user