Refactors and improves the Sliproad Handler, resolves #3109
This commit is contained in:
parent
df3c39cef5
commit
875f482203
@ -19,6 +19,7 @@
|
||||
- Improved turn angle calculation, detecting offsets due to lanes / minor variations due to inaccuracies
|
||||
- Corrected the bearings returned for intermediate steps - requires reprocessing
|
||||
- Improved turn locations for collapsed turns
|
||||
- Sliproad classification refinements: the situations we detect as Sliproads now resemble more closely the reality
|
||||
- Trip Plugin
|
||||
- changed internal behaviour to prefer the smallest lexicographic result over the largest one
|
||||
- Bugfixes
|
||||
|
@ -78,24 +78,24 @@ Feature: Car - Street names in instructions
|
||||
Scenario: Inner city expressway with on road
|
||||
Given the node map
|
||||
"""
|
||||
a b c g
|
||||
f
|
||||
|
||||
|
||||
|
||||
a b . . . c g
|
||||
`f .
|
||||
`
|
||||
.
|
||||
.
|
||||
d
|
||||
|
||||
|
||||
|
||||
.
|
||||
.
|
||||
.
|
||||
e
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | highway | name | name:pronunciation |
|
||||
| abc | primary | road | roooaad |
|
||||
| cg | primary | road | roooaad |
|
||||
| bfd | trunk_link | | |
|
||||
| cde | trunk | trunk | truank |
|
||||
| nodes | highway | name | name:pronunciation | oneway |
|
||||
| abc | primary | road | roooaad | |
|
||||
| cg | primary | road | roooaad | |
|
||||
| bfd | trunk_link | sliproad | | yes |
|
||||
| cde | trunk | trunk | truank | yes |
|
||||
|
||||
And the relations
|
||||
| type | way:from | way:to | node:via | restriction |
|
||||
|
@ -5,13 +5,17 @@ Feature: Turn Lane Guidance
|
||||
Given the profile "car"
|
||||
Given a grid size of 3 meters
|
||||
|
||||
|
||||
@sliproads
|
||||
Scenario: Separate Turn Lanes
|
||||
Given the node map
|
||||
"""
|
||||
e
|
||||
a b c g
|
||||
d
|
||||
.
|
||||
a ... b ..... c . g
|
||||
` .
|
||||
`... d
|
||||
.
|
||||
f
|
||||
"""
|
||||
|
||||
@ -41,8 +45,10 @@ Feature: Turn Lane Guidance
|
||||
Given the node map
|
||||
"""
|
||||
e
|
||||
a b c g
|
||||
d
|
||||
a . . b . . . c g
|
||||
` .
|
||||
` .
|
||||
` d
|
||||
f
|
||||
"""
|
||||
|
||||
@ -67,21 +73,22 @@ Feature: Turn Lane Guidance
|
||||
| a,g | in,straight,straight | depart,new name straight,arrive | ,left:false straight:true right:false, |
|
||||
| a,f | in,cross,cross | depart,turn right,arrive | ,left:false straight:false right:true, |
|
||||
|
||||
|
||||
@sliproads
|
||||
Scenario: Separate Turn Lanes Next to other turns
|
||||
Given the node map
|
||||
"""
|
||||
e
|
||||
a - - b.-.- - c-g
|
||||
| ' 'd
|
||||
| f
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
i - - h - - - j
|
||||
. e
|
||||
a . . b . . . c g
|
||||
. ` .
|
||||
. ` .
|
||||
. d
|
||||
. f
|
||||
.
|
||||
.
|
||||
.
|
||||
.
|
||||
i . . h . . . j
|
||||
"""
|
||||
|
||||
And the ways
|
||||
@ -109,6 +116,7 @@ Feature: Turn Lane Guidance
|
||||
| a,j | in,turn,other,other | depart,turn right,turn left,arrive | ,,left:true right:false, |
|
||||
| a,i | in,turn,other,other | depart,turn right,turn right,arrive | ,,left:false right:true, |
|
||||
|
||||
|
||||
@todo @2654 @none
|
||||
#https://github.com/Project-OSRM/osrm-backend/issues/2645
|
||||
#http://www.openstreetmap.org/export#map=19/52.56054/13.32152
|
||||
@ -231,6 +239,7 @@ Feature: Turn Lane Guidance
|
||||
| a,j | ghough,market,market | depart,turn left,arrive | ,none:true straight:false straight:false straight:false, |
|
||||
| a,f | ghough,ghough,ghough | depart,continue slight left,arrive | ,none:true straight:true straight:false straight:false, |
|
||||
|
||||
|
||||
Scenario: Check sliproad handler loop's exit condition, Issue #2896
|
||||
# http://www.openstreetmap.org/way/198481519
|
||||
Given the node locations
|
||||
|
@ -55,3 +55,38 @@ Feature: Collapse
|
||||
| waypoints | route | turns |
|
||||
| a,g | road,road,road | depart,continue uturn,arrive |
|
||||
| d,c | road,road,road | depart,continue uturn,arrive |
|
||||
|
||||
Scenario: Forking before a turn (forky)
|
||||
Given the node map
|
||||
"""
|
||||
g
|
||||
.
|
||||
c
|
||||
a . . b .'
|
||||
`d.
|
||||
f e
|
||||
"""
|
||||
# note: check clooapse.feature for a similar test case where we do not
|
||||
# classify the situation as Sliproad and therefore keep the fork inst.
|
||||
|
||||
And the ways
|
||||
| nodes | name | oneway | highway |
|
||||
| ab | road | yes | primary |
|
||||
| bd | road | yes | primary |
|
||||
| bc | road | yes | primary |
|
||||
| de | road | yes | primary |
|
||||
| fd | cross | no | secondary |
|
||||
| dc | cross | no | secondary |
|
||||
| cg | cross | no | secondary |
|
||||
|
||||
And the relations
|
||||
| type | way:from | way:to | node:via | restriction |
|
||||
| restriction | bd | dc | d | no_left_turn |
|
||||
| restriction | bc | dc | c | no_right_turn |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| a,g | road,cross,cross | depart,turn left,arrive |
|
||||
| a,e | road,road,road | depart,continue right,arrive |
|
||||
# We should discuss whether the next item should be collapsed to depart,turn right,arrive.
|
||||
| a,f | road,road,cross,cross | depart,continue slight right,turn right,arrive |
|
||||
|
@ -701,12 +701,15 @@ Feature: Collapse
|
||||
Given the node map
|
||||
"""
|
||||
g
|
||||
|
||||
.
|
||||
c
|
||||
a b
|
||||
d
|
||||
a . . b .'
|
||||
` d.
|
||||
f e
|
||||
"""
|
||||
# as it is right now we don't classify this as a sliproad,
|
||||
# check collapse-detail.feature for a similar test case
|
||||
# which removes the fork here due to it being a Sliproad.
|
||||
|
||||
And the ways
|
||||
| nodes | name | oneway | highway |
|
||||
@ -725,10 +728,9 @@ Feature: Collapse
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| a,g | road,cross,cross | depart,turn left,arrive |
|
||||
| a,e | road,road,road | depart,continue straight,arrive |
|
||||
# We should discuss whether the next item should be collapsed to depart,turn right,arrive.
|
||||
| a,f | road,road,cross,cross | depart,continue straight,turn right,arrive |
|
||||
| a,g | road,cross,cross | depart,fork left,arrive |
|
||||
| a,e | road,road,road | depart,fork slight right,arrive |
|
||||
| a,f | road,road,cross,cross | depart,fork slight right,turn right,arrive |
|
||||
|
||||
Scenario: On-Off on Highway
|
||||
Given the node map
|
||||
|
@ -9,23 +9,23 @@ Feature: Slipways and Dedicated Turn Lanes
|
||||
Given the node map
|
||||
"""
|
||||
e
|
||||
a b c d
|
||||
h
|
||||
|
||||
1
|
||||
|
||||
a b . . c d
|
||||
`h .
|
||||
`
|
||||
1 `
|
||||
.
|
||||
f
|
||||
|
||||
.
|
||||
g
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | highway | name |
|
||||
| abc | trunk | first |
|
||||
| cd | trunk | first |
|
||||
| bhf | trunk_link | |
|
||||
| cfg | primary | second |
|
||||
| ec | primary | second |
|
||||
| nodes | highway | name | oneway |
|
||||
| abc | trunk | first | |
|
||||
| cd | trunk | first | |
|
||||
| bhf | trunk_link | | yes |
|
||||
| cfg | primary | second | yes |
|
||||
| ec | primary | second | |
|
||||
|
||||
And the relations
|
||||
| type | way:from | way:to | node:via | restriction |
|
||||
@ -51,12 +51,12 @@ Feature: Slipways and Dedicated Turn Lanes
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | highway | name | maxspeed |
|
||||
| abc | trunk | first | 70 |
|
||||
| cd | trunk | first | 2 |
|
||||
| bhf | trunk_link | | 2 |
|
||||
| cfg | primary | second | 50 |
|
||||
| ec | primary | second | 50 |
|
||||
| nodes | highway | name | maxspeed | oneway |
|
||||
| abc | trunk | first | 70 | |
|
||||
| cd | trunk | first | 2 | |
|
||||
| bhf | trunk_link | | 2 | yes |
|
||||
| cfg | primary | second | 50 | yes |
|
||||
| ec | primary | second | 50 | |
|
||||
|
||||
And the relations
|
||||
| type | way:from | way:to | node:via | restriction |
|
||||
@ -125,24 +125,24 @@ Feature: Slipways and Dedicated Turn Lanes
|
||||
Scenario: Inner city expressway with on road
|
||||
Given the node map
|
||||
"""
|
||||
a b c g
|
||||
f
|
||||
|
||||
|
||||
|
||||
a b . . . c g
|
||||
`f .
|
||||
`
|
||||
.
|
||||
.
|
||||
d
|
||||
|
||||
|
||||
|
||||
.
|
||||
.
|
||||
.
|
||||
e
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | highway | name |
|
||||
| abc | primary | road |
|
||||
| cg | primary | road |
|
||||
| bfd | trunk_link | |
|
||||
| cde | trunk | trunk |
|
||||
| nodes | highway | name | oneway |
|
||||
| abc | primary | road | |
|
||||
| cg | primary | road | |
|
||||
| bfd | trunk_link | | yes |
|
||||
| cde | trunk | trunk | yes |
|
||||
|
||||
And the relations
|
||||
| type | way:from | way:to | node:via | restriction |
|
||||
@ -240,8 +240,8 @@ Feature: Slipways and Dedicated Turn Lanes
|
||||
| qe | secondary_link | Ettlinger Allee | | yes |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns | ref |
|
||||
| a,o | Schwarzwaldstrasse,Ettlinger Allee,Ettlinger Allee | depart,turn right,arrive | L561,L561, |
|
||||
| waypoints | route | turns | ref |
|
||||
| a,o | Schwarzwaldstrasse,Ettlinger Allee,Ettlinger Allee | depart,turn right,arrive | L561,L561, |
|
||||
|
||||
Scenario: Traffic Lights everywhere
|
||||
#http://map.project-osrm.org/?z=18¢er=48.995336%2C8.383813&loc=48.995467%2C8.384548&loc=48.995115%2C8.382761&hl=en&alt=0
|
||||
@ -431,3 +431,354 @@ Feature: Slipways and Dedicated Turn Lanes
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| a,i | road,road,road | depart,fork slight left,arrive |
|
||||
|
||||
|
||||
# The following tests are current false positives / false negatives #3199
|
||||
|
||||
@sliproads
|
||||
# http://www.openstreetmap.org/#map=19/52.59847/13.14815
|
||||
Scenario: Sliproad Detection
|
||||
Given the node map
|
||||
"""
|
||||
a . . .
|
||||
. .
|
||||
b . . . . . . c . . . d
|
||||
` . .
|
||||
e . .
|
||||
` . .
|
||||
f . .
|
||||
` . .
|
||||
g i
|
||||
` h .
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | highway | name |
|
||||
| abefgh | residential | Nachtigallensteig |
|
||||
| bcd | residential | Kiebitzsteig |
|
||||
| cg | residential | Haenflingsteig |
|
||||
| hid | residential | Waldkauzsteig |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| a,d | Nachtigallensteig,Kiebitzsteig,Kiebitzsteig | depart,turn left,arrive |
|
||||
| a,h | Nachtigallensteig,Nachtigallensteig | depart,arrive |
|
||||
|
||||
|
||||
@sliproads
|
||||
Scenario: Not a obvious Sliproad
|
||||
Given the node map
|
||||
"""
|
||||
d
|
||||
.
|
||||
s . a . . b . . c
|
||||
` .
|
||||
` e
|
||||
.`
|
||||
. `
|
||||
f g
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | highway | name | oneway |
|
||||
| sabc | primary | sabc | |
|
||||
| dbef | primary | dbef | yes |
|
||||
| aeg | primary | aeg | yes |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| s,f | sabc,aeg,dbef,dbef | depart,turn right,turn right,arrive |
|
||||
|
||||
@sliproads
|
||||
Scenario: Through Street, not a Sliproad although obvious
|
||||
Given the node map
|
||||
"""
|
||||
d
|
||||
.
|
||||
s . a . . b . . c
|
||||
` .
|
||||
` e
|
||||
. `
|
||||
. `
|
||||
f g
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | highway | name | oneway |
|
||||
| sabc | primary | sabc | |
|
||||
| dbef | primary | dbef | yes |
|
||||
| aeg | primary | aeg | yes |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| s,f | sabc,aeg,dbef,dbef | depart,turn right,turn right,arrive |
|
||||
|
||||
@sliproads
|
||||
Scenario: Sliproad target turn is restricted
|
||||
Given the node map
|
||||
"""
|
||||
d
|
||||
.
|
||||
s . a . . . . b . . c
|
||||
` .
|
||||
` .
|
||||
` .
|
||||
` .
|
||||
`.
|
||||
e
|
||||
.`
|
||||
f `
|
||||
. ` g
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | highway | name | oneway |
|
||||
| sa | primary | sabc | |
|
||||
| abc | primary | sabc | |
|
||||
| dbe | primary | dbef | yes |
|
||||
| ef | primary | dbef | |
|
||||
| ae | primary | aeg | yes |
|
||||
| eg | primary | aeg | |
|
||||
# the reason we have to split ways at e is that otherwise we can't handle restrictions via e
|
||||
|
||||
And the relations
|
||||
| type | way:from | way:to | node:via | restriction |
|
||||
| restriction | ae | ef | e | no_right_turn |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| s,f | sabc,dbef,dbef | depart,turn right,arrive |
|
||||
| s,g | sabc,aeg,aeg | depart,turn right,arrive |
|
||||
|
||||
@sliproads
|
||||
Scenario: Not a Sliproad, road not continuing straight
|
||||
Given the node map
|
||||
"""
|
||||
d
|
||||
.
|
||||
s . a . . b . . c
|
||||
` .
|
||||
` e . . g
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | highway | name | oneway |
|
||||
| sabc | primary | sabc | |
|
||||
| dbe | primary | dbe | yes |
|
||||
| aeg | primary | aeg | yes |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| s,c | sabc,sabc | depart,arrive |
|
||||
| s,g | sabc,aeg,aeg | depart,turn right,arrive |
|
||||
|
||||
@sliproads
|
||||
Scenario: Intersection too far away with Traffic Light shortly after initial split
|
||||
Given the node map
|
||||
"""
|
||||
d
|
||||
.
|
||||
s . a . . . . . . . . . . . . . t . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . b . . c
|
||||
` . . . . . . . . . . . .
|
||||
` . . . . . . . . . . . .
|
||||
` . . . . . . . . . . . .
|
||||
` . . . . . . . . . . . .
|
||||
` . . . . . . . . . . . .
|
||||
` . . . . . . . . . . . .
|
||||
` . . .
|
||||
` e
|
||||
.
|
||||
f
|
||||
.
|
||||
"""
|
||||
|
||||
And the nodes
|
||||
| node | highway |
|
||||
| t | traffic_signals |
|
||||
|
||||
And the ways
|
||||
| nodes | highway | name | oneway |
|
||||
| satbc | primary | sabc | |
|
||||
| dbef | primary | dbef | yes |
|
||||
| ae | primary | ae | yes |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| s,f | sabc,ae,dbef,dbef | depart,turn slight right,turn right,arrive |
|
||||
|
||||
@sliproads
|
||||
Scenario: Traffic Signal on Sliproad
|
||||
Given the node map
|
||||
"""
|
||||
d
|
||||
.
|
||||
s . a . . . . . b . . c
|
||||
` .
|
||||
` .
|
||||
` .
|
||||
t .
|
||||
` .
|
||||
e
|
||||
.
|
||||
.
|
||||
f
|
||||
"""
|
||||
|
||||
And the nodes
|
||||
| node | highway |
|
||||
| t | traffic_signals |
|
||||
|
||||
And the ways
|
||||
| nodes | highway | name | oneway |
|
||||
| sabc | primary | sabc | |
|
||||
| dbe | primary | dbe | yes |
|
||||
| ef | primary | ef | |
|
||||
| ate | primary | ate | yes |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| s,f | sabc,ef,ef | depart,turn right,arrive |
|
||||
|
||||
@sliproads
|
||||
Scenario: Sliproad tagged as link
|
||||
Given the node map
|
||||
"""
|
||||
d
|
||||
.
|
||||
s . a . . . . . b . . c
|
||||
` .
|
||||
` .
|
||||
` .
|
||||
` .
|
||||
` .
|
||||
e
|
||||
.
|
||||
.
|
||||
f
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | highway | name | oneway |
|
||||
| sabc | motorway | sabc | |
|
||||
| dbef | motorway | dbef | yes |
|
||||
| ae | motorway_link | ae | yes |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| s,f | sabc,dbef,dbef | depart,turn right,arrive |
|
||||
|
||||
@sliproads
|
||||
Scenario: Sliproad with same-ish names
|
||||
Given the node map
|
||||
"""
|
||||
d
|
||||
.
|
||||
s . a . . b . . c
|
||||
` .
|
||||
. e
|
||||
..
|
||||
.
|
||||
f
|
||||
.
|
||||
t
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | highway | name | ref | oneway |
|
||||
| sabc | primary | main | | |
|
||||
| dbe | primary | crossing | r0 | yes |
|
||||
| eft | primary | crossing | r0;r1 | yes |
|
||||
| af | primary | sliproad | | yes |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| s,t | main,crossing,crossing | depart,turn right,arrive |
|
||||
|
||||
@sliproads
|
||||
Scenario: Not a Sliproad, name mismatch
|
||||
Given the node map
|
||||
"""
|
||||
d
|
||||
.
|
||||
s . a . . b . . c
|
||||
` .
|
||||
. e
|
||||
. .
|
||||
..
|
||||
.
|
||||
f
|
||||
.
|
||||
t
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | highway | name | oneway |
|
||||
| sabc | primary | main | |
|
||||
| dbe | primary | top | yes |
|
||||
| ef | primary | bottom | yes |
|
||||
| ft | primary | away | yes |
|
||||
| af | primary | sliproad | yes |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| s,t | main,away,away | depart,turn right,arrive |
|
||||
|
||||
@sliproads
|
||||
Scenario: Not a Sliproad, low road priority
|
||||
Given the node map
|
||||
"""
|
||||
d
|
||||
.
|
||||
s . a . . b . . c
|
||||
` .
|
||||
. e
|
||||
. .
|
||||
..
|
||||
.
|
||||
f
|
||||
.
|
||||
t
|
||||
"""
|
||||
|
||||
And the ways
|
||||
# maxspeed otherwise service road will never be routed over and we won't see instructions
|
||||
| nodes | highway | name | maxspeed | oneway |
|
||||
| sabc | primary | main | 30 km/h | |
|
||||
| dbe | primary | crossing | 30 km/h | yes |
|
||||
| eft | primary | crossing | 30 km/h | yes |
|
||||
| ft | primary | away | 30 km/h | yes |
|
||||
| af | service | sliproad | 30 km/h | yes |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| s,t | main,away,away | depart,turn right,arrive |
|
||||
|
||||
@sliproads
|
||||
Scenario: Not a Sliproad, more than three roads at target intersection
|
||||
Given the node map
|
||||
"""
|
||||
d
|
||||
.
|
||||
s . a . . b . . c
|
||||
` .
|
||||
. e
|
||||
. .
|
||||
..
|
||||
. h
|
||||
f .
|
||||
. g
|
||||
t
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | highway | name | oneway |
|
||||
| sabc | primary | main | |
|
||||
| dbe | primary | top | yes |
|
||||
| eft | primary | bottom | yes |
|
||||
| fh | primary | another | |
|
||||
| fg | primary | another | |
|
||||
| af | primary | sliproad | yes |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| s,g | main,sliproad,another,another | depart,turn right,turn left,arrive |
|
||||
|
@ -1,6 +1,9 @@
|
||||
#ifndef OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_HPP_
|
||||
#define OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_HPP_
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
@ -10,6 +13,11 @@
|
||||
#include "util/node_based_graph.hpp"
|
||||
#include "util/typedefs.hpp" // EdgeID
|
||||
|
||||
#include <boost/range/adaptor/transformed.hpp>
|
||||
#include <boost/range/algorithm/find_if.hpp>
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
@ -60,7 +68,7 @@ struct IntersectionViewData : IntersectionShapeData
|
||||
// A Connected Road is the internal representation of a potential turn. Internally, we require
|
||||
// full list of all connected roads to determine the outcome.
|
||||
// The reasoning behind is that even invalid turns can influence the perceived angles, or even
|
||||
// instructions themselves. An pososible example can be described like this:
|
||||
// instructions themselves. An possible example can be described like this:
|
||||
//
|
||||
// aaa(2)aa
|
||||
// a - bbbbb
|
||||
@ -68,8 +76,7 @@ struct IntersectionViewData : IntersectionShapeData
|
||||
//
|
||||
// will not be perceived as a turn from (1) -> b, and as a U-turn from (1) -> (2).
|
||||
// In addition, they can influence whether a turn is obvious or not. b->(2) would also be no
|
||||
// turn-operation,
|
||||
// but rather a name change.
|
||||
// turn-operation, but rather a name change.
|
||||
//
|
||||
// If this were a normal intersection with
|
||||
//
|
||||
@ -103,6 +110,8 @@ struct ConnectedRoad final : IntersectionViewData
|
||||
// small helper function to print the content of a connected road
|
||||
std::string toString(const ConnectedRoad &road);
|
||||
|
||||
// Intersections are sorted roads: [0] being the UTurn road, then from sharp right to sharp left.
|
||||
|
||||
using IntersectionShape = std::vector<IntersectionShapeData>;
|
||||
|
||||
// Common operations shared among IntersectionView and Intersections.
|
||||
@ -117,6 +126,84 @@ template <typename Self> struct EnableIntersectionOps
|
||||
return std::min_element(self()->begin(), self()->end(), comp);
|
||||
}
|
||||
|
||||
// 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
|
||||
// allows checking intersections for validity
|
||||
auto valid() const
|
||||
{
|
||||
if (self()->empty())
|
||||
return false;
|
||||
|
||||
auto comp = [](const auto &lhs, const auto &rhs) { return lhs.CompareByAngle(rhs); };
|
||||
|
||||
const auto ordered = std::is_sorted(self()->begin(), self()->end(), comp);
|
||||
|
||||
if (!ordered)
|
||||
return false;
|
||||
|
||||
const auto uturn = self()->operator[](0).angle < std::numeric_limits<double>::epsilon();
|
||||
|
||||
if (!uturn)
|
||||
return false;
|
||||
|
||||
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.
|
||||
const auto &getUTurnRoad() const { return self()->operator[](0); }
|
||||
|
||||
// Returns the right-most road at this intersection.
|
||||
const auto &getRightmostRoad() const
|
||||
{
|
||||
return self()->size() > 1 ? self()->operator[](1) : self()->getUTurnRoad();
|
||||
}
|
||||
|
||||
// Returns the left-most road at this intersection.
|
||||
const auto &getLeftmostRoad() const
|
||||
{
|
||||
return self()->size() > 1 ? self()->back() : self()->getUTurnRoad();
|
||||
}
|
||||
|
||||
// Can this be skipped over?
|
||||
auto isTrafficSignalOrBarrier() const { return self()->size() == 2; }
|
||||
|
||||
// Checks if there is at least one road available (except UTurn road) on which to continue.
|
||||
auto isDeadEnd() const
|
||||
{
|
||||
auto pred = [](const auto &road) { return road.entry_allowed; };
|
||||
return !std::any_of(self()->begin() + 1, self()->end(), pred);
|
||||
}
|
||||
|
||||
// Returns the number of roads we can enter at this intersection, respectively.
|
||||
auto countEnterable() const
|
||||
{
|
||||
auto pred = [](const auto &road) { return road.entry_allowed; };
|
||||
return std::count_if(self()->begin(), self()->end(), pred);
|
||||
}
|
||||
|
||||
// Returns the number of roads we can not enter at this intersection, respectively.
|
||||
auto countNonEnterable() const { return self()->size() - self()->countEnterable(); }
|
||||
|
||||
private:
|
||||
auto self() { return static_cast<Self *>(this); }
|
||||
auto self() const { return static_cast<const Self *>(this); }
|
||||
@ -126,28 +213,12 @@ struct IntersectionView final : std::vector<IntersectionViewData>, //
|
||||
EnableIntersectionOps<IntersectionView> //
|
||||
{
|
||||
using Base = std::vector<IntersectionViewData>;
|
||||
|
||||
bool valid() const
|
||||
{
|
||||
return std::is_sorted(begin(), end(), std::mem_fn(&IntersectionViewData::CompareByAngle));
|
||||
};
|
||||
};
|
||||
|
||||
struct Intersection final : std::vector<ConnectedRoad>, //
|
||||
EnableIntersectionOps<Intersection> //
|
||||
{
|
||||
using Base = std::vector<ConnectedRoad>;
|
||||
|
||||
/*
|
||||
* 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
|
||||
* allows checking intersections for validity
|
||||
*/
|
||||
bool valid() const;
|
||||
|
||||
// given all possible turns, which is the highest connected number of lanes per turn. This value
|
||||
// is used, for example, during generation of intersections.
|
||||
std::uint8_t getHighestConnectedLaneCount(const util::NodeBasedDynamicGraph &) const;
|
||||
};
|
||||
|
||||
} // namespace guidance
|
||||
|
@ -35,6 +35,12 @@ class IntersectionGenerator
|
||||
const std::vector<QueryNode> &node_info_list,
|
||||
const CompressedEdgeContainer &compressed_edge_container);
|
||||
|
||||
// For a source node `a` and a via edge `ab` creates an intersection at target `b`.
|
||||
//
|
||||
// a . . . b . .
|
||||
// .
|
||||
// .
|
||||
//
|
||||
IntersectionView operator()(const NodeID nid, const EdgeID via_eid) const;
|
||||
|
||||
/*
|
||||
|
@ -3,15 +3,22 @@
|
||||
|
||||
#include "extractor/guidance/intersection.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/coordinate_calculation.hpp"
|
||||
#include "util/guidance/name_announcements.hpp"
|
||||
#include "util/name_table.hpp"
|
||||
#include "util/node_based_graph.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
@ -48,9 +55,7 @@ class IntersectionHandler
|
||||
const util::NameTable &name_table;
|
||||
const SuffixTable &street_name_suffix_table;
|
||||
const IntersectionGenerator &intersection_generator;
|
||||
|
||||
// counts the number on allowed entry roads
|
||||
std::size_t countValid(const Intersection &intersection) const;
|
||||
const NodeBasedGraphWalker graph_walker; // for skipping traffic signal, distances etc.
|
||||
|
||||
// Decide on a basic turn types
|
||||
TurnType::Enum findBasicTurnType(const EdgeID via_edge, const ConnectedRoad &candidate) const;
|
||||
@ -59,7 +64,8 @@ class IntersectionHandler
|
||||
// determining whether there is a road that can be seen as obvious turn in the presence of many
|
||||
// other possible turns. The function will consider road categories and other inputs like the
|
||||
// turn angles.
|
||||
std::size_t findObviousTurn(const EdgeID via_edge, const Intersection &intersection) const;
|
||||
template <typename IntersectionType> // works with Intersection and IntersectionView
|
||||
std::size_t findObviousTurn(const EdgeID via_edge, const IntersectionType &intersection) const;
|
||||
|
||||
// Obvious turns can still take multiple forms. This function looks at the turn onto a road
|
||||
// candidate when coming from a via_edge and determines the best instruction to emit.
|
||||
@ -83,9 +89,457 @@ class IntersectionHandler
|
||||
const std::size_t begin,
|
||||
const std::size_t end) const;
|
||||
|
||||
// Checks the intersection for a through street connected to `intersection[index]`
|
||||
bool isThroughStreet(const std::size_t index, const Intersection &intersection) const;
|
||||
|
||||
// See `getNextIntersection`
|
||||
struct IntersectionViewAndNode final
|
||||
{
|
||||
IntersectionView intersection; // < actual intersection
|
||||
NodeID node; // < node at this intersection
|
||||
};
|
||||
|
||||
// Skips over artificial intersections i.e. traffic lights, barriers etc.
|
||||
// Returns the next non-artificial intersection and its node in the node based
|
||||
// graph if an intersection could be found or none otherwise.
|
||||
//
|
||||
// a ... tl ... b .. c
|
||||
// .
|
||||
// .
|
||||
// d
|
||||
//
|
||||
// ^ at
|
||||
// ^ via
|
||||
//
|
||||
// For this scenario returns intersection at `b` and `b`.
|
||||
boost::optional<IntersectionHandler::IntersectionViewAndNode>
|
||||
getNextIntersection(const NodeID at, const EdgeID via) const;
|
||||
};
|
||||
|
||||
// Impl.
|
||||
|
||||
template <typename IntersectionType> // works with Intersection and IntersectionView
|
||||
std::size_t IntersectionHandler::findObviousTurn(const EdgeID via_edge,
|
||||
const IntersectionType &intersection) const
|
||||
{
|
||||
using EdgeData = osrm::util::NodeBasedDynamicGraph::EdgeData;
|
||||
using osrm::util::angularDeviation;
|
||||
|
||||
// no obvious road
|
||||
if (intersection.size() == 1)
|
||||
return 0;
|
||||
|
||||
// a single non u-turn is obvious
|
||||
if (intersection.size() == 2)
|
||||
return 1;
|
||||
|
||||
const EdgeData &in_way_data = node_based_graph.GetEdgeData(via_edge);
|
||||
|
||||
// the strategy for picking the most obvious turn involves deciding between
|
||||
// an overall best candidate and a best candidate that shares the same name
|
||||
// as the in road, i.e. a continue road
|
||||
std::size_t best_option = 0;
|
||||
double best_option_deviation = 180;
|
||||
std::size_t best_continue = 0;
|
||||
double best_continue_deviation = 180;
|
||||
|
||||
/* helper functions */
|
||||
const auto IsContinueRoad = [&](const EdgeData &way_data) {
|
||||
return !util::guidance::requiresNameAnnounced(
|
||||
in_way_data.name_id, way_data.name_id, name_table, street_name_suffix_table);
|
||||
};
|
||||
auto sameOrHigherPriority = [&in_way_data](const auto &way_data) {
|
||||
return way_data.road_classification.GetPriority() <=
|
||||
in_way_data.road_classification.GetPriority();
|
||||
};
|
||||
auto IsLowPriority = [](const auto &way_data) {
|
||||
return way_data.road_classification.IsLowPriorityRoadClass();
|
||||
};
|
||||
// These two Compare functions are used for sifting out best option and continue
|
||||
// candidates by evaluating all the ways in an intersection by what they share
|
||||
// with the in way. Ideal candidates are of similar road class with the in way
|
||||
// and are require relatively straight turns.
|
||||
const auto RoadCompare = [&](const auto &lhs, const auto &rhs) {
|
||||
const EdgeData &lhs_data = node_based_graph.GetEdgeData(lhs.eid);
|
||||
const EdgeData &rhs_data = node_based_graph.GetEdgeData(rhs.eid);
|
||||
const auto lhs_deviation = angularDeviation(lhs.angle, STRAIGHT_ANGLE);
|
||||
const auto rhs_deviation = angularDeviation(rhs.angle, STRAIGHT_ANGLE);
|
||||
|
||||
const bool rhs_same_classification =
|
||||
rhs_data.road_classification == in_way_data.road_classification;
|
||||
const bool lhs_same_classification =
|
||||
lhs_data.road_classification == in_way_data.road_classification;
|
||||
const bool rhs_same_or_higher_priority = sameOrHigherPriority(rhs_data);
|
||||
const bool rhs_low_priority = IsLowPriority(rhs_data);
|
||||
const bool lhs_same_or_higher_priority = sameOrHigherPriority(lhs_data);
|
||||
const bool lhs_low_priority = IsLowPriority(lhs_data);
|
||||
auto left_tie = std::tie(lhs.entry_allowed,
|
||||
lhs_same_or_higher_priority,
|
||||
rhs_low_priority,
|
||||
rhs_deviation,
|
||||
lhs_same_classification);
|
||||
auto right_tie = std::tie(rhs.entry_allowed,
|
||||
rhs_same_or_higher_priority,
|
||||
lhs_low_priority,
|
||||
lhs_deviation,
|
||||
rhs_same_classification);
|
||||
return left_tie > right_tie;
|
||||
};
|
||||
const auto RoadCompareSameName = [&](const auto &lhs, const auto &rhs) {
|
||||
const EdgeData &lhs_data = node_based_graph.GetEdgeData(lhs.eid);
|
||||
const EdgeData &rhs_data = node_based_graph.GetEdgeData(rhs.eid);
|
||||
const auto lhs_continues = IsContinueRoad(lhs_data);
|
||||
const auto rhs_continues = IsContinueRoad(rhs_data);
|
||||
const auto left_tie = std::tie(lhs.entry_allowed, lhs_continues);
|
||||
const auto right_tie = std::tie(rhs.entry_allowed, rhs_continues);
|
||||
return left_tie > right_tie || (left_tie == right_tie && RoadCompare(lhs, rhs));
|
||||
};
|
||||
|
||||
auto best_option_it = std::min_element(begin(intersection), end(intersection), RoadCompare);
|
||||
|
||||
// min element should only return end() when vector is empty
|
||||
BOOST_ASSERT(best_option_it != end(intersection));
|
||||
|
||||
best_option = std::distance(begin(intersection), best_option_it);
|
||||
best_option_deviation = angularDeviation(intersection[best_option].angle, STRAIGHT_ANGLE);
|
||||
const auto &best_option_data = node_based_graph.GetEdgeData(intersection[best_option].eid);
|
||||
|
||||
// Unless the in way is also low priority, it is generally undesirable to
|
||||
// indicate that a low priority road is obvious
|
||||
if (IsLowPriority(best_option_data) &&
|
||||
best_option_data.road_classification != in_way_data.road_classification)
|
||||
{
|
||||
best_option = 0;
|
||||
best_option_deviation = 180;
|
||||
}
|
||||
|
||||
// double check if the way with the lowest deviation from straight is still be better choice
|
||||
const auto straightest = intersection.findClosestTurn(STRAIGHT_ANGLE);
|
||||
if (straightest != best_option_it)
|
||||
{
|
||||
const EdgeData &straightest_data = node_based_graph.GetEdgeData(straightest->eid);
|
||||
double straightest_data_deviation = angularDeviation(straightest->angle, STRAIGHT_ANGLE);
|
||||
const auto deviation_diff =
|
||||
std::abs(best_option_deviation - straightest_data_deviation) > FUZZY_ANGLE_DIFFERENCE;
|
||||
const auto not_ramp_class = !straightest_data.road_classification.IsRampClass();
|
||||
const auto not_link_class = !straightest_data.road_classification.IsLinkClass();
|
||||
if (deviation_diff && !IsLowPriority(straightest_data) && not_ramp_class &&
|
||||
not_link_class && !IsContinueRoad(best_option_data))
|
||||
{
|
||||
best_option = std::distance(begin(intersection), straightest);
|
||||
best_option_deviation =
|
||||
angularDeviation(intersection[best_option].angle, STRAIGHT_ANGLE);
|
||||
}
|
||||
}
|
||||
|
||||
// No non-low priority roads? Declare no obvious turn
|
||||
if (best_option == 0)
|
||||
return 0;
|
||||
|
||||
auto best_continue_it =
|
||||
std::min_element(begin(intersection), end(intersection), RoadCompareSameName);
|
||||
const auto best_continue_data = node_based_graph.GetEdgeData(best_continue_it->eid);
|
||||
if (IsContinueRoad(best_continue_data) ||
|
||||
(in_way_data.name_id == EMPTY_NAMEID && best_continue_data.name_id == EMPTY_NAMEID))
|
||||
{
|
||||
best_continue = std::distance(begin(intersection), best_continue_it);
|
||||
best_continue_deviation =
|
||||
angularDeviation(intersection[best_continue].angle, STRAIGHT_ANGLE);
|
||||
}
|
||||
|
||||
// if the best angle is going straight but the road is turning, declare no obvious turn
|
||||
if (0 != best_continue && best_option != best_continue &&
|
||||
best_option_deviation < MAXIMAL_ALLOWED_NO_TURN_DEVIATION &&
|
||||
node_based_graph.GetEdgeData(intersection[best_continue].eid).road_classification ==
|
||||
best_option_data.road_classification)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// get a count of number of ways from that intersection that qualify to have
|
||||
// continue instruction because they share a name with the approaching way
|
||||
const std::int64_t continue_count =
|
||||
count_if(++begin(intersection), end(intersection), [&](const auto &way) {
|
||||
return IsContinueRoad(node_based_graph.GetEdgeData(way.eid));
|
||||
});
|
||||
const std::int64_t continue_count_valid =
|
||||
count_if(++begin(intersection), end(intersection), [&](const auto &way) {
|
||||
return IsContinueRoad(node_based_graph.GetEdgeData(way.eid)) && way.entry_allowed;
|
||||
});
|
||||
|
||||
// checks if continue candidates are sharp turns
|
||||
const bool all_continues_are_narrow = [&]() {
|
||||
return std::count_if(begin(intersection), end(intersection), [&](const auto &road) {
|
||||
const EdgeData &road_data = node_based_graph.GetEdgeData(road.eid);
|
||||
const double &road_angle = angularDeviation(road.angle, STRAIGHT_ANGLE);
|
||||
return IsContinueRoad(road_data) && (road_angle < NARROW_TURN_ANGLE);
|
||||
}) == continue_count;
|
||||
}();
|
||||
|
||||
// return true if the best_option candidate is more promising than the best_continue candidate
|
||||
// otherwise return false, the best_continue candidate is more promising
|
||||
const auto best_over_best_continue = [&]() {
|
||||
// no continue road exists
|
||||
if (best_continue == 0)
|
||||
return true;
|
||||
|
||||
// we have multiple continues and not all are narrow. This suggests that
|
||||
// the continue candidates are ambiguous
|
||||
if (!all_continues_are_narrow && (continue_count >= 2 && intersection.size() >= 4))
|
||||
return true;
|
||||
|
||||
// if the best continue is not narrow and we also have at least 2 possible choices, the
|
||||
// intersection size does not matter anymore
|
||||
if (continue_count_valid >= 2 && best_continue_deviation >= 2 * NARROW_TURN_ANGLE)
|
||||
return true;
|
||||
|
||||
// continue data now most certainly exists
|
||||
const auto &continue_data = node_based_graph.GetEdgeData(intersection[best_continue].eid);
|
||||
|
||||
// best_continue is obvious by road class
|
||||
if (obviousByRoadClass(in_way_data.road_classification,
|
||||
continue_data.road_classification,
|
||||
best_option_data.road_classification))
|
||||
return false;
|
||||
|
||||
// best_option is obvious by road class
|
||||
if (obviousByRoadClass(in_way_data.road_classification,
|
||||
best_option_data.road_classification,
|
||||
continue_data.road_classification))
|
||||
return true;
|
||||
|
||||
// the best_option deviation is very straight and not a ramp
|
||||
if (best_option_deviation < best_continue_deviation &&
|
||||
best_option_deviation < FUZZY_ANGLE_DIFFERENCE &&
|
||||
!best_option_data.road_classification.IsRampClass())
|
||||
return true;
|
||||
|
||||
// the continue road is of a lower priority, while the road continues on the same priority
|
||||
// with a better angle
|
||||
if (best_option_deviation < best_continue_deviation &&
|
||||
in_way_data.road_classification == best_option_data.road_classification &&
|
||||
continue_data.road_classification.GetPriority() >
|
||||
best_option_data.road_classification.GetPriority())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}();
|
||||
|
||||
if (best_over_best_continue)
|
||||
{
|
||||
// Find left/right deviation
|
||||
// skipping over service roads
|
||||
const std::size_t left_index = [&]() {
|
||||
const auto index_candidate = (best_option + 1) % intersection.size();
|
||||
if (index_candidate == 0)
|
||||
return index_candidate;
|
||||
const auto &candidate_data =
|
||||
node_based_graph.GetEdgeData(intersection[index_candidate].eid);
|
||||
if (obviousByRoadClass(in_way_data.road_classification,
|
||||
best_option_data.road_classification,
|
||||
candidate_data.road_classification))
|
||||
return (index_candidate + 1) % intersection.size();
|
||||
else
|
||||
return index_candidate;
|
||||
|
||||
}();
|
||||
const auto right_index = [&]() {
|
||||
BOOST_ASSERT(best_option > 0);
|
||||
const auto index_candidate = best_option - 1;
|
||||
if (index_candidate == 0)
|
||||
return index_candidate;
|
||||
const auto candidate_data =
|
||||
node_based_graph.GetEdgeData(intersection[index_candidate].eid);
|
||||
if (obviousByRoadClass(in_way_data.road_classification,
|
||||
best_option_data.road_classification,
|
||||
candidate_data.road_classification))
|
||||
return index_candidate - 1;
|
||||
else
|
||||
return index_candidate;
|
||||
}();
|
||||
|
||||
const double left_deviation =
|
||||
angularDeviation(intersection[left_index].angle, STRAIGHT_ANGLE);
|
||||
const double right_deviation =
|
||||
angularDeviation(intersection[right_index].angle, STRAIGHT_ANGLE);
|
||||
|
||||
// return best_option candidate if it is nearly straight and distinct from the nearest other
|
||||
// out
|
||||
// way
|
||||
if (best_option_deviation < MAXIMAL_ALLOWED_NO_TURN_DEVIATION &&
|
||||
std::min(left_deviation, right_deviation) > FUZZY_ANGLE_DIFFERENCE)
|
||||
return best_option;
|
||||
|
||||
const auto &left_data = node_based_graph.GetEdgeData(intersection[left_index].eid);
|
||||
const auto &right_data = node_based_graph.GetEdgeData(intersection[right_index].eid);
|
||||
|
||||
const bool obvious_to_left =
|
||||
left_index == 0 || obviousByRoadClass(in_way_data.road_classification,
|
||||
best_option_data.road_classification,
|
||||
left_data.road_classification);
|
||||
const bool obvious_to_right =
|
||||
right_index == 0 || obviousByRoadClass(in_way_data.road_classification,
|
||||
best_option_data.road_classification,
|
||||
right_data.road_classification);
|
||||
|
||||
// if the best_option turn isn't narrow, but there is a nearly straight turn, we don't
|
||||
// consider the
|
||||
// turn obvious
|
||||
const auto check_narrow = [&intersection, best_option_deviation](const std::size_t index) {
|
||||
return angularDeviation(intersection[index].angle, STRAIGHT_ANGLE) <=
|
||||
FUZZY_ANGLE_DIFFERENCE &&
|
||||
(best_option_deviation > NARROW_TURN_ANGLE || intersection[index].entry_allowed);
|
||||
};
|
||||
|
||||
// other narrow turns?
|
||||
if (check_narrow(right_index) && !obvious_to_right)
|
||||
return 0;
|
||||
|
||||
if (check_narrow(left_index) && !obvious_to_left)
|
||||
return 0;
|
||||
|
||||
// checks if a given way in the intersection is distinct enough from the best_option
|
||||
// candidate
|
||||
const auto isDistinct = [&](const std::size_t index, const double deviation) {
|
||||
/*
|
||||
If the neighbor is not possible to enter, we allow for a lower
|
||||
distinction rate. If the road category is smaller, its also adjusted. Only
|
||||
roads of the same priority require the full distinction ratio.
|
||||
*/
|
||||
const auto &best_option_data =
|
||||
node_based_graph.GetEdgeData(intersection[best_option].eid);
|
||||
const auto adjusted_distinction_ratio = [&]() {
|
||||
// not allowed competitors are easily distinct
|
||||
if (!intersection[index].entry_allowed)
|
||||
return 0.7 * DISTINCTION_RATIO;
|
||||
// a bit less obvious are road classes
|
||||
else if (in_way_data.road_classification == best_option_data.road_classification &&
|
||||
best_option_data.road_classification.GetPriority() <
|
||||
node_based_graph.GetEdgeData(intersection[index].eid)
|
||||
.road_classification.GetPriority())
|
||||
return 0.8 * DISTINCTION_RATIO;
|
||||
// if road classes are the same, we use the full ratio
|
||||
else
|
||||
return DISTINCTION_RATIO;
|
||||
}();
|
||||
return index == 0 || deviation / best_option_deviation >= adjusted_distinction_ratio ||
|
||||
(deviation <= NARROW_TURN_ANGLE && !intersection[index].entry_allowed);
|
||||
};
|
||||
|
||||
const bool distinct_to_left = isDistinct(left_index, left_deviation);
|
||||
const bool distinct_to_right = isDistinct(right_index, right_deviation);
|
||||
// Well distinct turn that is nearly straight
|
||||
if ((distinct_to_left || obvious_to_left) && (distinct_to_right || obvious_to_right))
|
||||
return best_option;
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto &continue_data = node_based_graph.GetEdgeData(intersection[best_continue].eid);
|
||||
if (std::abs(best_continue_deviation) < 1)
|
||||
return best_continue;
|
||||
|
||||
// check if any other similar best continues exist
|
||||
std::size_t i, last = intersection.size();
|
||||
for (i = 1; i < last; ++i)
|
||||
{
|
||||
if (i == best_continue || !intersection[i].entry_allowed)
|
||||
continue;
|
||||
|
||||
const auto &turn_data = node_based_graph.GetEdgeData(intersection[i].eid);
|
||||
const bool is_obvious_by_road_class =
|
||||
obviousByRoadClass(in_way_data.road_classification,
|
||||
continue_data.road_classification,
|
||||
turn_data.road_classification);
|
||||
|
||||
// if the main road is obvious by class, we ignore the current road as a potential
|
||||
// prevention of obviousness
|
||||
if (is_obvious_by_road_class)
|
||||
continue;
|
||||
|
||||
// continuation could be grouped with a straight turn and the turning road is a ramp
|
||||
if (turn_data.road_classification.IsRampClass() &&
|
||||
best_continue_deviation < GROUP_ANGLE &&
|
||||
!continue_data.road_classification.IsRampClass())
|
||||
continue;
|
||||
|
||||
// perfectly straight turns prevent obviousness
|
||||
const auto turn_deviation = angularDeviation(intersection[i].angle, STRAIGHT_ANGLE);
|
||||
if (turn_deviation < FUZZY_ANGLE_DIFFERENCE)
|
||||
return 0;
|
||||
|
||||
const auto deviation_ratio = turn_deviation / best_continue_deviation;
|
||||
|
||||
// in comparison to normal deviations, a continue road can offer a smaller distinction
|
||||
// ratio. Other roads close to the turn angle are not as obvious, if one road continues.
|
||||
if (deviation_ratio < DISTINCTION_RATIO / 1.5)
|
||||
return 0;
|
||||
|
||||
/* in comparison to another continuing road, we need a better distinction. This prevents
|
||||
situations where the turn is probably less obvious. An example are places that have a
|
||||
road with the same name entering/exiting:
|
||||
|
||||
d
|
||||
/
|
||||
/
|
||||
a -- b
|
||||
\
|
||||
\
|
||||
c
|
||||
*/
|
||||
|
||||
const auto same_name = !util::guidance::requiresNameAnnounced(
|
||||
turn_data.name_id, continue_data.name_id, name_table, street_name_suffix_table);
|
||||
|
||||
if (same_name && deviation_ratio < 1.5 * DISTINCTION_RATIO)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Segregated intersections can result in us finding an obvious turn, even though its only
|
||||
// obvious due to a very short segment in between. So if the segment coming in is very
|
||||
// short, we check the previous intersection for other continues in the opposite bearing.
|
||||
const auto node_at_intersection = node_based_graph.GetTarget(via_edge);
|
||||
|
||||
const double constexpr MAX_COLLAPSE_DISTANCE = 30;
|
||||
const auto distance_at_u_turn = intersection[0].segment_length;
|
||||
if (distance_at_u_turn < MAX_COLLAPSE_DISTANCE)
|
||||
{
|
||||
// this request here actually goes against the direction of the ingoing edgeid. This can
|
||||
// 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
|
||||
// turn, this should be alright.
|
||||
NodeID new_node;
|
||||
const auto previous_intersection = [&]() {
|
||||
EdgeID turn_edge;
|
||||
std::tie(new_node, turn_edge) = intersection_generator.SkipDegreeTwoNodes(
|
||||
node_at_intersection, intersection[0].eid);
|
||||
return intersection_generator.GetConnectedRoads(new_node, turn_edge);
|
||||
}();
|
||||
|
||||
if (new_node != node_at_intersection)
|
||||
{
|
||||
const auto continue_road = intersection[best_continue];
|
||||
for (const auto &comparison_road : previous_intersection)
|
||||
{
|
||||
// since we look at the intersection in the wrong direction, a similar angle
|
||||
// actually represents a near 180 degree different in bearings between the two
|
||||
// roads. So if there is a road that is enterable in the opposite direction just
|
||||
// prior, a turn is not obvious
|
||||
const auto &turn_data = node_based_graph.GetEdgeData(comparison_road.eid);
|
||||
if (angularDeviation(comparison_road.angle, STRAIGHT_ANGLE) > GROUP_ANGLE &&
|
||||
angularDeviation(comparison_road.angle, continue_road.angle) <
|
||||
FUZZY_ANGLE_DIFFERENCE &&
|
||||
!turn_data.reversed && continue_data.CanCombineWith(turn_data))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return best_continue;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "extractor/guidance/constants.hpp"
|
||||
#include "extractor/guidance/intersection_generator.hpp"
|
||||
#include "util/coordinate.hpp"
|
||||
#include "util/coordinate_calculation.hpp"
|
||||
#include "util/node_based_graph.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
@ -41,7 +42,7 @@ class NodeBasedGraphWalker
|
||||
boost::optional<std::pair<NodeID, EdgeID>> TraverseRoad(NodeID starting_at_node_id,
|
||||
EdgeID following_edge_id,
|
||||
accumulator_type &accumulator,
|
||||
const selector_type &selector);
|
||||
const selector_type &selector) const;
|
||||
|
||||
private:
|
||||
const util::NodeBasedDynamicGraph &node_based_graph;
|
||||
@ -139,7 +140,7 @@ boost::optional<std::pair<NodeID, EdgeID>>
|
||||
NodeBasedGraphWalker::TraverseRoad(NodeID current_node_id,
|
||||
EdgeID current_edge_id,
|
||||
accumulator_type &accumulator,
|
||||
const selector_type &selector)
|
||||
const selector_type &selector) const
|
||||
{
|
||||
/*
|
||||
* since graph hopping is used in many ways, we don't generate an adjusted intersection
|
||||
@ -190,6 +191,60 @@ NodeBasedGraphWalker::TraverseRoad(NodeID current_node_id,
|
||||
return {};
|
||||
}
|
||||
|
||||
struct SkipTrafficSignalBarrierRoadSelector
|
||||
{
|
||||
boost::optional<EdgeID> operator()(const NodeID,
|
||||
const EdgeID,
|
||||
const IntersectionView &intersection,
|
||||
const util::NodeBasedDynamicGraph &) const
|
||||
{
|
||||
if (intersection.isTrafficSignalOrBarrier())
|
||||
{
|
||||
return boost::make_optional(intersection[1].eid);
|
||||
}
|
||||
else
|
||||
{
|
||||
return boost::none;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct DistanceToNextIntersectionAccumulator
|
||||
{
|
||||
DistanceToNextIntersectionAccumulator(
|
||||
const extractor::guidance::CoordinateExtractor &extractor_,
|
||||
const util::NodeBasedDynamicGraph &graph_,
|
||||
const double threshold)
|
||||
: extractor{extractor_}, graph{graph_}, threshold{threshold}
|
||||
{
|
||||
}
|
||||
|
||||
bool terminate()
|
||||
{
|
||||
if (distance > threshold)
|
||||
{
|
||||
too_far_away = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void update(const NodeID start, const EdgeID onto, const NodeID)
|
||||
{
|
||||
using namespace util::coordinate_calculation;
|
||||
|
||||
const auto coords = extractor.GetForwardCoordinatesAlongRoad(start, onto);
|
||||
distance += getLength(coords, &haversineDistance);
|
||||
}
|
||||
|
||||
const extractor::guidance::CoordinateExtractor &extractor;
|
||||
const util::NodeBasedDynamicGraph &graph;
|
||||
const double threshold;
|
||||
bool too_far_away = false;
|
||||
double distance = 0.;
|
||||
};
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
|
@ -11,6 +11,8 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
@ -20,7 +22,7 @@ namespace guidance
|
||||
|
||||
// Intersection handlers deal with all issues related to intersections.
|
||||
// They assign appropriate turn operations to the TurnOperations.
|
||||
class SliproadHandler : public IntersectionHandler
|
||||
class SliproadHandler final : public IntersectionHandler
|
||||
{
|
||||
public:
|
||||
SliproadHandler(const IntersectionGenerator &intersection_generator,
|
||||
@ -40,6 +42,37 @@ class SliproadHandler : public IntersectionHandler
|
||||
Intersection operator()(const NodeID nid,
|
||||
const EdgeID via_eid,
|
||||
Intersection intersection) const override final;
|
||||
|
||||
private:
|
||||
boost::optional<std::size_t> getObviousIndexWithSliproads(const EdgeID from,
|
||||
const Intersection &intersection,
|
||||
const NodeID at) const;
|
||||
|
||||
// Next intersection from `start` onto `onto` is too far away for a Siproad scenario
|
||||
bool nextIntersectionIsTooFarAway(const NodeID start, const EdgeID onto) const;
|
||||
|
||||
// Through street: does a road continue with from's name at the intersection
|
||||
bool isThroughStreet(const EdgeID from, const IntersectionView &intersection) const;
|
||||
|
||||
// Does the road from `current` to `next` continue
|
||||
bool roadContinues(const EdgeID current, const EdgeID next) const;
|
||||
|
||||
// Is the area under the triangle a valid Sliproad triangle
|
||||
bool isValidSliproadArea(const double max_area, const NodeID, const NodeID, const NodeID) const;
|
||||
|
||||
// Is the Sliproad a link the both roads it shortcuts must not be links
|
||||
bool isValidSliproadLink(const IntersectionViewData &sliproad,
|
||||
const IntersectionViewData &first,
|
||||
const IntersectionViewData &second) const;
|
||||
|
||||
// Could a Sliproad reach this intersection?
|
||||
static bool canBeTargetOfSliproad(const IntersectionView &intersection);
|
||||
|
||||
// Scales a threshold based on the underlying road classification.
|
||||
// Example: a 100 m threshold for a highway if different on living streets.
|
||||
// The return value is guaranteed to not be larger than `threshold`.
|
||||
static double scaledThresholdByRoadClass(const double max_threshold,
|
||||
const RoadClassification &classification);
|
||||
};
|
||||
|
||||
} // namespace guidance
|
||||
|
@ -1,13 +1,7 @@
|
||||
#include "extractor/guidance/intersection.hpp"
|
||||
|
||||
#include <boost/range/adaptor/transformed.hpp>
|
||||
#include <boost/range/algorithm/find_if.hpp>
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
using osrm::util::angularDeviation;
|
||||
|
||||
@ -71,34 +65,6 @@ std::string toString(const ConnectedRoad &road)
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Intersection::valid() const
|
||||
{
|
||||
return !empty() &&
|
||||
std::is_sorted(begin(), end(), std::mem_fn(&ConnectedRoad::compareByAngle)) &&
|
||||
operator[](0).angle < std::numeric_limits<double>::epsilon();
|
||||
}
|
||||
|
||||
std::uint8_t
|
||||
Intersection::getHighestConnectedLaneCount(const util::NodeBasedDynamicGraph &graph) const
|
||||
{
|
||||
BOOST_ASSERT(valid()); // non empty()
|
||||
|
||||
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 = *this | boost::adaptors::transformed(to_lane_count);
|
||||
boost::range::find_if(view, extract_maximal_value);
|
||||
return max_lanes;
|
||||
}
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
|
@ -1,9 +1,8 @@
|
||||
#include "extractor/guidance/intersection_handler.hpp"
|
||||
#include "extractor/guidance/constants.hpp"
|
||||
|
||||
#include "util/coordinate_calculation.hpp"
|
||||
#include "util/log.hpp"
|
||||
#include "util/guidance/name_announcements.hpp"
|
||||
#include "util/log.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
@ -33,17 +32,11 @@ IntersectionHandler::IntersectionHandler(const util::NodeBasedDynamicGraph &node
|
||||
const IntersectionGenerator &intersection_generator)
|
||||
: node_based_graph(node_based_graph), node_info_list(node_info_list), name_table(name_table),
|
||||
street_name_suffix_table(street_name_suffix_table),
|
||||
intersection_generator(intersection_generator)
|
||||
intersection_generator(intersection_generator),
|
||||
graph_walker(node_based_graph, intersection_generator)
|
||||
{
|
||||
}
|
||||
|
||||
std::size_t IntersectionHandler::countValid(const Intersection &intersection) const
|
||||
{
|
||||
return std::count_if(intersection.begin(), intersection.end(), [](const ConnectedRoad &road) {
|
||||
return road.entry_allowed;
|
||||
});
|
||||
}
|
||||
|
||||
TurnType::Enum IntersectionHandler::findBasicTurnType(const EdgeID via_edge,
|
||||
const ConnectedRoad &road) const
|
||||
{
|
||||
@ -371,423 +364,44 @@ bool IntersectionHandler::isThroughStreet(const std::size_t index,
|
||||
return false;
|
||||
}
|
||||
|
||||
std::size_t IntersectionHandler::findObviousTurn(const EdgeID via_edge,
|
||||
const Intersection &intersection) const
|
||||
boost::optional<IntersectionHandler::IntersectionViewAndNode>
|
||||
IntersectionHandler::getNextIntersection(const NodeID at, const EdgeID via) const
|
||||
{
|
||||
// no obvious road
|
||||
if (intersection.size() == 1)
|
||||
return 0;
|
||||
// We use the intersection generator to jump over traffic signals, barriers. The intersection
|
||||
// generater takes a starting node and a corresponding edge starting at this node. It returns
|
||||
// the next non-artificial intersection writing as out param. the source node and the edge
|
||||
// for which the target is the next intersection.
|
||||
//
|
||||
// . .
|
||||
// a . . tl . . c .
|
||||
// . .
|
||||
//
|
||||
// e0 ^ ^ e1
|
||||
//
|
||||
// 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.
|
||||
|
||||
// a single non u-turn is obvious
|
||||
if (intersection.size() == 2)
|
||||
return 1;
|
||||
NodeID node = SPECIAL_NODEID;
|
||||
EdgeID edge = SPECIAL_EDGEID;
|
||||
|
||||
const EdgeData &in_way_data = node_based_graph.GetEdgeData(via_edge);
|
||||
std::tie(node, edge) = intersection_generator.SkipDegreeTwoNodes(at, via);
|
||||
auto intersection = intersection_generator(node, edge);
|
||||
|
||||
// the strategy for picking the most obvious turn involves deciding between
|
||||
// an overall best candidate and a best candidate that shares the same name
|
||||
// as the in road, i.e. a continue road
|
||||
std::size_t best_option = 0;
|
||||
double best_option_deviation = 180;
|
||||
std::size_t best_continue = 0;
|
||||
double best_continue_deviation = 180;
|
||||
|
||||
/* helper functions */
|
||||
const auto IsContinueRoad = [&](const EdgeData &way_data) {
|
||||
return !util::guidance::requiresNameAnnounced(
|
||||
in_way_data.name_id, way_data.name_id, name_table, street_name_suffix_table);
|
||||
};
|
||||
auto sameOrHigherPriority = [&in_way_data](const auto &way_data) {
|
||||
return way_data.road_classification.GetPriority() <=
|
||||
in_way_data.road_classification.GetPriority();
|
||||
};
|
||||
auto IsLowPriority = [](const auto &way_data) {
|
||||
return way_data.road_classification.IsLowPriorityRoadClass();
|
||||
};
|
||||
// These two Compare functions are used for sifting out best option and continue
|
||||
// candidates by evaluating all the ways in an intersection by what they share
|
||||
// with the in way. Ideal candidates are of similar road class with the in way
|
||||
// and are require relatively straight turns.
|
||||
const auto RoadCompare = [&](const ConnectedRoad &lhs, const ConnectedRoad &rhs) {
|
||||
const EdgeData &lhs_data = node_based_graph.GetEdgeData(lhs.eid);
|
||||
const EdgeData &rhs_data = node_based_graph.GetEdgeData(rhs.eid);
|
||||
const auto lhs_deviation = angularDeviation(lhs.angle, STRAIGHT_ANGLE);
|
||||
const auto rhs_deviation = angularDeviation(rhs.angle, STRAIGHT_ANGLE);
|
||||
|
||||
const bool rhs_same_classification =
|
||||
rhs_data.road_classification == in_way_data.road_classification;
|
||||
const bool lhs_same_classification =
|
||||
lhs_data.road_classification == in_way_data.road_classification;
|
||||
const bool rhs_same_or_higher_priority = sameOrHigherPriority(rhs_data);
|
||||
const bool rhs_low_priority = IsLowPriority(rhs_data);
|
||||
const bool lhs_same_or_higher_priority = sameOrHigherPriority(lhs_data);
|
||||
const bool lhs_low_priority = IsLowPriority(lhs_data);
|
||||
auto left_tie = std::tie(lhs.entry_allowed,
|
||||
lhs_same_or_higher_priority,
|
||||
rhs_low_priority,
|
||||
rhs_deviation,
|
||||
lhs_same_classification);
|
||||
auto right_tie = std::tie(rhs.entry_allowed,
|
||||
rhs_same_or_higher_priority,
|
||||
lhs_low_priority,
|
||||
lhs_deviation,
|
||||
rhs_same_classification);
|
||||
return left_tie > right_tie;
|
||||
};
|
||||
const auto RoadCompareSameName = [&](const ConnectedRoad &lhs, const ConnectedRoad &rhs) {
|
||||
const EdgeData &lhs_data = node_based_graph.GetEdgeData(lhs.eid);
|
||||
const EdgeData &rhs_data = node_based_graph.GetEdgeData(rhs.eid);
|
||||
const auto lhs_continues = IsContinueRoad(lhs_data);
|
||||
const auto rhs_continues = IsContinueRoad(rhs_data);
|
||||
const auto left_tie = std::tie(lhs.entry_allowed, lhs_continues);
|
||||
const auto right_tie = std::tie(rhs.entry_allowed, rhs_continues);
|
||||
return left_tie > right_tie || (left_tie == right_tie && RoadCompare(lhs, rhs));
|
||||
};
|
||||
|
||||
auto best_option_it = std::min_element(begin(intersection), end(intersection), RoadCompare);
|
||||
|
||||
// min element should only return end() when vector is empty
|
||||
BOOST_ASSERT(best_option_it != end(intersection));
|
||||
|
||||
best_option = std::distance(begin(intersection), best_option_it);
|
||||
best_option_deviation = angularDeviation(intersection[best_option].angle, STRAIGHT_ANGLE);
|
||||
const auto &best_option_data = node_based_graph.GetEdgeData(intersection[best_option].eid);
|
||||
|
||||
// Unless the in way is also low priority, it is generally undesirable to
|
||||
// indicate that a low priority road is obvious
|
||||
if (IsLowPriority(best_option_data) &&
|
||||
best_option_data.road_classification != in_way_data.road_classification)
|
||||
// This should never happen, guard against nevertheless
|
||||
if (node == SPECIAL_NODEID || edge == SPECIAL_EDGEID)
|
||||
{
|
||||
best_option = 0;
|
||||
best_option_deviation = 180;
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
// double check if the way with the lowest deviation from straight is still be better choice
|
||||
const auto straightest = intersection.findClosestTurn(STRAIGHT_ANGLE);
|
||||
if (straightest != best_option_it)
|
||||
auto intersection_node = node_based_graph.GetTarget(edge);
|
||||
|
||||
if (intersection.size() <= 2 || intersection.isTrafficSignalOrBarrier())
|
||||
{
|
||||
const EdgeData &straightest_data = node_based_graph.GetEdgeData(straightest->eid);
|
||||
double straightest_data_deviation = angularDeviation(straightest->angle, STRAIGHT_ANGLE);
|
||||
const auto deviation_diff =
|
||||
std::abs(best_option_deviation - straightest_data_deviation) > FUZZY_ANGLE_DIFFERENCE;
|
||||
const auto not_ramp_class = !straightest_data.road_classification.IsRampClass();
|
||||
const auto not_link_class = !straightest_data.road_classification.IsLinkClass();
|
||||
if (deviation_diff && !IsLowPriority(straightest_data) && not_ramp_class &&
|
||||
not_link_class && !IsContinueRoad(best_option_data))
|
||||
{
|
||||
best_option = std::distance(begin(intersection), straightest);
|
||||
best_option_deviation =
|
||||
angularDeviation(intersection[best_option].angle, STRAIGHT_ANGLE);
|
||||
}
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
// No non-low priority roads? Declare no obvious turn
|
||||
if (best_option == 0)
|
||||
return 0;
|
||||
|
||||
auto best_continue_it =
|
||||
std::min_element(begin(intersection), end(intersection), RoadCompareSameName);
|
||||
const auto best_continue_data = node_based_graph.GetEdgeData(best_continue_it->eid);
|
||||
if (IsContinueRoad(best_continue_data) ||
|
||||
(in_way_data.name_id == EMPTY_NAMEID && best_continue_data.name_id == EMPTY_NAMEID))
|
||||
{
|
||||
best_continue = std::distance(begin(intersection), best_continue_it);
|
||||
best_continue_deviation =
|
||||
angularDeviation(intersection[best_continue].angle, STRAIGHT_ANGLE);
|
||||
}
|
||||
|
||||
// if the best angle is going straight but the road is turning, declare no obvious turn
|
||||
if (0 != best_continue && best_option != best_continue &&
|
||||
best_option_deviation < MAXIMAL_ALLOWED_NO_TURN_DEVIATION &&
|
||||
node_based_graph.GetEdgeData(intersection[best_continue].eid).road_classification ==
|
||||
best_option_data.road_classification)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// get a count of number of ways from that intersection that qualify to have
|
||||
// continue instruction because they share a name with the approaching way
|
||||
const std::int64_t continue_count =
|
||||
count_if(++begin(intersection), end(intersection), [&](const ConnectedRoad &way) {
|
||||
return IsContinueRoad(node_based_graph.GetEdgeData(way.eid));
|
||||
});
|
||||
const std::int64_t continue_count_valid =
|
||||
count_if(++begin(intersection), end(intersection), [&](const ConnectedRoad &way) {
|
||||
return IsContinueRoad(node_based_graph.GetEdgeData(way.eid)) && way.entry_allowed;
|
||||
});
|
||||
|
||||
// checks if continue candidates are sharp turns
|
||||
const bool all_continues_are_narrow = [&]() {
|
||||
return std::count_if(
|
||||
begin(intersection), end(intersection), [&](const ConnectedRoad &road) {
|
||||
const EdgeData &road_data = node_based_graph.GetEdgeData(road.eid);
|
||||
const double &road_angle = angularDeviation(road.angle, STRAIGHT_ANGLE);
|
||||
return IsContinueRoad(road_data) && (road_angle < NARROW_TURN_ANGLE);
|
||||
}) == continue_count;
|
||||
}();
|
||||
|
||||
// return true if the best_option candidate is more promising than the best_continue candidate
|
||||
// otherwise return false, the best_continue candidate is more promising
|
||||
const auto best_over_best_continue = [&]() {
|
||||
// no continue road exists
|
||||
if (best_continue == 0)
|
||||
return true;
|
||||
|
||||
// we have multiple continues and not all are narrow. This suggests that
|
||||
// the continue candidates are ambiguous
|
||||
if (!all_continues_are_narrow && (continue_count >= 2 && intersection.size() >= 4))
|
||||
return true;
|
||||
|
||||
// if the best continue is not narrow and we also have at least 2 possible choices, the
|
||||
// intersection size does not matter anymore
|
||||
if (continue_count_valid >= 2 && best_continue_deviation >= 2 * NARROW_TURN_ANGLE)
|
||||
return true;
|
||||
|
||||
// continue data now most certainly exists
|
||||
const auto &continue_data = node_based_graph.GetEdgeData(intersection[best_continue].eid);
|
||||
|
||||
// best_continue is obvious by road class
|
||||
if (obviousByRoadClass(in_way_data.road_classification,
|
||||
continue_data.road_classification,
|
||||
best_option_data.road_classification))
|
||||
return false;
|
||||
|
||||
// best_option is obvious by road class
|
||||
if (obviousByRoadClass(in_way_data.road_classification,
|
||||
best_option_data.road_classification,
|
||||
continue_data.road_classification))
|
||||
return true;
|
||||
|
||||
// the best_option deviation is very straight and not a ramp
|
||||
if (best_option_deviation < best_continue_deviation &&
|
||||
best_option_deviation < FUZZY_ANGLE_DIFFERENCE &&
|
||||
!best_option_data.road_classification.IsRampClass())
|
||||
return true;
|
||||
|
||||
// the continue road is of a lower priority, while the road continues on the same priority
|
||||
// with a better angle
|
||||
if (best_option_deviation < best_continue_deviation &&
|
||||
in_way_data.road_classification == best_option_data.road_classification &&
|
||||
continue_data.road_classification.GetPriority() >
|
||||
best_option_data.road_classification.GetPriority())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}();
|
||||
|
||||
if (best_over_best_continue)
|
||||
{
|
||||
// Find left/right deviation
|
||||
// skipping over service roads
|
||||
const std::size_t left_index = [&]() {
|
||||
const auto index_candidate = (best_option + 1) % intersection.size();
|
||||
if (index_candidate == 0)
|
||||
return index_candidate;
|
||||
const auto &candidate_data =
|
||||
node_based_graph.GetEdgeData(intersection[index_candidate].eid);
|
||||
if (obviousByRoadClass(in_way_data.road_classification,
|
||||
best_option_data.road_classification,
|
||||
candidate_data.road_classification))
|
||||
return (index_candidate + 1) % intersection.size();
|
||||
else
|
||||
return index_candidate;
|
||||
|
||||
}();
|
||||
const auto right_index = [&]() {
|
||||
BOOST_ASSERT(best_option > 0);
|
||||
const auto index_candidate = best_option - 1;
|
||||
if (index_candidate == 0)
|
||||
return index_candidate;
|
||||
const auto candidate_data =
|
||||
node_based_graph.GetEdgeData(intersection[index_candidate].eid);
|
||||
if (obviousByRoadClass(in_way_data.road_classification,
|
||||
best_option_data.road_classification,
|
||||
candidate_data.road_classification))
|
||||
return index_candidate - 1;
|
||||
else
|
||||
return index_candidate;
|
||||
}();
|
||||
|
||||
const double left_deviation =
|
||||
angularDeviation(intersection[left_index].angle, STRAIGHT_ANGLE);
|
||||
const double right_deviation =
|
||||
angularDeviation(intersection[right_index].angle, STRAIGHT_ANGLE);
|
||||
|
||||
// return best_option candidate if it is nearly straight and distinct from the nearest other
|
||||
// out
|
||||
// way
|
||||
if (best_option_deviation < MAXIMAL_ALLOWED_NO_TURN_DEVIATION &&
|
||||
std::min(left_deviation, right_deviation) > FUZZY_ANGLE_DIFFERENCE)
|
||||
return best_option;
|
||||
|
||||
const auto &left_data = node_based_graph.GetEdgeData(intersection[left_index].eid);
|
||||
const auto &right_data = node_based_graph.GetEdgeData(intersection[right_index].eid);
|
||||
|
||||
const bool obvious_to_left =
|
||||
left_index == 0 || obviousByRoadClass(in_way_data.road_classification,
|
||||
best_option_data.road_classification,
|
||||
left_data.road_classification);
|
||||
const bool obvious_to_right =
|
||||
right_index == 0 || obviousByRoadClass(in_way_data.road_classification,
|
||||
best_option_data.road_classification,
|
||||
right_data.road_classification);
|
||||
|
||||
// if the best_option turn isn't narrow, but there is a nearly straight turn, we don't
|
||||
// consider the
|
||||
// turn obvious
|
||||
const auto check_narrow = [&intersection, best_option_deviation](const std::size_t index) {
|
||||
return angularDeviation(intersection[index].angle, STRAIGHT_ANGLE) <=
|
||||
FUZZY_ANGLE_DIFFERENCE &&
|
||||
(best_option_deviation > NARROW_TURN_ANGLE || intersection[index].entry_allowed);
|
||||
};
|
||||
|
||||
// other narrow turns?
|
||||
if (check_narrow(right_index) && !obvious_to_right)
|
||||
return 0;
|
||||
|
||||
if (check_narrow(left_index) && !obvious_to_left)
|
||||
return 0;
|
||||
|
||||
// checks if a given way in the intersection is distinct enough from the best_option
|
||||
// candidate
|
||||
const auto isDistinct = [&](const std::size_t index, const double deviation) {
|
||||
/*
|
||||
If the neighbor is not possible to enter, we allow for a lower
|
||||
distinction rate. If the road category is smaller, its also adjusted. Only
|
||||
roads of the same priority require the full distinction ratio.
|
||||
*/
|
||||
const auto &best_option_data =
|
||||
node_based_graph.GetEdgeData(intersection[best_option].eid);
|
||||
const auto adjusted_distinction_ratio = [&]() {
|
||||
// not allowed competitors are easily distinct
|
||||
if (!intersection[index].entry_allowed)
|
||||
return 0.7 * DISTINCTION_RATIO;
|
||||
// a bit less obvious are road classes
|
||||
else if (in_way_data.road_classification == best_option_data.road_classification &&
|
||||
best_option_data.road_classification.GetPriority() <
|
||||
node_based_graph.GetEdgeData(intersection[index].eid)
|
||||
.road_classification.GetPriority())
|
||||
return 0.8 * DISTINCTION_RATIO;
|
||||
// if road classes are the same, we use the full ratio
|
||||
else
|
||||
return DISTINCTION_RATIO;
|
||||
}();
|
||||
return index == 0 || deviation / best_option_deviation >= adjusted_distinction_ratio ||
|
||||
(deviation <= NARROW_TURN_ANGLE && !intersection[index].entry_allowed);
|
||||
};
|
||||
|
||||
const bool distinct_to_left = isDistinct(left_index, left_deviation);
|
||||
const bool distinct_to_right = isDistinct(right_index, right_deviation);
|
||||
// Well distinct turn that is nearly straight
|
||||
if ((distinct_to_left || obvious_to_left) && (distinct_to_right || obvious_to_right))
|
||||
return best_option;
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto &continue_data = node_based_graph.GetEdgeData(intersection[best_continue].eid);
|
||||
if (std::abs(best_continue_deviation) < 1)
|
||||
return best_continue;
|
||||
|
||||
// check if any other similar best continues exist
|
||||
std::size_t i, last = intersection.size();
|
||||
for (i = 1; i < last; ++i)
|
||||
{
|
||||
if (i == best_continue || !intersection[i].entry_allowed)
|
||||
continue;
|
||||
|
||||
const auto &turn_data = node_based_graph.GetEdgeData(intersection[i].eid);
|
||||
const bool is_obvious_by_road_class =
|
||||
obviousByRoadClass(in_way_data.road_classification,
|
||||
continue_data.road_classification,
|
||||
turn_data.road_classification);
|
||||
|
||||
// if the main road is obvious by class, we ignore the current road as a potential
|
||||
// prevention of obviousness
|
||||
if (is_obvious_by_road_class)
|
||||
continue;
|
||||
|
||||
// continuation could be grouped with a straight turn and the turning road is a ramp
|
||||
if (turn_data.road_classification.IsRampClass() &&
|
||||
best_continue_deviation < GROUP_ANGLE &&
|
||||
!continue_data.road_classification.IsRampClass())
|
||||
continue;
|
||||
|
||||
// perfectly straight turns prevent obviousness
|
||||
const auto turn_deviation = angularDeviation(intersection[i].angle, STRAIGHT_ANGLE);
|
||||
if (turn_deviation < FUZZY_ANGLE_DIFFERENCE)
|
||||
return 0;
|
||||
|
||||
const auto deviation_ratio = turn_deviation / best_continue_deviation;
|
||||
|
||||
// in comparison to normal deviations, a continue road can offer a smaller distinction
|
||||
// ratio. Other roads close to the turn angle are not as obvious, if one road continues.
|
||||
if (deviation_ratio < DISTINCTION_RATIO / 1.5)
|
||||
return 0;
|
||||
|
||||
/* in comparison to another continuing road, we need a better distinction. This prevents
|
||||
situations where the turn is probably less obvious. An example are places that have a
|
||||
road with the same name entering/exiting:
|
||||
|
||||
d
|
||||
/
|
||||
/
|
||||
a -- b
|
||||
\
|
||||
\
|
||||
c
|
||||
*/
|
||||
|
||||
const auto same_name = !util::guidance::requiresNameAnnounced(
|
||||
turn_data.name_id, continue_data.name_id, name_table, street_name_suffix_table);
|
||||
|
||||
if (same_name && deviation_ratio < 1.5 * DISTINCTION_RATIO)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Segregated intersections can result in us finding an obvious turn, even though its only
|
||||
// obvious due to a very short segment in between. So if the segment coming in is very
|
||||
// short, we check the previous intersection for other continues in the opposite bearing.
|
||||
const auto node_at_intersection = node_based_graph.GetTarget(via_edge);
|
||||
|
||||
const double constexpr MAX_COLLAPSE_DISTANCE = 30;
|
||||
const auto distance_at_u_turn = intersection[0].segment_length;
|
||||
if (distance_at_u_turn < MAX_COLLAPSE_DISTANCE)
|
||||
{
|
||||
// this request here actually goes against the direction of the ingoing edgeid. This can
|
||||
// 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
|
||||
// turn, this should be alright.
|
||||
NodeID new_node;
|
||||
const auto previous_intersection = [&]() {
|
||||
EdgeID turn_edge;
|
||||
std::tie(new_node, turn_edge) = intersection_generator.SkipDegreeTwoNodes(
|
||||
node_at_intersection, intersection[0].eid);
|
||||
return intersection_generator.GetConnectedRoads(new_node, turn_edge);
|
||||
}();
|
||||
|
||||
if (new_node != node_at_intersection)
|
||||
{
|
||||
const auto continue_road = intersection[best_continue];
|
||||
for (const auto &comparison_road : previous_intersection)
|
||||
{
|
||||
// since we look at the intersection in the wrong direction, a similar angle
|
||||
// actually represents a near 180 degree different in bearings between the two
|
||||
// roads. So if there is a road that is enterable in the opposite direction just
|
||||
// prior, a turn is not obvious
|
||||
const auto &turn_data = node_based_graph.GetEdgeData(comparison_road.eid);
|
||||
if (angularDeviation(comparison_road.angle, STRAIGHT_ANGLE) > GROUP_ANGLE &&
|
||||
angularDeviation(comparison_road.angle, continue_road.angle) <
|
||||
FUZZY_ANGLE_DIFFERENCE &&
|
||||
!turn_data.reversed && continue_data.CanCombineWith(turn_data))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return best_continue;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return boost::make_optional(
|
||||
IntersectionViewAndNode{std::move(intersection), intersection_node});
|
||||
}
|
||||
|
||||
} // namespace guidance
|
||||
|
@ -193,7 +193,7 @@ Intersection MotorwayHandler::fromMotorway(const EdgeID via_eid, Intersection in
|
||||
assignFork(via_eid, intersection[3], intersection[2], intersection[1]);
|
||||
}
|
||||
|
||||
else if (countValid(intersection) > 0) // check whether turns exist at all
|
||||
else if (intersection.countEnterable() > 0) // check whether turns exist at all
|
||||
{
|
||||
// FALLBACK, this should hopefully never be reached
|
||||
return fallback(std::move(intersection));
|
||||
@ -338,7 +338,7 @@ Intersection MotorwayHandler::fromMotorway(const EdgeID via_eid, Intersection in
|
||||
|
||||
Intersection MotorwayHandler::fromRamp(const EdgeID via_eid, Intersection intersection) const
|
||||
{
|
||||
auto num_valid_turns = countValid(intersection);
|
||||
auto num_valid_turns = intersection.countEnterable();
|
||||
// ramp straight into a motorway/ramp
|
||||
if (intersection.size() == 2 && num_valid_turns == 1)
|
||||
{
|
||||
|
@ -1,5 +1,4 @@
|
||||
#include "extractor/guidance/node_based_graph_walker.hpp"
|
||||
#include "util/coordinate_calculation.hpp"
|
||||
#include <utility>
|
||||
|
||||
using osrm::util::angularDeviation;
|
||||
|
@ -4,11 +4,14 @@
|
||||
#include "util/coordinate_calculation.hpp"
|
||||
#include "util/guidance/name_announcements.hpp"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
using EdgeData = osrm::util::NodeBasedDynamicGraph::EdgeData;
|
||||
using osrm::extractor::guidance::getTurnDirection;
|
||||
using osrm::util::angularDeviation;
|
||||
|
||||
@ -32,7 +35,8 @@ SliproadHandler::SliproadHandler(const IntersectionGenerator &intersection_gener
|
||||
{
|
||||
}
|
||||
|
||||
// included for interface reasons only
|
||||
// The intersection has to connect a Sliproad, see the example scenario below:
|
||||
// Intersection at `d`: Sliproad `bd` connecting `cd` and the road starting at `d`.
|
||||
bool SliproadHandler::canProcess(const NodeID /*nid*/,
|
||||
const EdgeID /*via_eid*/,
|
||||
const Intersection &intersection) const
|
||||
@ -40,261 +44,699 @@ bool SliproadHandler::canProcess(const NodeID /*nid*/,
|
||||
return intersection.size() > 2;
|
||||
}
|
||||
|
||||
// Detect sliproad b-d in the following example:
|
||||
//
|
||||
// .
|
||||
// e
|
||||
// .
|
||||
// .
|
||||
// a ... b .... c .
|
||||
// ` .
|
||||
// ` .
|
||||
// ` .
|
||||
// d
|
||||
// .
|
||||
//
|
||||
// ^ a nid
|
||||
// ^ ab source_edge_id
|
||||
// ^ b intersection
|
||||
Intersection SliproadHandler::
|
||||
operator()(const NodeID, const EdgeID source_edge_id, Intersection intersection) const
|
||||
operator()(const NodeID /*nid*/, const EdgeID source_edge_id, Intersection intersection) const
|
||||
{
|
||||
BOOST_ASSERT(intersection.size() > 2);
|
||||
|
||||
// Potential splitting / start of a Sliproad (b)
|
||||
auto intersection_node_id = node_based_graph.GetTarget(source_edge_id);
|
||||
|
||||
// if there is no turn, there is no sliproad
|
||||
if (intersection.size() <= 2)
|
||||
return intersection;
|
||||
// Road index prefering non-sliproads (bc)
|
||||
auto obvious = getObviousIndexWithSliproads(source_edge_id, intersection, intersection_node_id);
|
||||
|
||||
const auto findNextIntersectionForRoad =
|
||||
[&](const NodeID at_node, const ConnectedRoad &road, NodeID &output_node) {
|
||||
auto intersection = intersection_generator(at_node, road.eid);
|
||||
auto in_edge = road.eid;
|
||||
// skip over traffic lights
|
||||
// to prevent ending up in an endless loop, we remember all visited nodes. This is
|
||||
// necessary, since merging of roads can actually create enterable loops of degree two
|
||||
std::unordered_set<NodeID> visited_nodes;
|
||||
auto node = at_node;
|
||||
while (intersection.size() == 2 && visited_nodes.count(node) == 0)
|
||||
{
|
||||
visited_nodes.insert(node);
|
||||
node = node_based_graph.GetTarget(in_edge);
|
||||
if (node == at_node)
|
||||
{
|
||||
// we ended up in a loop without exit
|
||||
output_node = SPECIAL_NODEID;
|
||||
intersection.clear();
|
||||
return intersection;
|
||||
}
|
||||
in_edge = intersection[1].eid;
|
||||
output_node = node_based_graph.GetTarget(in_edge);
|
||||
intersection = intersection_generator(node, in_edge);
|
||||
}
|
||||
if (intersection.size() <= 2)
|
||||
{
|
||||
output_node = SPECIAL_NODEID;
|
||||
intersection.clear();
|
||||
}
|
||||
return intersection;
|
||||
};
|
||||
|
||||
const std::size_t obvious_turn_index = [&]() -> std::size_t {
|
||||
const auto index = findObviousTurn(source_edge_id, intersection);
|
||||
if (index != 0)
|
||||
return index;
|
||||
else if (intersection.size() == 3 && intersection[1].instruction.type == TurnType::Fork)
|
||||
{
|
||||
// Forks themselves do not contain a `obvious` turn index. If we look at a fork that has
|
||||
// a one-sided sliproad, however, the non-sliproad can be considered `obvious`. Here we
|
||||
// assume that this could be the case and check for a potential sliproad/non-sliproad
|
||||
// situation.
|
||||
NodeID intersection_node_one = SPECIAL_NODEID, intersection_node_two = SPECIAL_NODEID;
|
||||
const auto intersection_following_index_one = findNextIntersectionForRoad(
|
||||
intersection_node_id, intersection[1], intersection_node_one);
|
||||
const auto intersection_following_index_two = findNextIntersectionForRoad(
|
||||
intersection_node_id, intersection[2], intersection_node_two);
|
||||
// in case of broken roads, we return
|
||||
if (intersection_following_index_one.empty() ||
|
||||
intersection_following_index_two.empty())
|
||||
return 0;
|
||||
|
||||
// In case of loops at the end of the road, we will arrive back at the intersection
|
||||
// itself. If that is the case, the road is obviously not a sliproad.
|
||||
|
||||
// a sliproad has to enter a road without choice
|
||||
const auto couldBeSliproad = [](const IntersectionView &intersection) {
|
||||
if (intersection.size() != 3)
|
||||
return false;
|
||||
if ((intersection[1].entry_allowed && intersection[2].entry_allowed) ||
|
||||
intersection[0].entry_allowed)
|
||||
return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
if (couldBeSliproad(intersection_following_index_one) &&
|
||||
intersection_node_id != intersection_node_two)
|
||||
return 2;
|
||||
else if (couldBeSliproad(intersection_following_index_two) &&
|
||||
intersection_node_id != intersection_node_one)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}();
|
||||
|
||||
if (obvious_turn_index == 0)
|
||||
return intersection;
|
||||
|
||||
const auto &next_road = intersection[obvious_turn_index];
|
||||
|
||||
const auto linkTest = [this, next_road](const ConnectedRoad &road) {
|
||||
return !node_based_graph.GetEdgeData(road.eid).roundabout && road.entry_allowed &&
|
||||
angularDeviation(road.angle, STRAIGHT_ANGLE) <= 2 * NARROW_TURN_ANGLE &&
|
||||
!hasRoundaboutType(road.instruction) &&
|
||||
angularDeviation(next_road.angle, road.angle) >
|
||||
std::numeric_limits<double>::epsilon();
|
||||
};
|
||||
|
||||
bool hasNarrow =
|
||||
std::find_if(intersection.begin(), intersection.end(), linkTest) != intersection.end();
|
||||
if (!hasNarrow)
|
||||
return intersection;
|
||||
|
||||
const auto source_edge_data = node_based_graph.GetEdgeData(source_edge_id);
|
||||
// check whether the continue road is valid
|
||||
const auto check_valid = [this, source_edge_data](const ConnectedRoad &road) {
|
||||
const auto road_edge_data = node_based_graph.GetEdgeData(road.eid);
|
||||
// Test to see if the source edge and the one we're looking at are the same road
|
||||
|
||||
const auto same_name = !util::guidance::requiresNameAnnounced(
|
||||
road_edge_data.name_id, source_edge_data.name_id, name_table, street_name_suffix_table);
|
||||
|
||||
return road_edge_data.road_classification == source_edge_data.road_classification &&
|
||||
road_edge_data.name_id != EMPTY_NAMEID && source_edge_data.name_id != EMPTY_NAMEID &&
|
||||
same_name && road.entry_allowed;
|
||||
};
|
||||
|
||||
if (!check_valid(next_road))
|
||||
return intersection;
|
||||
|
||||
// Threshold check, if the intersection is too far away, don't bother continuing
|
||||
const auto coordinate_extractor = intersection_generator.GetCoordinateExtractor();
|
||||
const auto next_road_length = util::coordinate_calculation::getLength(
|
||||
coordinate_extractor.GetForwardCoordinatesAlongRoad(
|
||||
node_based_graph.GetTarget(source_edge_id), next_road.eid),
|
||||
&util::coordinate_calculation::haversineDistance);
|
||||
if (next_road_length > MAX_SLIPROAD_THRESHOLD)
|
||||
if (!obvious)
|
||||
{
|
||||
return intersection;
|
||||
}
|
||||
auto next_intersection_node = node_based_graph.GetTarget(next_road.eid);
|
||||
|
||||
const auto next_road_next_intersection =
|
||||
findNextIntersectionForRoad(intersection_node_id, next_road, next_intersection_node);
|
||||
// Potential non-sliproad road (bc), leading to the intersection (c) the Sliproad (bd) shortcuts
|
||||
const auto &main_road = intersection[*obvious];
|
||||
|
||||
if (next_road_next_intersection.empty())
|
||||
// The road leading to the intersection (bc) has to continue from our source
|
||||
if (!roadContinues(source_edge_id, main_road.eid))
|
||||
{
|
||||
return intersection;
|
||||
}
|
||||
|
||||
// Link-check for (bc) and later on (cd) which both are getting shortcutted by Sliproad
|
||||
const auto is_potential_link = [this, main_road](const ConnectedRoad &road) {
|
||||
if (!road.entry_allowed)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prevent from starting in or going onto a roundabout
|
||||
auto onto_roundabout = hasRoundaboutType(road.instruction);
|
||||
|
||||
if (onto_roundabout)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Narrow turn angle for road (bd) and guard against data issues (overlapping roads)
|
||||
auto is_narrow = angularDeviation(road.angle, STRAIGHT_ANGLE) <= 2 * NARROW_TURN_ANGLE;
|
||||
auto same_angle = angularDeviation(main_road.angle, road.angle) //
|
||||
<= std::numeric_limits<double>::epsilon();
|
||||
|
||||
if (!is_narrow || same_angle)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto &road_data = node_based_graph.GetEdgeData(road.eid);
|
||||
|
||||
auto is_roundabout = road_data.roundabout;
|
||||
|
||||
if (is_roundabout)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
if (!std::any_of(begin(intersection), end(intersection), is_potential_link))
|
||||
{
|
||||
return intersection;
|
||||
}
|
||||
|
||||
// If the intersection is too far away, don't bother continuing
|
||||
if (nextIntersectionIsTooFarAway(intersection_node_id, main_road.eid))
|
||||
{
|
||||
return intersection;
|
||||
}
|
||||
|
||||
// Try to find the intersection at (c) which the Sliproad shortcuts
|
||||
const auto main_road_intersection = getNextIntersection(intersection_node_id, main_road.eid);
|
||||
|
||||
if (!main_road_intersection)
|
||||
{
|
||||
return intersection;
|
||||
}
|
||||
|
||||
if (main_road_intersection->intersection.isDeadEnd())
|
||||
{
|
||||
return intersection;
|
||||
}
|
||||
|
||||
// If we are at a traffic loop at the end of a road, don't consider it a sliproad
|
||||
if (intersection_node_id == next_intersection_node)
|
||||
return intersection;
|
||||
|
||||
std::unordered_set<NameID> target_road_names;
|
||||
|
||||
for (const auto &road : next_road_next_intersection)
|
||||
if (intersection_node_id == main_road_intersection->node)
|
||||
{
|
||||
const auto &target_data = node_based_graph.GetEdgeData(road.eid);
|
||||
target_road_names.insert(target_data.name_id);
|
||||
return intersection;
|
||||
}
|
||||
|
||||
for (auto &road : intersection)
|
||||
std::vector<NameID> target_road_name_ids;
|
||||
target_road_name_ids.reserve(main_road_intersection->intersection.size());
|
||||
|
||||
for (const auto &road : main_road_intersection->intersection)
|
||||
{
|
||||
if (linkTest(road))
|
||||
const auto &target_data = node_based_graph.GetEdgeData(road.eid);
|
||||
target_road_name_ids.push_back(target_data.name_id);
|
||||
}
|
||||
|
||||
auto sliproad_found = false;
|
||||
|
||||
// For all roads at the main intersection except the UTurn road: check Sliproad scenarios.
|
||||
for (std::size_t road_index = 1, last = intersection.size(); road_index < last; ++road_index)
|
||||
{
|
||||
const auto index_left_of_main_road = (*obvious - 1) % intersection.size();
|
||||
const auto index_right_of_main_road = (*obvious + 1) % intersection.size();
|
||||
|
||||
// Be strict and require the Sliproad to be either left or right of the main road.
|
||||
if (road_index != index_left_of_main_road && road_index != index_right_of_main_road)
|
||||
continue;
|
||||
|
||||
auto &sliproad = intersection[road_index]; // this is what we're checking and assigning to
|
||||
const auto &sliproad_data = node_based_graph.GetEdgeData(sliproad.eid);
|
||||
|
||||
// Intersection is orderd: 0 is UTurn, then from sharp right to sharp left.
|
||||
// We already have an obvious index (bc) for going straight-ish.
|
||||
const auto is_right_sliproad_turn = road_index < *obvious;
|
||||
const auto is_left_sliproad_turn = road_index > *obvious;
|
||||
|
||||
// Road at the intersection the main road leads onto which the sliproad arrives onto
|
||||
const auto crossing_road = [&] {
|
||||
if (is_left_sliproad_turn)
|
||||
return main_road_intersection->intersection.getLeftmostRoad();
|
||||
|
||||
if (is_right_sliproad_turn)
|
||||
return main_road_intersection->intersection.getRightmostRoad();
|
||||
|
||||
BOOST_ASSERT_MSG(false, "Sliproad is neither a left nor right of obvious main road");
|
||||
return main_road_intersection->intersection.getLeftmostRoad();
|
||||
}();
|
||||
|
||||
const auto &crossing_road_data = node_based_graph.GetEdgeData(crossing_road.eid);
|
||||
|
||||
// The crossing road at the main road's intersection must not be incoming-only
|
||||
if (crossing_road_data.reversed)
|
||||
{
|
||||
EdgeID candidate_in = road.eid;
|
||||
const auto target_intersection = [&](NodeID node) {
|
||||
auto intersection = intersection_generator(node, candidate_in);
|
||||
// skip over traffic lights
|
||||
if (intersection.size() == 2)
|
||||
{
|
||||
node = node_based_graph.GetTarget(candidate_in);
|
||||
candidate_in = intersection[1].eid;
|
||||
intersection = intersection_generator(node, candidate_in);
|
||||
}
|
||||
return intersection;
|
||||
}(intersection_node_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto link_data = node_based_graph.GetEdgeData(road.eid);
|
||||
// Check if the road continues here
|
||||
const bool is_through_street =
|
||||
!target_intersection.empty() && link_data.name_id != EMPTY_NAMEID &&
|
||||
target_intersection.end() !=
|
||||
std::find_if(
|
||||
target_intersection.begin() + 1,
|
||||
target_intersection.end(),
|
||||
[this, &link_data](const IntersectionViewData &road) {
|
||||
const auto &road_edge_data = node_based_graph.GetEdgeData(road.eid);
|
||||
// Discard service and other low priority roads - never Sliproad candidate
|
||||
if (sliproad_data.road_classification.IsLowPriorityRoadClass())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto same_name =
|
||||
road_edge_data.name_id != EMPTY_NAMEID &&
|
||||
!util::guidance::requiresNameAnnounced(road_edge_data.name_id,
|
||||
link_data.name_id,
|
||||
name_table,
|
||||
street_name_suffix_table);
|
||||
// Incoming-only can never be a Sliproad
|
||||
if (sliproad_data.reversed)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return same_name;
|
||||
});
|
||||
// This is what we know so far:
|
||||
//
|
||||
// .
|
||||
// e
|
||||
// .
|
||||
// .
|
||||
// a ... b .... c . < `main_road_intersection` is intersection at `c`
|
||||
// ` .
|
||||
// ` .
|
||||
// ` .
|
||||
// d < `target_intersection` is intersection at `d`
|
||||
// . `sliproad_edge_target` is node `d`
|
||||
// e
|
||||
//
|
||||
//
|
||||
// ^ `sliproad` is `bd`
|
||||
// ^ `intersection` is intersection at `b`
|
||||
|
||||
// if the sliproad candidate is a through street, we cannot handle it as a sliproad
|
||||
if (is_through_street)
|
||||
continue;
|
||||
if (!is_potential_link(sliproad))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const auto &candidate_road : target_intersection)
|
||||
// The Sliproad graph edge - in the following we use the graph walker to
|
||||
// adjust this edge forward jumping over artificial intersections.
|
||||
auto sliproad_edge = sliproad.eid;
|
||||
|
||||
// Starting out at the intersection and going onto the Sliproad we skip artificial
|
||||
// degree two intersections and limit the max hop count in doing so.
|
||||
IntersectionFinderAccumulator intersection_finder{10, intersection_generator};
|
||||
const SkipTrafficSignalBarrierRoadSelector road_selector{};
|
||||
(void)graph_walker.TraverseRoad(intersection_node_id, // start node
|
||||
sliproad_edge, // onto edge
|
||||
intersection_finder, // accumulator
|
||||
road_selector); // selector
|
||||
|
||||
sliproad_edge = intersection_finder.via_edge_id;
|
||||
const auto target_intersection = intersection_finder.intersection;
|
||||
|
||||
// Constrain the Sliproad's target to sliproad, outgoing, incoming from main intersection
|
||||
if (target_intersection.size() != 3)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const NodeID sliproad_edge_target = node_based_graph.GetTarget(sliproad_edge);
|
||||
|
||||
// Distinct triangle nodes `bcd`
|
||||
if (intersection_node_id == main_road_intersection->node ||
|
||||
intersection_node_id == sliproad_edge_target ||
|
||||
main_road_intersection->node == sliproad_edge_target)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the sliproad candidate is a through street, we cannot handle it as a sliproad.
|
||||
if (isThroughStreet(sliproad_edge, target_intersection))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// The turn off of the Sliproad has to be obvious and a narrow turn
|
||||
{
|
||||
const auto index = findObviousTurn(sliproad_edge, target_intersection);
|
||||
|
||||
if (index == 0)
|
||||
{
|
||||
const auto &candidate_data = node_based_graph.GetEdgeData(candidate_road.eid);
|
||||
if (target_road_names.count(candidate_data.name_id) > 0)
|
||||
{
|
||||
if (node_based_graph.GetTarget(candidate_road.eid) == next_intersection_node)
|
||||
{
|
||||
road.instruction.type = TurnType::Sliproad;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto skip_traffic_light_intersection = intersection_generator(
|
||||
node_based_graph.GetTarget(candidate_in), candidate_road.eid);
|
||||
if (skip_traffic_light_intersection.size() == 2 &&
|
||||
node_based_graph.GetTarget(skip_traffic_light_intersection[1].eid) ==
|
||||
next_intersection_node)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
road.instruction.type = TurnType::Sliproad;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const auto onto = target_intersection[index];
|
||||
const auto angle_deviation = angularDeviation(onto.angle, STRAIGHT_ANGLE);
|
||||
const auto is_narrow_turn = angle_deviation <= 2 * NARROW_TURN_ANGLE;
|
||||
|
||||
if (!is_narrow_turn)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for curvature. Depending on the turn's direction at `c`. Scenario for right turn:
|
||||
//
|
||||
// a ... b .... c . a ... b .... c . a ... b .... c .
|
||||
// ` . ` . . . .
|
||||
// ` . . . . .
|
||||
// ` . .. . .
|
||||
// d d . d
|
||||
//
|
||||
// Sliproad Not a Sliproad
|
||||
{
|
||||
const auto &extractor = intersection_generator.GetCoordinateExtractor();
|
||||
|
||||
const NodeID start = intersection_node_id; // b
|
||||
const EdgeID edge = sliproad_edge; // bd
|
||||
|
||||
const auto coords = extractor.GetForwardCoordinatesAlongRoad(start, edge);
|
||||
BOOST_ASSERT(coords.size() >= 2);
|
||||
|
||||
// Now keep start and end coordinate fix and check for curvature
|
||||
const auto start_coord = coords.front();
|
||||
const auto end_coord = coords.back();
|
||||
|
||||
const auto first = std::begin(coords) + 1;
|
||||
const auto last = std::end(coords) - 1;
|
||||
|
||||
auto snuggles = false;
|
||||
|
||||
using namespace util::coordinate_calculation;
|
||||
|
||||
// In addition, if it's a right/left turn we expect the rightmost/leftmost
|
||||
// turn at `c` to be more or less ~90 degree for a Sliproad scenario.
|
||||
double deviation_from_straight = 0;
|
||||
|
||||
if (is_right_sliproad_turn)
|
||||
{
|
||||
snuggles = std::all_of(first, last, [=](auto each) { //
|
||||
return !isCCW(start_coord, each, end_coord);
|
||||
});
|
||||
|
||||
const auto rightmost = main_road_intersection->intersection.getRightmostRoad();
|
||||
deviation_from_straight = angularDeviation(rightmost.angle, STRAIGHT_ANGLE);
|
||||
}
|
||||
else if (is_left_sliproad_turn)
|
||||
{
|
||||
snuggles = std::all_of(first, last, [=](auto each) { //
|
||||
return isCCW(start_coord, each, end_coord);
|
||||
});
|
||||
|
||||
const auto leftmost = main_road_intersection->intersection.getLeftmostRoad();
|
||||
deviation_from_straight = angularDeviation(leftmost.angle, STRAIGHT_ANGLE);
|
||||
}
|
||||
|
||||
// The data modelling for small Sliproads is not reliable enough.
|
||||
// Only check for curvature and ~90 degree when it makes sense to do so.
|
||||
const constexpr auto MIN_LENGTH = 3.;
|
||||
|
||||
const auto length = haversineDistance(node_info_list[intersection_node_id],
|
||||
node_info_list[main_road_intersection->node]);
|
||||
|
||||
const double perpendicular_angle = 90 + FUZZY_ANGLE_DIFFERENCE;
|
||||
|
||||
if (length >= MIN_LENGTH)
|
||||
{
|
||||
if (!snuggles)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (deviation_from_straight > perpendicular_angle)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for area under triangle `bdc`.
|
||||
//
|
||||
// a ... b .... c .
|
||||
// ` .
|
||||
// ` .
|
||||
// ` .
|
||||
// d
|
||||
//
|
||||
const auto area_threshold = std::pow(
|
||||
scaledThresholdByRoadClass(MAX_SLIPROAD_THRESHOLD, sliproad_data.road_classification),
|
||||
2.);
|
||||
|
||||
if (!isValidSliproadArea(area_threshold,
|
||||
intersection_node_id,
|
||||
main_road_intersection->node,
|
||||
sliproad_edge_target))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check all roads at `d` if one is connected to `c`, is so `bd` is Sliproad.
|
||||
for (const auto &candidate_road : target_intersection)
|
||||
{
|
||||
const auto &candidate_data = node_based_graph.GetEdgeData(candidate_road.eid);
|
||||
|
||||
// Name mismatch: check roads at `c` and `d` for same name
|
||||
const auto name_mismatch = [&](const NameID road_name_id) {
|
||||
const auto unnamed = road_name_id == EMPTY_NAMEID;
|
||||
|
||||
return unnamed ||
|
||||
util::guidance::requiresNameAnnounced(road_name_id, //
|
||||
candidate_data.name_id, //
|
||||
name_table, //
|
||||
street_name_suffix_table); //
|
||||
|
||||
};
|
||||
|
||||
const auto candidate_road_name_mismatch = std::all_of(begin(target_road_name_ids), //
|
||||
end(target_road_name_ids), //
|
||||
name_mismatch); //
|
||||
|
||||
if (candidate_road_name_mismatch)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the Sliproad `bd` is a link, `bc` and `cd` must not be links.
|
||||
if (!isValidSliproadLink(sliproad, main_road, candidate_road))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (node_based_graph.GetTarget(candidate_road.eid) == main_road_intersection->node)
|
||||
{
|
||||
sliproad.instruction.type = TurnType::Sliproad;
|
||||
sliproad_found = true;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto skip_traffic_light_intersection = intersection_generator(
|
||||
node_based_graph.GetTarget(sliproad_edge), candidate_road.eid);
|
||||
if (skip_traffic_light_intersection.isTrafficSignalOrBarrier() &&
|
||||
node_based_graph.GetTarget(skip_traffic_light_intersection[1].eid) ==
|
||||
main_road_intersection->node)
|
||||
{
|
||||
|
||||
sliproad.instruction.type = TurnType::Sliproad;
|
||||
sliproad_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (next_road.instruction.type == TurnType::Fork)
|
||||
// Now in case we found a Sliproad and assigned the corresponding type to the road,
|
||||
// it could be that the intersection from which the Sliproad splits off was a Fork before.
|
||||
// In those cases the obvious non-Sliproad is now obvious and we discard the Fork turn type.
|
||||
if (sliproad_found && main_road.instruction.type == TurnType::Fork)
|
||||
{
|
||||
const auto &next_data = node_based_graph.GetEdgeData(next_road.eid);
|
||||
const auto &source_edge_data = node_based_graph.GetEdgeData(source_edge_id);
|
||||
const auto &main_road_data = node_based_graph.GetEdgeData(main_road.eid);
|
||||
|
||||
const auto same_name =
|
||||
next_data.name_id != EMPTY_NAMEID && source_edge_data.name_id != EMPTY_NAMEID &&
|
||||
!util::guidance::requiresNameAnnounced(
|
||||
next_data.name_id, source_edge_data.name_id, name_table, street_name_suffix_table);
|
||||
const auto same_name = source_edge_data.name_id != EMPTY_NAMEID && //
|
||||
main_road_data.name_id != EMPTY_NAMEID && //
|
||||
!util::guidance::requiresNameAnnounced(source_edge_data.name_id,
|
||||
main_road_data.name_id,
|
||||
name_table,
|
||||
street_name_suffix_table); //
|
||||
|
||||
if (same_name)
|
||||
{
|
||||
if (angularDeviation(next_road.angle, STRAIGHT_ANGLE) < 5)
|
||||
intersection[obvious_turn_index].instruction.type = TurnType::Suppressed;
|
||||
if (angularDeviation(main_road.angle, STRAIGHT_ANGLE) < 5)
|
||||
intersection[*obvious].instruction.type = TurnType::Suppressed;
|
||||
else
|
||||
intersection[obvious_turn_index].instruction.type = TurnType::Continue;
|
||||
intersection[obvious_turn_index].instruction.direction_modifier =
|
||||
getTurnDirection(intersection[obvious_turn_index].angle);
|
||||
intersection[*obvious].instruction.type = TurnType::Continue;
|
||||
intersection[*obvious].instruction.direction_modifier =
|
||||
getTurnDirection(intersection[*obvious].angle);
|
||||
}
|
||||
else if (next_data.name_id != EMPTY_NAMEID)
|
||||
else if (main_road_data.name_id != EMPTY_NAMEID)
|
||||
{
|
||||
intersection[obvious_turn_index].instruction.type = TurnType::NewName;
|
||||
intersection[obvious_turn_index].instruction.direction_modifier =
|
||||
getTurnDirection(intersection[obvious_turn_index].angle);
|
||||
intersection[*obvious].instruction.type = TurnType::NewName;
|
||||
intersection[*obvious].instruction.direction_modifier =
|
||||
getTurnDirection(intersection[*obvious].angle);
|
||||
}
|
||||
else
|
||||
{
|
||||
intersection[obvious_turn_index].instruction.type = TurnType::Suppressed;
|
||||
intersection[*obvious].instruction.type = TurnType::Suppressed;
|
||||
}
|
||||
}
|
||||
|
||||
return intersection;
|
||||
}
|
||||
|
||||
// Implementation details
|
||||
|
||||
boost::optional<std::size_t> SliproadHandler::getObviousIndexWithSliproads(
|
||||
const EdgeID from, const Intersection &intersection, const NodeID at) const
|
||||
{
|
||||
BOOST_ASSERT(from != SPECIAL_EDGEID);
|
||||
BOOST_ASSERT(at != SPECIAL_NODEID);
|
||||
|
||||
// If a turn is obvious without taking Sliproads into account use this
|
||||
const auto index = findObviousTurn(from, intersection);
|
||||
|
||||
if (index != 0)
|
||||
{
|
||||
return boost::make_optional(index);
|
||||
}
|
||||
|
||||
// Otherwise check if the road is forking into two and one of them is a Sliproad;
|
||||
// then the non-Sliproad is the obvious one.
|
||||
if (intersection.size() != 3)
|
||||
{
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
const auto forking = intersection[1].instruction.type == TurnType::Fork &&
|
||||
intersection[2].instruction.type == TurnType::Fork;
|
||||
|
||||
if (!forking)
|
||||
{
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
const auto first = getNextIntersection(at, intersection.getRightmostRoad().eid);
|
||||
const auto second = getNextIntersection(at, intersection.getLeftmostRoad().eid);
|
||||
|
||||
if (!first || !second)
|
||||
{
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
if (first->intersection.isDeadEnd() || second->intersection.isDeadEnd())
|
||||
{
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
// In case of loops at the end of the road, we will arrive back at the intersection
|
||||
// itself. If that is the case, the road is obviously not a sliproad.
|
||||
if (canBeTargetOfSliproad(first->intersection) && at != second->node)
|
||||
{
|
||||
return boost::make_optional(std::size_t{2});
|
||||
}
|
||||
|
||||
if (canBeTargetOfSliproad(second->intersection) && at != first->node)
|
||||
{
|
||||
return boost::make_optional(std::size_t{1});
|
||||
}
|
||||
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
bool SliproadHandler::nextIntersectionIsTooFarAway(const NodeID start, const EdgeID onto) const
|
||||
{
|
||||
BOOST_ASSERT(start != SPECIAL_NODEID);
|
||||
BOOST_ASSERT(onto != SPECIAL_EDGEID);
|
||||
|
||||
const auto &extractor = intersection_generator.GetCoordinateExtractor();
|
||||
|
||||
// Base max distance threshold on the current road class we're on
|
||||
const auto &data = node_based_graph.GetEdgeData(onto);
|
||||
const auto threshold = scaledThresholdByRoadClass(MAX_SLIPROAD_THRESHOLD, // <- scales down
|
||||
data.road_classification);
|
||||
|
||||
DistanceToNextIntersectionAccumulator accumulator{extractor, node_based_graph, threshold};
|
||||
const SkipTrafficSignalBarrierRoadSelector selector{};
|
||||
|
||||
(void)graph_walker.TraverseRoad(start, onto, accumulator, selector);
|
||||
|
||||
return accumulator.too_far_away;
|
||||
}
|
||||
|
||||
bool SliproadHandler::isThroughStreet(const EdgeID from, const IntersectionView &intersection) const
|
||||
{
|
||||
BOOST_ASSERT(from != SPECIAL_EDGEID);
|
||||
BOOST_ASSERT(!intersection.empty());
|
||||
|
||||
const auto &edge_name_id = node_based_graph.GetEdgeData(from).name_id;
|
||||
|
||||
auto first = begin(intersection) + 1; // Skip UTurn road
|
||||
auto last = end(intersection);
|
||||
|
||||
auto same_name = [&](const auto &road) {
|
||||
const auto &road_name_id = node_based_graph.GetEdgeData(road.eid).name_id;
|
||||
|
||||
return edge_name_id != EMPTY_NAMEID && //
|
||||
road_name_id != EMPTY_NAMEID && //
|
||||
!util::guidance::requiresNameAnnounced(edge_name_id,
|
||||
road_name_id,
|
||||
name_table,
|
||||
street_name_suffix_table); //
|
||||
};
|
||||
|
||||
return std::find_if(first, last, same_name) != last;
|
||||
}
|
||||
|
||||
bool SliproadHandler::roadContinues(const EdgeID current, const EdgeID next) const
|
||||
{
|
||||
const auto ¤t_data = node_based_graph.GetEdgeData(current);
|
||||
const auto &next_data = node_based_graph.GetEdgeData(next);
|
||||
|
||||
auto same_road_category = current_data.road_classification == next_data.road_classification;
|
||||
auto same_travel_mode = current_data.travel_mode == next_data.travel_mode;
|
||||
|
||||
auto same_name = current_data.name_id != EMPTY_NAMEID && //
|
||||
next_data.name_id != EMPTY_NAMEID && //
|
||||
!util::guidance::requiresNameAnnounced(current_data.name_id,
|
||||
next_data.name_id,
|
||||
name_table,
|
||||
street_name_suffix_table); //
|
||||
|
||||
const auto continues = same_road_category && same_travel_mode && same_name;
|
||||
return continues;
|
||||
}
|
||||
|
||||
bool SliproadHandler::isValidSliproadArea(const double max_area,
|
||||
const NodeID a,
|
||||
const NodeID b,
|
||||
const NodeID c) const
|
||||
|
||||
{
|
||||
using namespace util::coordinate_calculation;
|
||||
|
||||
const auto first = node_info_list[a];
|
||||
const auto second = node_info_list[b];
|
||||
const auto third = node_info_list[c];
|
||||
|
||||
const auto length = haversineDistance(first, second);
|
||||
const auto heigth = haversineDistance(second, third);
|
||||
|
||||
const auto area = (length * heigth) / 2.;
|
||||
|
||||
// Everything below is data issue - there are some weird situations where
|
||||
// nodes are really close to each other and / or tagging ist just plain off.
|
||||
const constexpr auto MIN_SLIPROAD_AREA = 3.;
|
||||
|
||||
if (area < MIN_SLIPROAD_AREA || area > max_area)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SliproadHandler::isValidSliproadLink(const IntersectionViewData &sliproad,
|
||||
const IntersectionViewData &first,
|
||||
const IntersectionViewData &second) const
|
||||
{
|
||||
// If the Sliproad is not a link we don't care
|
||||
const auto &sliproad_data = node_based_graph.GetEdgeData(sliproad.eid);
|
||||
if (!sliproad_data.road_classification.IsLinkClass())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// otherwise neither the first road leading to the intersection we shortcut
|
||||
const auto &first_road_data = node_based_graph.GetEdgeData(first.eid);
|
||||
if (first_road_data.road_classification.IsLinkClass())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// nor the second road coming from the intersection we shotcut must be links
|
||||
const auto &second_road_data = node_based_graph.GetEdgeData(second.eid);
|
||||
if (second_road_data.road_classification.IsLinkClass())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SliproadHandler::canBeTargetOfSliproad(const IntersectionView &intersection)
|
||||
{
|
||||
// Example to handle:
|
||||
// .
|
||||
// a . . b .
|
||||
// ` .
|
||||
// ` .
|
||||
// c < intersection
|
||||
// .
|
||||
//
|
||||
|
||||
// One outgoing two incoming
|
||||
if (intersection.size() != 3)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto backwards = intersection[0].entry_allowed;
|
||||
const auto multiple_allowed = intersection[1].entry_allowed && intersection[2].entry_allowed;
|
||||
|
||||
if (backwards || multiple_allowed)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
double SliproadHandler::scaledThresholdByRoadClass(const double max_threshold,
|
||||
const RoadClassification &classification)
|
||||
{
|
||||
double factor = 1.0;
|
||||
|
||||
switch (classification.GetPriority())
|
||||
{
|
||||
case RoadPriorityClass::MOTORWAY:
|
||||
factor = 1.0;
|
||||
break;
|
||||
case RoadPriorityClass::TRUNK:
|
||||
factor = 0.8;
|
||||
break;
|
||||
case RoadPriorityClass::PRIMARY:
|
||||
factor = 0.8;
|
||||
break;
|
||||
case RoadPriorityClass::SECONDARY:
|
||||
factor = 0.6;
|
||||
break;
|
||||
case RoadPriorityClass::TERTIARY:
|
||||
factor = 0.5;
|
||||
break;
|
||||
case RoadPriorityClass::MAIN_RESIDENTIAL:
|
||||
factor = 0.4;
|
||||
break;
|
||||
case RoadPriorityClass::SIDE_RESIDENTIAL:
|
||||
factor = 0.3;
|
||||
break;
|
||||
case RoadPriorityClass::LINK_ROAD:
|
||||
factor = 0.3;
|
||||
break;
|
||||
case RoadPriorityClass::CONNECTIVITY:
|
||||
factor = 0.1;
|
||||
break;
|
||||
|
||||
// What
|
||||
case RoadPriorityClass::BIKE_PATH:
|
||||
case RoadPriorityClass::FOOT_PATH:
|
||||
default:
|
||||
factor = 0.1;
|
||||
}
|
||||
|
||||
const auto scaled = max_threshold * factor;
|
||||
|
||||
BOOST_ASSERT(scaled <= max_threshold);
|
||||
return scaled;
|
||||
}
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
|
Loading…
Reference in New Issue
Block a user