From d166fc6a75503bbdc77f827b28967150e7dbebe9 Mon Sep 17 00:00:00 2001 From: Michael Krasnyk Date: Tue, 6 Feb 2018 13:36:45 +0100 Subject: [PATCH] Restructure Obvious Turn Handling, code transfer from #4426 --- .../guidance/obvious-turn-discovery.feature | 1212 +++++++++++++++++ features/guidance/turn-lanes.feature | 5 +- features/guidance/turn.feature | 46 +- .../options/extract/turn_function.feature | 6 +- include/extractor/road_classification.hpp | 70 + include/guidance/intersection_handler.hpp | 629 +++++++++ profiles/lib/guidance.lua | 54 +- src/extractor/scripting_environment_lua.cpp | 16 + test/nodejs/constants.js | 2 +- 9 files changed, 2011 insertions(+), 29 deletions(-) create mode 100644 features/guidance/obvious-turn-discovery.feature diff --git a/features/guidance/obvious-turn-discovery.feature b/features/guidance/obvious-turn-discovery.feature new file mode 100644 index 000000000..b0cfde53f --- /dev/null +++ b/features/guidance/obvious-turn-discovery.feature @@ -0,0 +1,1212 @@ +@routing @guidance @obvious +Feature: Simple Turns + + Background: + Given the profile "car" + Given a grid size of 10 meters + + # https://www.openstreetmap.org/#map=19/52.51802/13.31337 + Scenario: Crossing a square + Given the node map + """ + d + | + e - - - - - - - c - g + | | + | | + | | + | | + f - - - - - - - b + | + a + """ + + And the ways + | nodes | highway | name | oneway | + | ab | residential | losch | no | + | cd | residential | ron | no | + | cefb | residential | alt | yes | + | bc | residential | alt | no | + | gc | residential | guer | no | + + When I route I should get + | from | to | route | turns | + | a | d | losch,ron | depart,arrive | + + + # https://www.openstreetmap.org/#map=18/52.51355/13.21988 + Scenario: Turning tertiary next to residential + Given the node map + """ + a - - - b - - - - c + | + d + ` _ + `-_ + e + """ + + And the ways + | nodes | highway | name | + | abde | tertiary | havel | + | bc | residential | anger | + + When I route I should get + | from | to | route | turns | + | a | c | havel,anger | depart,arrive | + | a | e | havel,havel,havel | depart,continue right,arrive | + + + #https://www.openstreetmap.org/#map=19/52.50996/13.23183 + Scenario: Tertiary turning at unclassified + Given the node map + """ + a - - - b - - - c + | + | + d + """ + + And the ways + | nodes | highway | + | ab | tertiary | + | bd | tertiary | + | bc | unclassified | + + When I route I should get + | from | to | route | turns | + | a | c | ab,bc | depart,arrive | + | a | d | ab,bd,bd | depart,turn right,arrive | + + #https://www.openstreetmap.org/#map=19/52.50602/13.25468 + # this scenario detects obvious correctly, but requires changes in collapsing roads + @todo + Scenario: Small offset due to large Intersection + Given the node map + """ + a - - - - - b - - - - - - c + | + d + ` ` + ` ` + f e + """ + + And the ways + | nodes | highway | + | abc | residential | + | bde | residential | + | df | residential | + + When I route I should get + | from | to | route | turns | + | a | e | abc,bde,bde | depart,turn right,arrive | + | a | f | abc,df,df | depart,turn sharp right,arrive | + + # https://www.openstreetmap.org/#map=19/52.49709/13.26620 + # https://www.openstreetmap.org/#map=19/52.49458/13.26273 + @todo + Scenario: Offsets in road + Given a grid size of 3 meters + Given the node map + """ + i + | + | + | + a - - - - - - - b e - - - - - - - - - f + `c - - - - - - - d` + | | + | | + | | + g h + """ + + And the ways + | nodes | highway | name | + | abcdef | residential | Zikade | + | gc | residential | Lärche | + | hd | residential | Kiefer | + | ei | residential | Kiefer | + + When I route I should get + | from | to | route | turns | locations | + | a | f | Zikade,Zikade | depart,arrive | a,f | + | f | a | Zikade,Zikade | depart,arrive | f,a | + | a | g | Zikade,Lärche,Lärche | depart,turn right,arrive | a,c,g | + | h | i | Kiefer,Kiefer | depart,arrive | h,i | + | i | h | Kiefer,Kiefer | depart,arrive | i,h | + | h | f | Kiefer,Zikade,Zikade | depart,turn right,arrive | h,d,f | + + + # https://www.openstreetmap.org/#map=20/52.49408/13.27375 + Scenario: Straight on unnamed service + Given the node map + """ + a - - - - b - - - - c + ` . + d + """ + + And the ways + | nodes | highway | name | + | abc | service | | + | bd | service | | + + When I route I should get + | from | to | route | turns | locations | + | a | c | , | depart,arrive | a,c | + | a | d | ,, | depart,turn slight right,arrive | a,b,d | + + + # https://www.openstreetmap.org/#map=19/52.49198/13.28069 + @todo + Scenario: Curved roads at turn + Given the node map + """ + ............g + .f + e``` + / + a - - - - b + c + ` + ` + ` + d + """ + + And the ways + | nodes | highway | name | oneway | + | abcd | residential | herbert | yes | + | befg | residential | casper | yes | + + When I route I should get + | from | to | route | turns | + | a | d | herbert,herbert | depart,arrive | + | a | g | herbert,casper,casper | depart,turn left,arrive | + + # https://www.openstreetmap.org/#map=19/52.49189/13.28431 + @todo + Scenario: Turning residential + Given the node map + """ + d + ` + a - - - b + \ + \ + c + """ + + And the ways + | nodes | highway | name | oneway | + | abc | residential | bismark | yes | + | bd | residential | caspar | yes | + + When I route I should get + | from | to | route | turns | + | a | c | bismark,bismark | depart,arrive | + | a | d | bismark,caspar,caspar | depart,turn left,arrive | + + # https://www.openstreetmap.org/#map=19/52.48681/13.28547 + @todo + Scenario: Slight Loss in Category with turning road + Given the node map + """ + g + / + f + e ..c - - - - d + a - - - - b````` + """ + + And the ways + | nodes | highway | name | + | ab | tertiary | warm | + | bcd | residential | warm | + | begg | tertiary | paul | + + When I route I should get + | from | to | route | turns | + | a | d | warm,warm | depart,arrive | + | a | g | warm,paul,paul | depart,turn slight left,arrive | + + # https://www.openstreetmap.org/#map=19/52.48820/13.29947 + Scenario: Driveway within curved road + Given the node map + """ + f--e + \ + ` + g d + \ | + \| + c + w + ./ + .b + a-` + """ + + And the ways + | nodes | highway | name | oneway | + | abc | residential | charlot | no | + | cdef | residential | fried | yes | + | gc | service | | | + + When I route I should get + | from | to | route | turns | + | a | f | charlot,fried | depart,arrive | + | a | g | charlot,, | depart,turn left,arrive | + + # https://www.openstreetmap.org/#map=20/52.46815/13.33984 + Scenario: Curve onto end of the road + Given the node map + """ + d - - - - e - f-_ + ``g + h + a - - - - - - b - - _ _ _ + ` ` ` c + """ + + And the ways + | nodes | highway | name | + | ab | residential | menz | + | defghbc | residential | rem | + + When I route I should get + | from | to | route | turns | + | a | c | menz,rem | depart,arrive | + | d | c | rem,rem,rem | depart,continue left,arrive | + | c | d | rem,rem,rem | depart,continue right,arrive | + | c | a | rem,menz | depart,arrive | + + # https://www.openstreetmap.org/#map=19/37.58151/-122.34863 + Scenario: Straight towards oneway street, Service Category, Unnamed + Given the node map + """ + a - - b - - c + | + d + """ + + And the ways + | nodes | highway | name | oneway | + | ab | service | | | + | cb | service | | yes | + | bd | service | | | + + When I route I should get + | from | to | route | turns | + | a | d | , | depart,turn right,arrive | + + + # https://www.openstreetmap.org/#map=19/37.61256/-122.40371 + Scenario: Turning Road with Offset at Segregated Intersection + Given the node map + """ + i h + | | + | . f - - g + a _ | ` | + b - c - - d - - e + | | + | | + j k + """ + + And the ways + | nodes | highway | name | oneway | + | abcd | residential | park | no | + | cfg | residential | diego | no | + | de | service | | yes | + | kdfh | primary | camino | yes | + | icj | primary | camino | yes | + + When I route I should get + | from | to | route | turns | + | a | g | park,diego,diego | depart,turn slight left,arrive | + | a | h | park,camino,camino | depart,turn left,arrive | + | a | j | park,camino,camino | depart,turn right,arrive | + + + # https://www.openstreetmap.org/#map=19/37.76407/-122.49642 + Scenario: Road splitting of straight turn + Given the node map + """ + d - - - - e - - - - -.f - - - - g + .```````` + a - - - - b - - - - - c + """ + + And the ways + | nodes | highway | name | oneway | + | ab | secondary | 37 | yes | + | bc | residential | 37 | yes | + | bf | secondary_link | Sunset | yes | + | defg | secondary | Sunset | yes | + + When I route I should get + | from | to | route | turns | + | a | c | 37,37 | depart,arrive | + | a | g | 37,Sunset,Sunset | depart,turn slight left,arrive | + + + # https://www.openstreetmap.org/#map=19/37.77072/-122.41727 + @todo + Scenario: Splitting road at traffic island before a turn + Given the node map + """ + _ _ e + .c - - - - - - - - - d - ` + a - - b ` + `f g` ` + h ` + | ` + i ` + .`.` + j. + . + . + . + k + """ + + And the ways + | nodes | highway | name | oneway | + | ab | primary | howard | no | + | dcb | primary | howard | yes | + | bfghij | primary | howard | yes | + | edjk | primary | ness | yes | + + + When I route I should get + | from | to | route | turns | # | + | a | k | howard,ness,ness | depart,turn left,arrive | | + | e | k | ness,ness | depart,continue slight left,arrive | #if modelled better, a depart/arrive would be more desirable | + | e | a | ness,howard,howard | depart,turn slight right,arrive | | + + # https://www.openstreetmap.org/#map=19/37.63171/-122.46205 + @todo + Scenario: Weird combination of turning roads + Given the node map + """ + a + | + b + |` + | e + | `f + | ` g _ + | j-- - - - h - - i + k + | l - - - - m - - n + | q + | p + | o + | + | + c + | + d + """ + + And the ways + | nodes | highway | name | oneway | + | abk | tertiary_link | loop | no | + | kcd | residential | loop | no | + | copqm | tertiary_link | | yes | + | klmn | tertiary_link | drive | yes | + | ih | tertiary_link | drive | yes | + | hjk | residential | drive | yes | + | hgfeb | tertiary_link | | yes | + + When I route I should get + | from | to | turns | route | + | i | a | depart,turn right,arrive | drive,loop,loop | + | i | d | depart,turn left,arrive | drive,loop,loop | + | a | n | depart,turn left,arrive | loop,drive,drive | + | d | n | depart,turn right,arrive | loop,drive,drive | + + # https://www.openstreetmap.org/#map=19/37.26591/-121.84474 + @todo + Scenario: Road splitting (unmerged) + Given the node map + """ + . g - - - h + d - - - - c - - e - - f ` + . ` ` + a - - - - b ` + ` + ` + ` + ` + ` + i + """ + + And the ways + | nodes | highway | name | oneway | + | ei | service | | | + | abe | primary | lane | yes | + | ecd | primary | lane | yes | + | hgfe | primary | lane | no | + + When I route I should get + | from | to | route | turns | + | a | h | lane,lane | depart,arrive | + | a | i | lane,, | depart,turn right,arrive | + | h | d | lane,lane | depart,arrive | + | h | i | lane,, | depart,turn left,arrive | + + + # https://www.openstreetmap.org/#map=19/37.85108/-122.27078 + @todo + Scenario: Turning Road category + Given the node map + """ + e d + | | + | | + f c i + | | ` + | j` + | ` | l + |k |.` + | | + g b + | | + | | + | | + h a + """ + + And the ways + | nodes | highway | name | oneway | + | abl | primary | adeline | yes | + | bjcd | tertiary | martin | yes | + | efg | tertiary | martin | yes | + | ijkgh | primary | adeline | yes | + + When I route I should get + | from | to | route | turns | + | e | h | martin,adeline | depart,arrive | + | a | d | adeline,martin,martin | depart,turn slight left, arrive | + | a | l | adeline,adeline,adeline | depart,continue slight right,arrive | + | i | h | adeline,adeline | depart,arrive | + + # https://www.openstreetmap.org/#map=19/37.76471/-122.49639 + @todo + Scenario: Turning road + Given the node map + """ + f - - - - - - - e - - - - d + g + h + a - - - b - - - - - - - - c + | + | + i + """ + + And the ways + | nodes | highway | name | oneway | + | abd | primary | lincoln | yes | + | def | primary | lincoln | yes | + | eghbi | secondary | 37 | yes | + + When I route I should get + | from | to | route | turns | + | d | i | lincoln,37,37 | depart,turn left,arrive | + | d | f | lincoln,lincoln | depart,arrive | + + # https://www.openstreetmap.org/#map=19/37.63541/-122.48343 + # https://www.openstreetmap.org/#map=19/52.47752/13.28864 + @todo + Scenario: Road Splitting up + Given the node map + """ + d + ` + ` + ` + a - - - - - - - - - b + ` + ` + `c + + """ + + And the ways + | nodes | highway | name | + | abc | residential | vista | + | bd | residential | sierra | + + When I route I should get + | from | to | route | turns | + | a | c | vista,vista | depart,arrive | + | a | d | vista,sierra,sierra | depart,turn left,arrive | + + # https://www.openstreetmap.org/#map=19/52.45191/13.44113 + @todo + Scenario: Road Splitting up at a Driveway + Given the node map + """ + d + ` + ` + ` + a - - - - - - - - - b - - - - - e + ` + ` + ` + `c + """ + + And the ways + | nodes | highway | name | + | abc | residential | britz | + | bd | residential | palz | + | be | service | | + + When I route I should get + | from | to | route | turns | + | a | c | britz,britz | depart,arrive | + | a | e | britz,, | depart,turn straight,arrive | + + # https://www.openstreetmap.org/#map=20/37.62997/-122.49246 + @todo + Scenario: Curving road with name-handoff + Given the node map + """ + a + | + | + b + ` + c + ` + d + l - k h ` e + ` j - i ` ` f - - - - - g + """ + + And the ways + | nodes | highway | name | oneway | + | abcd | tertiary | palm | no | + | defg | tertiary | clare | no | + | lkjihd | residential | clare | yes | + + When I route I should get + | from | to | route | turns | + | a | g | palm,clare,clare | depart,turn left,arrive | + | g | a | clare,palm,palm | depart,turn right,arrive | + | l | g | clare,clare | depart,arrive | + | l | a | clare,palm,palm | depart,turn left,arrive | + + # https://www.openstreetmap.org/#map=19/37.84291/-122.23681 + Scenario: Two roads turning into the same direction + Given the node map + """ + e + | + |.c + a - - - b` + | + | + d + """ + + And the ways + | nodes | highway | name | oneway | + | abc | residential | romany | no | + | db | residential | ost | yes | + | be | residential | martin | yes | + + When I route I should get + | from | to | route | turns | + | a | c | romany,romany | depart,arrive | + + + # https://www.openstreetmap.org/#map=19/37.82815/-122.28733 + @todo + Scenario: Turning Secondary Next to Residential + Given the node map + """ + c + | | + | | + -----` | + a b - - - d + ---------- + """ + + And the ways + | nodes | highway | name | + | ab | secondary | mandela | + | bc | secondary | horton | + | bd | residential | yerda | + + When I route I should get + | from | to | route | turns | + | a | d | mandela,yerda,yerda | depart,turn straight,arrive | + | a | c | mandela,horton,horton | depart,turn left,arrive | + + + # https://www.openstreetmap.org/#map=19/52.46341/13.40272 + Scenario: Loss of road class on turn (segregated) + Given the node map + """ + f - - - e - - - d + | + 1 + | + a - - - b - - - c + | + | + g + """ + + And the ways + | nodes | highway | name | oneway | + | ab | tertiary | germ | yes | + | ef | tertiary | germ | yes | + | de | tertiary | ober | yes | + | bc | tertiary | ober | yes | + | be | tertiary | germ | no | + | bg | residential | germ | no | + + When I route I should get + | from | to | route | turns | + | 1 | g | germ,germ | depart,arrive | + | d | g | ober,germ,germ | depart,turn left,arrive | + | a | g | germ,germ,germ | depart,continue right,arrive | + + # https://www.openstreetmap.org/#map=19/37.29821/-121.86874 + Scenario: Sliproads of Higher Category when entering Intersection + Given the node map + """ + e + | + g| + | + | + h | + i | + a - - b - - - c - - d + j | + k | + | + l| + | + f + """ + + And the ways + | nodes | highway | name | oneway | + | abcd | tertiary | stone | no | + | ecf | primary | curt | yes | + | eghib | primary_link | | yes | + | bjklf | primary_link | | yes | + + When I route I should get + | from | to | route | turns | + | a | d | stone,stone | depart,arrive | + + + # https://www.openstreetmap.org/#map=19/37.63866/-122.46677 + @todo + Scenario: Slightly offset traffic circle + Given the node map + """ + i----h + ` g + ` ` + a - - - - - b | + c f + ` d - e ` + """ + + And the ways + | nodes | highway | name | + | abcdefghib | residential | road | + + When I route I should get + | from | to | route | turns | + | a | e | road,road | depart,arrive | + | i | a | road,road | depart,arrive | + | d | a | road,road | depart,arrive | + + + # https://www.openstreetmap.org/#map=19/37.63829/-122.46345 + @todo + Scenario: Entering a motorway (curved) + Given the node map + """ + a - - - - - - b - - - c - - - - - d + e g + f + | + i - h - j + """ + + And the ways + | nodes | highway | name | oneway | + | abcd | trunk | sky | yes | + | befgc | trunk_link | | yes | + | fh | trunk_link | | no | + | ihj | residential | susan | no | + + When I route I should get + | from | to | route | turns | + | i | d | susan,,sky | depart,turn left,arrive | + | j | d | susan,,sky | depart,turn right,arrive | + + + # https://www.openstreetmap.org/#map=18/48.99155/8.43520 + @todo + Scenario: Passing a motorway link on a trunk road + Given the node map + """ + a - - - - - - - - - - b----- + ` ````````````c + ` d + """ + + And the ways + | nodes | highway | name | ref | oneway | + | abc | trunk | sued | K 9652 | yes | + | bd | motorway_link | | A 5 | yes | + + When I route I should get + | from | to | route | turns | + | a | c | sued,sued | depart,arrive | + | a | d | sued,, | depart,on ramp right,arrive | + + + # https://www.openstreetmap.org/#map=19/48.98900/8.43800 + Scenario: Splitting motorway links without names but with destinations + Given the node map + """ + .... h + . .g.` + ..... e..`............... f + ... d---``` + c` + / + / + b + / + a + """ + + And the ways + | nodes | highway | name | destination | oneway | + | abcde | motorway_link | | | yes | + | ef | motorway_link | | right | yes | + | egh | motorway_link | | left | yes | + + When I route I should get + | from | to | route | turns | + | a | f | ,, | depart,fork slight right,arrive | + | a | h | ,, | depart,fork slight left,arrive | + + # https://www.openstreetmap.org/#map=19/49.01098/8.36052 + Scenario: Splitting Road at intersection (modelled turn) + Given the node map + """ + h + | + j - - - - - g - - - - - - - - - - - - i + f + e + d + a - - - b - - - - - - - - - - - - - - c + """ + + And the ways + | nodes | highway | name | oneway | + | ab | primary | ente | yes | + | bc | primary | rhein | yes | + | bdefg | primary_link | rhein | yes | + | ig | primary | rhein | yes | + | gj | primary | ente | yes | + | gh | residential | rhein | no | + + When I route I should get + | from | to | route | turns | + | a | c | ente,rhein,rhein | depart,new name straight,arrive | + | a | h | ente,rhein,rhein | depart,turn left,arrive | + + + # https://www.openstreetmap.org/#map=19/48.99776/8.47766 + Scenario: Narrow cross + Given the node map + """ + a e + \ / + \ / + \ / + b + / \ + / \ + / \ + d c + """ + + And the ways + | nodes | highway | name | + | ab | secondary | baden | + | be | primary | gym | + | bc | tertiary | ritter | + | db | primary | baden | + + When I route I should get + | from | to | route | turns | + | a | d | baden,baden,baden | depart,continue right,arrive | + | a | c | baden,ritter | depart,arrive | + | a | e | baden,gym,gym | depart,turn sharp left,arrive | + + + # https://www.openstreetmap.org/#map=19/48.99870/8.48122 + Scenario: Residential Segregated + Given the node map + """ + f e + | | + | | + | | + | | + | | + | | + | | + a - - b - - c - - d + """ + + And the ways + | nodes | highway | name | oneway | + | ab | residential | possel | no | + | fb | residential | berg | yes | + | bc | residential | berg | no | + | ce | residential | berg | yes | + | cd | residential | kastell | no | + + When I route I should get + | from | to | route | turns | + | a | d | possel,kastell | depart,arrive | + | a | e | possel,berg,berg | depart,turn left,arrive | + + + # https://www.openstreetmap.org/#map=19/48.99810/8.46749 + @todo + Scenario: Slight End of Road + Given the node map + """ + d + / + / + a - - - - - - b - - - - - - - c + """ + + And the ways + | nodes | highway | name | + | abd | residential | kanzler | + | bc | residential | gartner | + + When I route I should get + | from | to | route | turns | + | a | c | kanzler,gartner | depart,arrive | + | a | d | kanzler,kanzler,kanzler | depart,continue left,arrive | + | d | a | kanzler,kanzler,kanzler | depart,continue right,arrive | + + # https://www.openstreetmap.org/#map=18/52.46942/13.33159 + Scenario: Curved crossing + Given the node map + """ + f + .` + h . + `. e ` + `. . ` + `. . ` + `. . ` + d + c - ` `. + a - - - - - - - - - b - - ` ` ` `. + g + """ + + And the ways + | nodes | highway | name | oneway | + | abcd | residential | hand | no | + | def | residential | hand | yes | + | hdg | secondary | schmi | no | + + When I route I should get + | from | to | route | turns | + | a | f | hand,hand | depart,arrive | + | h | g | schmi,schmi | depart,arrive | + + + # https://www.openstreetmap.org/#map=19/52.56562/13.39109 + @todo + Scenario: Dented Straight + Given the node map + """ + a - - - b. . d - - - - e + ` ` + `c` + | + | + f + """ + + And the ways + | nodes | highway | name | + | abcde | residential | nord | + | cf | residential | stern | + + When I route I should get + | from | to | route | turns | + | a | e | nord,nord | depart,arrive | + | e | a | nord,nord | depart,arrive | + + + # https://www.openstreetmap.org/#map=17/52.57124/13.39892 + @todo + Scenario: Primary road curved turn + Given the node map + """ + m - - l.... + `k. + a - - b - . ``- - - - p + `c . \ + `. `j - - - o + d \ + \ i + e \ + | h + | | + f g + + + """ + + And the ways + | nodes | highway | name | oneway | + | abc | primary | schoen | yes | + | cdef | primary | grab | yes | + | klm | primary | schoen | yes | + | ghijk | primary | grab | yes | + | cj | secondary_link | mann | yes | + | jo | secondary | mann | yes | + | pk | secondary | mann | yes | + + When I route I should get + | from | to | route | turns | + | a | f | schoen,grab,grab | depart,turn right,arrive | + | g | m | grab,schoen | depart,arrive | + | a | o | schoen,mann,mann | depart,turn slight left,arrive | + + + # https://www.openstreetmap.org/#map=18/52.55374/13.41462 + @todo + Scenario: Turn Links as Straights + Given the node map + """ + l k + | | + j - - -i - - hg- - - f + | / |/ + /| / | + a - --bc - - d - - - e + | | + m n + """ + + And the ways + | nodes | highway | oneway | name | + | abc | primary | yes | born | + | ij | primary | yes | born | + | cde | primary | yes | wisb | + | fghi | primary | yes | wisb | + | li | primary | yes | berl | + | hk | primary | yes | berl | + | icm | primary | yes | scho | + | ndh | primary | yes | scho | + | bh | primary_link | yes | | + | gc | primary_link | yes | | + + When I route I should get + | from | to | route | turns | locations | # | + | a | k | born,,berl | depart,turn left,arrive | a,b,k | On improved collapse, this should offer berl on turn | + | f | m | wisb,,scho | depart,turn left,arrive | f,g,m | On improved collapse, this should offer scho on turn | + + + # https://www.openstreetmap.org/#map=19/52.56934/13.40131 + @todo + Scenario: Crossing Segregated before a turn + Given the node map + """ + _-d + f - - - e -- ` ` + | _-c + a - - -.b -- ` ` + . ` + g` + """ + + And the ways + | nodes | highway | oneway | name | + | ab | primary | yes | scho | + | bc | primary | yes | brei | + | de | primary | yes | brei | + | ef | primary | yes | scho | + | gb | secondary | no | woll | + | be | secondary_link | no | woll | + + When I route I should get + | from | to | route | turns | + | g | c | woll,brei,brei | depart,turn right,arrive | + | g | f | woll,scho,scho | depart,turn sharp left,arrive | + | a | c | scho,brei | depart,arrive | + | d | f | brei,scho | depart,arrive | + + + # https://www.openstreetmap.org/#map=19/52.58072/13.42985 + @todo + Scenario: Trunk Turning into Motorway + Given the node map + """ + a - - - - - b - - - + ` . ` ` ` ` ` ` ` ` c + ` - - - - d + + """ + + And the ways + | nodes | highway | name | oneway | + | ab | trunk | prenz | yes | + | bc | motorway | | yes | + | bd | trunk_link | | yes | + + When I route I should get + | from | to | route | turns | + | a | c | prenz, | depart,arrive | + | a | d | prenz,,, | depart,turn slight right,arrive | + + + # https://www.openstreetmap.org/#map=19/52.57800/13.42900 + Scenario: Splitting Secondary + Given the node map + """ + . . c + .. `` + a - - - - - - - b ` ` ` ` + \ + \ d - - - - - - - - - e + """ + + And the ways + | nodes | highway | name | lanes | oneway | + | ab | secondary | dame | 2 | no | + | bc | secondary | pase | 2 | no | + | bde | secondary | feuc | 1 | yes | + + When I route I should get + | from | to | route | turns | + | a | c | dame,pase,pase | depart,new name straight,arrive | + | a | e | dame,feuc,feuc | depart,turn slight right,arrive | + + # https://www.openstreetmap.org/#map=19/52.48468/13.34532 + @todo + Scenario: Forking into Tertiary + Given the node map + """ + d + . . ` + a - - - - - b - - - - - c + """ + + And the ways + | nodes | highway | name | oneway | lanes | + | ab | primary | kenny | yes | 4 | + | bc | tertiary | kenny | yes | 2 | + | bd | primary | domi | yes | 2 | + + When I route I should get + | from | to | route | turns | + | a | c | kenny,kenny,kenny | depart,fork slight right,arrive | + | a | d | kenny,domi,domi | depart,fork slight left,arrie | + + # https://www.openstreetmap.org/#map=18/52.56960/13.43815 + Scenario: Turn onto classified + Given the node map + """ + e f + | | + | | + a - - - - - b - - c - - . . + ` ` ` d + """ + + And the ways + | nodes | highway | name | oneway | + | abc | secondary | roma | no | + | cd | unclassified | roma | no | + | eb | secondary | roth | yes | + | cf | secondary | roth | yes | + + When I route I should get + | from | to | route | turns | + | a | d | roma,roma | depart,arrive | + | a | f | roma,roth,roth | depart,turn left,arrive | + + + # https://www.openstreetmap.org/#map=18/52.50908/13.27312 + @todo + Scenario: Merging onto a different street + Given the node map + """ + d + `.`.`.`.`.`.b - - - - c + a` + """ + + And the ways + | nodes | highway | name | oneway | + | ab | secondary | masu | yes | + | dbc | primary | theo | yes | + + When I route I should get + | from | to | route | turns | + | a | c | masu,theo | depart,arrive | + | d | c | theo,theo | depart,arrive | + + # https://www.openstreetmap.org/#map=18/52.51299/13.28936 + Scenario: Lanes override road classes + Given the node map + """ + e + | + | + a - - - b - - - c + | + | + d + """ + + And the ways + | nodes | highway | name | lanes | + | ab | primary | knob | 2 | + | bc | unclassified | knob | | + | db | primary | soph | 3 | + | be | living_street | soph | 3 | + + When I route I should get + | from | to | route | turns | + | a | c | knob,knob | depart,arrive | + | d | e | soph,soph | depart,arrive | + | d | a | soph,knob,knob | depart,turn left,arrive | diff --git a/features/guidance/turn-lanes.feature b/features/guidance/turn-lanes.feature index 54a8e3be3..bbc2a867a 100644 --- a/features/guidance/turn-lanes.feature +++ b/features/guidance/turn-lanes.feature @@ -614,8 +614,9 @@ Feature: Turn Lane Guidance | | | | b d - h c - ' -- g - - f + h-----c + | `-f + g """ And the ways diff --git a/features/guidance/turn.feature b/features/guidance/turn.feature index 80913f16b..a76774748 100644 --- a/features/guidance/turn.feature +++ b/features/guidance/turn.feature @@ -964,15 +964,15 @@ Feature: Simple Turns Given the node map """ g - - f y - i - j k a b x - e c - d - + | + _--f-----y + i-' | + j-k-a]|[b---x + e 'c + |'d' + | h - + | q """ @@ -1373,6 +1373,36 @@ Feature: Simple Turns | a,d | ab,bcd,bcd | depart,fork slight right,arrive | | a,g | ab,befg,befg | depart,fork slight left,arrive | + @routing @car + Scenario: No turn instruction when turning from unnamed onto unnamed + Given the node map + """ + a + | + | + | + | + b----------------c + | + | + | + | + | + | + d + """ + + And the ways + | nodes | highway | name | ref | + | ab | trunk_link | | | + | db | secondary | | L 460 | + | bc | secondary | | | + + When I route I should get + | from | to | route | ref | turns | + | d | c | ,, | L 460,, | depart,turn right,arrive | + | c | d | ,, | ,L 460,L 460 | depart,turn left,arrive | + # https://www.openstreetmap.org/#map=18/52.25130/10.42545 Scenario: Turn for roads with no name, ref changes Given the node map diff --git a/features/options/extract/turn_function.feature b/features/options/extract/turn_function.feature index adf412ccf..a1db9339f 100644 --- a/features/options/extract/turn_function.feature +++ b/features/options/extract/turn_function.feature @@ -152,7 +152,7 @@ Feature: Turn Function Information And stdout should contain "number_of_roads 3" And stdout should contain "source_priority_class 4" And stdout should contain "target_priority_class 0" - And stdout should contain "target_priority_class 11" + And stdout should contain "target_priority_class 10" # turning abd, give information about bc And stdout should contain /roads_on_the_right \[1\] speed: [0-9]+, is_incoming: false, is_outgoing: true, highway_turn_classification: 4, access_turn_classification: 0/ # turning abc, give information about bd @@ -180,7 +180,3 @@ Feature: Turn Function Information And stdout should contain /roads_on_the_right \[1\] speed: [0-9]+, is_incoming: true, is_outgoing: false, highway_turn_classification: 3, access_turn_classification: 0/ # turning abc, give information about about db And stdout should contain /roads_on_the_left \[1\] speed: [0-9]+, is_incoming: true, is_outgoing: false, highway_turn_classification: 0, access_turn_classification: 1/ - - - - diff --git a/include/extractor/road_classification.hpp b/include/extractor/road_classification.hpp index a98c20525..5aca13e1c 100644 --- a/include/extractor/road_classification.hpp +++ b/include/extractor/road_classification.hpp @@ -1,6 +1,7 @@ #ifndef OSRM_EXTRACTOR_CLASSIFICATION_DATA_HPP_ #define OSRM_EXTRACTOR_CLASSIFICATION_DATA_HPP_ +#include #include #include #include @@ -22,17 +23,25 @@ namespace RoadPriorityClass typedef std::uint8_t Enum; // Top priority Road const constexpr Enum MOTORWAY = 0; +const constexpr Enum MOTORWAY_LINK = 1; // Second highest priority const constexpr Enum TRUNK = 2; +const constexpr Enum TRUNK_LINK = 3; // Main roads const constexpr Enum PRIMARY = 4; +const constexpr Enum PRIMARY_LINK = 5; const constexpr Enum SECONDARY = 6; +const constexpr Enum SECONDARY_LINK = 7; const constexpr Enum TERTIARY = 8; +const constexpr Enum TERTIARY_LINK = 9; // Residential Categories const constexpr Enum MAIN_RESIDENTIAL = 10; const constexpr Enum SIDE_RESIDENTIAL = 11; +const constexpr Enum ALLEY = 12; +const constexpr Enum PARKING = 13; // Link Category const constexpr Enum LINK_ROAD = 14; +const constexpr Enum UNCLASSIFIED = 15; // Bike Accessible const constexpr Enum BIKE_PATH = 16; // Walk Accessible @@ -125,6 +134,67 @@ inline bool canBeSeenAsFork(const RoadClassification first, const RoadClassifica static_cast(second.GetPriority())) <= 1; } +// priority groups are road classes that can be categoriesed as somewhat similar +inline std::uint32_t getRoadGroup(const RoadClassification classification) +{ + // a list of dividers (inclusive) specifying the end of a class + const auto constexpr num_dividers = 7; + // dividers point one past the entry we want, so motorways will be pre-primary + const constexpr RoadPriorityClass::Enum dividers[num_dividers] = { + RoadPriorityClass::PRIMARY, + RoadPriorityClass::TERTIARY_LINK, + RoadPriorityClass::ALLEY, + RoadPriorityClass::LINK_ROAD, + RoadPriorityClass::UNCLASSIFIED, + RoadPriorityClass::BIKE_PATH, + RoadPriorityClass::CONNECTIVITY + 1}; + + const auto upper = + std::upper_bound(dividers, dividers + num_dividers, classification.GetPriority()); + return upper - dividers; +} + +// a road classification is strictly less, if it belongs to a lower general category of roads. E.g. +// normal city roads are strictly less of a priority than a motorway and alleys are strictly less +// than inner-city roads +inline bool strictlyLess(const RoadClassification lhs, const RoadClassification rhs) +{ + const auto lhs_class = getRoadGroup(lhs); + const auto rhs_class = getRoadGroup(rhs); + // different class, not neighbors + return lhs_class > rhs_class && + ((lhs.GetPriority() - rhs.GetPriority() > 4) || lhs.IsLowPriorityRoadClass()); +} + +// check whether a link class is the fitting link class to a road +inline bool isLinkTo(const RoadClassification link, const RoadClassification road) +{ + // needs to be a link/non-link combination + if (!link.IsLinkClass() || road.IsLinkClass()) + return false; + + switch (link.GetPriority()) + { + case RoadPriorityClass::MOTORWAY_LINK: + return road.GetPriority() == RoadPriorityClass::MOTORWAY; + + case RoadPriorityClass::TRUNK_LINK: + return road.GetPriority() == RoadPriorityClass::TRUNK; + + case RoadPriorityClass::PRIMARY_LINK: + return road.GetPriority() == RoadPriorityClass::PRIMARY; + + case RoadPriorityClass::SECONDARY_LINK: + return road.GetPriority() == RoadPriorityClass::SECONDARY; + + case RoadPriorityClass::TERTIARY_LINK: + return road.GetPriority() == RoadPriorityClass::TERTIARY; + + default: + return false; + } +} + inline bool obviousByRoadClass(const RoadClassification in_classification, const RoadClassification obvious_candidate, const RoadClassification compare_candidate) diff --git a/include/guidance/intersection_handler.hpp b/include/guidance/intersection_handler.hpp index 8bb589791..642a97e4c 100644 --- a/include/guidance/intersection_handler.hpp +++ b/include/guidance/intersection_handler.hpp @@ -68,6 +68,16 @@ class IntersectionHandler TurnType::Enum areSameClasses(const EdgeID via_edge, const ConnectedRoad &road) const; + template // works with Intersection and IntersectionView + inline bool IsDistinctTurn(const std::size_t index, + const EdgeID via_edge, + const IntersectionType &intersection) const; + + template // works with Intersection and IntersectionView + inline bool IsDistinctContinue(const std::size_t index, + const EdgeID via_edge, + const IntersectionType &intersection) const; + // Find the most obvious turn to follow. The function returns an index into the intersection // 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 @@ -75,6 +85,14 @@ class IntersectionHandler template // works with Intersection and IntersectionView std::size_t findObviousTurn(const EdgeID via_edge, const IntersectionType &intersection) const; + template // works with Intersection and IntersectionView + std::size_t findObviousTurnOld(const EdgeID via_edge, + const IntersectionType &intersection) const; + + template // works with Intersection and IntersectionView + std::size_t findObviousTurnNew(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. // `through_street` indicates if the street turned onto is a through sreet (think mergees and @@ -124,10 +142,621 @@ class IntersectionHandler }; // Impl. +using osrm::extractor::getRoadGroup; +template // works with Intersection and IntersectionView +inline bool IntersectionHandler::IsDistinctTurn(const std::size_t index, + const EdgeID via_edge, + const IntersectionType &intersection) const +{ + // for comparing road categories + const auto &via_edge_data = node_based_graph.GetEdgeData(via_edge); + const auto &candidate = intersection[index]; + const auto &candidate_data = node_based_graph.GetEdgeData(candidate.eid); + + auto const num_lanes = [](auto const &data) { + return data.flags.road_classification.GetNumberOfLanes(); + }; + + auto const override_class_by_lanes = [&](auto const &compare_data) { + // sometimes roads of same size are tagged strangely within a neighborhood, combining + // primary roads with residential roads. If the road with can be deducted from lanes, we + // can override such a classification + if (num_lanes(compare_data) > 0 && num_lanes(via_edge_data) > 0) + { + // check if via-edge has more than one additional lane, relative to the compare data + if (num_lanes(via_edge_data) - num_lanes(compare_data) > 1) + return true; + } + return false; + }; + + // check if a road is distinct to the obvious turn candidate in its road class. This is the case + // only if we pass by a lower road category class or a link to the same category + auto const distinct_by_class = [&](auto const &road) { + auto const &compare_data = node_based_graph.GetEdgeData(road.eid); + + // passing a road of a stricly lower category (e.g. residential driving past driveway, + // primary road passing a residential road) but also exiting a freeway onto a primary in the + // presence of an alley + if (strictlyLess(compare_data.flags.road_classification, + via_edge_data.flags.road_classification) && + strictlyLess(compare_data.flags.road_classification, + candidate_data.flags.road_classification) && + override_class_by_lanes(compare_data)) + { + return true; + } + + // passing by a link of the same category + if (isLinkTo(compare_data.flags.road_classification, + via_edge_data.flags.road_classification) && + isLinkTo(compare_data.flags.road_classification, + candidate_data.flags.road_classification)) + return true; + + // staying on the same road class, encountering a road that is a severe change in class + // (residential-> motorway_link) is considered a fair distinction + if (compare_data.flags.road_classification.IsLinkClass() && + (via_edge_data.flags.road_classification.GetPriority() == + candidate_data.flags.road_classification.GetPriority()) && + (std::abs(static_cast(getRoadGroup(via_edge_data.flags.road_classification)) - + static_cast(getRoadGroup(compare_data.flags.road_classification))) > + 4) && + override_class_by_lanes(compare_data)) + { + return true; + } + + return false; + }; + + // in case of narrow turns, we apply different criteria than for actual turns. In case of a + // narrow turn, having two choices one of which is forbidden is fine. In case of a end of the + // road turn, having two directions and not being allowed to turn onto one of them isn't always + // as clear + auto const candidate_deviation = util::angularDeviation(candidate.angle, STRAIGHT_ANGLE); + + const auto &via_edge_annotation = + node_data_container.GetAnnotation(via_edge_data.annotation_data); + const auto &candidate_annotation = + node_data_container.GetAnnotation(candidate_data.annotation_data); + + const auto constexpr max_narrow_deviation = GROUP_ANGLE; + // on cases where the candidate deviation is in a narrow range, we can consider the deviaiton of + // other turns as a distinction criteria + // + // c + // * + // * + // b - d + // | + // a + // for example can be considered obvious as goig straight, while + // + // c + // d * + // * * + // b + // | + // a + // should err on the side of caution (when only comparing deviations) + + if (candidate_deviation <= max_narrow_deviation) + { + // check if the candidate changes it's name + auto const candidate_changes_name = + util::guidance::requiresNameAnnounced(via_edge_annotation.name_id, + candidate_annotation.name_id, + name_table, + street_name_suffix_table); + + // check if there are other narrow turns are not considered passing a low category or simply + // a link of the same type as the potentially obvious turn + auto const is_similar_turn = [&](auto const &road) { + // skip over our candidate + if (road.eid == candidate.eid) + return false; + + // since we have a narrow turn, we only care for roads allowing entry + if (candidate_deviation < NARROW_TURN_ANGLE && !road.entry_allowed) + { + return false; + } + + // detect link roads in segregated intersections + if (!road.entry_allowed && (intersection.size() == 5) && + (std::count_if(intersection.begin(), intersection.end(), [](auto const &road) { + return road.entry_allowed; + }) <= 2)) + { + // if we are on a link road and all other turns form a 4 way intersection, the + // angular differences of all other turns need to be near 90 degrees + bool all_close_to_90 = true; + for (std::size_t i = 1; i < 3; ++i) + { + auto const deviation = + util::angularDeviation(intersection[i].angle, intersection[i + 1].angle); + if (deviation < 75 || deviation > 105) + { + all_close_to_90 = false; + break; + } + } + if (all_close_to_90) + { + return false; + } + } + + auto const compare_deviation = util::angularDeviation(road.angle, STRAIGHT_ANGLE); + auto const &compare_data = node_based_graph.GetEdgeData(road.eid); + auto const &compare_annotation = + node_data_container.GetAnnotation(compare_data.annotation_data); + + // in the states, many small side-roads are marked restricted. We could consider them + // driveways. Passing by one of these should always be obvious + if (candidate_deviation < NARROW_TURN_ANGLE && + (compare_deviation > 1.5 * candidate_deviation) && compare_data.flags.restricted && + !via_edge_data.flags.restricted && !candidate_data.flags.restricted) + { + return false; + } + + // if we see a roundabout that is a larger turn, we do not consider it similar. This is + // related to throughabouts which often are slightly curved on exits: + // | + // - a d - + // \` e f ` / + // b - - c + if (compare_data.flags.roundabout != via_edge_data.flags.roundabout && + via_edge_data.flags.roundabout == candidate_data.flags.roundabout && + candidate_deviation < compare_deviation) + return false; + + // to find whether a continuing road is turning, we need to check if it is an actual + // turn, a segregated intersection + + auto const opposing_turn = + intersection.FindClosestBearing(util::bearing::reverse(road.perceived_bearing)); + auto const opposing_data = node_based_graph.GetEdgeData(opposing_turn->eid); + // Check for a situation like: + // + // a a + // a a + // a a + b b + b b + // c ac + // c a c + // + // opposed to + // + // a + // a + // a a + b b + // a + // a + auto const name_changes_onto_compare = + util::guidance::requiresNameAnnounced(via_edge_annotation.name_id, + compare_annotation.name_id, + name_table, + street_name_suffix_table); + auto const opposing_name = + node_data_container.GetAnnotation(opposing_data.annotation_data).name_id; + auto const name_changes_onto_compare_from_opposing = + util::guidance::requiresNameAnnounced(opposing_name, + compare_annotation.name_id, + name_table, + street_name_suffix_table); + + // check if the continuing road takes a turn, and we are turning off it. This is + // required, sicne we could end up announcing `follow X for 2 miles` and if `X` turns, + // we would be inclined to do the turn as well, if it isn't crazy (like a sharp turn) + auto const continue_turns = (via_edge_annotation.name_id != EMPTY_NAMEID) && + !name_changes_onto_compare && + (util::angularDeviation(road.angle, opposing_turn->angle) < + (STRAIGHT_ANGLE - NARROW_TURN_ANGLE) && + name_changes_onto_compare_from_opposing) && + util::angularDeviation(road.angle, 0) > NARROW_TURN_ANGLE; + + auto const continuing_road_takes_a_turn = candidate_changes_name && continue_turns; + // at least a relative and a maximum difference, if the road name does not turn. + // Since we can announce `stay on X for 2 miles, we need to ensure that we announce + // turns off it (even if straight). Otherwise people might follow X further than they + // should + // For roads splitting with the same name, we ask for a larger difference. + auto const minimum_angle_difference = FUZZY_ANGLE_DIFFERENCE; + /* + (via_edge_annotation.name_id != EMPTY_NAMEID && !candidate_changes_name && + !name_changes_onto_compare) + ? NARROW_TURN_ANGLE + : FUZZY_ANGLE_DIFFERENCE; + */ + + // if a turn angle isn't remotely forward, we don't consider a deviation to be distinct + // auto const both_turns_go_into_same_direction = + // (candidate.angle >= STRAIGHT_ANGLE) == + // (road.angle >= STRAIGHT_ANGLE); // are both turns to the left? + auto const roads_deviation_is_distinct = + compare_deviation / std::max(0.1, candidate_deviation) > DISTINCTION_RATIO && + std::abs(compare_deviation - candidate_deviation) > minimum_angle_difference; + + auto const continue_is_main_class = + via_edge_data.flags.road_classification.GetPriority() <= + extractor::RoadPriorityClass::SECONDARY; + if ((!continuing_road_takes_a_turn || !continue_is_main_class) && + roads_deviation_is_distinct) + { + return false; + } + + // in case of slight turns, there can be exits that are also very narrow. If they are on + // a new lane though, we accept smaller distinction angles + // + // a - - - b - - - - c + // ` ` ` `d + // + // A narrow exit lane can be present, but still be distinct from the road + if (num_lanes(via_edge_data) > 0 && + num_lanes(candidate_data) == num_lanes(via_edge_data)) + { + if (compare_deviation > candidate_deviation && + candidate_deviation <= FUZZY_ANGLE_DIFFERENCE && + (compare_deviation - candidate_deviation) > 0.5 * FUZZY_ANGLE_DIFFERENCE) + { + // very slight angle going straight on the exact same number of lanes as coming + // in, one turn branching off in a slight angle with additional lanes + return false; + } + } + + // when crossing an intersection of a similar road category, lower deviations can also + // make sense + // crossing a compare road + auto const crossing_compare = + !name_changes_onto_compare_from_opposing && + (util::angularDeviation(opposing_turn->angle, road.angle) > + STRAIGHT_ANGLE - FUZZY_ANGLE_DIFFERENCE) && + name_changes_onto_compare; + + // in case of a continuing road of higher road class, we accept quite a bit loweer + // distinction + auto const compare_has_lower_class = + (candidate_data.flags.road_classification.GetPriority() == + via_edge_data.flags.road_classification.GetPriority()) && + (candidate_data.flags.road_classification.GetPriority() < + compare_data.flags.road_classification.GetPriority()); + + // for something like a tertiary link, we skip over tertiary, secondary_link, secondary, + // primary_link and require at least a primary road + auto const compare_has_way_higher_class = + (candidate_data.flags.road_classification.GetPriority() == + via_edge_data.flags.road_classification.GetPriority()) && + (std::abs(static_cast( + candidate_data.flags.road_classification.GetPriority()) - + static_cast( + compare_data.flags.road_classification.GetPriority())) > 4); + + if (!candidate_changes_name && !continuing_road_takes_a_turn && + (compare_has_lower_class || compare_has_way_higher_class || crossing_compare) && + compare_deviation / std::max(0.1, candidate_deviation) > 0.7 * DISTINCTION_RATIO) + { + return false; + } + + // since the angle and allowed match, we compare road categories. Passing a low priority + // road allows us to consider it non obvious + if (distinct_by_class(road)) + { + return false; + } + + // switching the general road class within a turn is not a likely maneuver. We consider + // a turn distinct enough (given it's straight/narrow continue), if it's road class + // differs from other turns (and is of a lesser category) + if ((getRoadGroup(via_edge_data.flags.road_classification) != + getRoadGroup(compare_data.flags.road_classification)) && + (via_edge_data.flags.road_classification.GetPriority() == + candidate_data.flags.road_classification.GetPriority())) + return false; + + return true; + }; + + auto const itr = + std::find_if(intersection.begin() + 1, intersection.end(), is_similar_turn); + return itr == intersection.end(); + } + else + { + // deviation is larger than NARROW_TURN_ANGLE0 here for the candidate + + // check if there is any turn, that might look just as obvious, even though it might not be + // allowed. Entry-allowed isn't considered a valid distinction criterion here + auto const is_similar_turn = [&](auto const &road) { + // skip over our candidate + if (road.eid == candidate.eid) + return false; + + // we do not consider roads of far lesser category to be more obvious + const auto &compare_data = node_based_graph.GetEdgeData(road.eid); + /* + if (strictlyLess(compare_data.flags.road_classification, + candidate_data.flags.road_classification)) + { + std::cout << "Road class is strictly less" << std::endl; + return false; + } + */ + + // if the class is just not on the same level + if (distinct_by_class(road) && !override_class_by_lanes(compare_data)) + { + return false; + } + + // just as above, switching the general road class within a turn is not a likely + // maneuver. We consider + // a turn distinct enough (given it's straight/narrow continue), if it's road class + // differs from other turns. However, the difference in angles between the two needs to + // be reasonable as well. When coming down to tertiary and less, road groups are more or + // less random + if (util::angularDeviation(road.angle, candidate.angle) < 100 && + via_edge_data.flags.road_classification.GetPriority() <= + extractor::RoadPriorityClass::SECONDARY && + ((getRoadGroup(via_edge_data.flags.road_classification) != + getRoadGroup(compare_data.flags.road_classification)) && + (via_edge_data.flags.road_classification.GetPriority() == + candidate_data.flags.road_classification.GetPriority())) && + !override_class_by_lanes(compare_data) && + (via_edge_data.flags.road_classification.GetPriority() != + extractor::RoadPriorityClass::UNCLASSIFIED) && + (compare_data.flags.road_classification.GetPriority() != + extractor::RoadPriorityClass::UNCLASSIFIED)) + { + return false; + } + + // if the turn is much stronger, we are also fine (note that we do not have to check + // absolutes, since candidate is at least > NARROW_TURN_ANGLE + const auto compare_deviation = util::angularDeviation(road.angle, STRAIGHT_ANGLE); + if (compare_deviation / candidate_deviation > DISTINCTION_RATIO) + { + return false; + } + + return true; + }; + + return std::find_if(intersection.begin() + 1, intersection.end(), is_similar_turn) == + intersection.end(); + } +} + +template // works with Intersection and IntersectionView +inline bool IntersectionHandler::IsDistinctContinue(const std::size_t index, + const EdgeID via_edge, + const IntersectionType &intersection) const +{ + // if its good enough for a turn, it's good enough for a continue + if (IsDistinctTurn(index, via_edge, intersection)) + return true; + + auto const in_classification = node_based_graph.GetEdgeData(via_edge).flags.road_classification; + auto const continue_classification = + node_based_graph.GetEdgeData(intersection[index].eid).flags.road_classification; + + // nearly straight on the same road type + if (in_classification.GetPriority() == continue_classification.GetPriority() && + util::angularDeviation(intersection[index].angle, STRAIGHT_ANGLE) < + MAXIMAL_ALLOWED_NO_TURN_DEVIATION) + return true; + + return false; +} + +// Impl. template // works with Intersection and IntersectionView std::size_t IntersectionHandler::findObviousTurn(const EdgeID via_edge, const IntersectionType &intersection) const +{ + auto obvious_old = findObviousTurnOld(via_edge, intersection); + auto obvious_new = findObviousTurnNew(via_edge, intersection); + // if (obvious_new != obvious_old) + // { + // std::cout << "via_edge==" << via_edge << " old " << obvious_old << " new " << + // obvious_new + // << "\n"; + // BOOST_ASSERT(false); + // } + (void)obvious_old; + return obvious_new; +} + +template // works with Intersection and IntersectionView +std::size_t IntersectionHandler::findObviousTurnNew(const EdgeID via_edge, + const IntersectionType &intersection) const +{ + + // no obvious road + if (intersection.size() == 1) + return 0; + + // a single non u-turn is obvious + if (intersection.size() == 2) + return 1; + + // the way we are coming from + auto const &via_edge_data = node_based_graph.GetEdgeData(via_edge); + auto const &via_edge_annotation = + node_data_container.GetAnnotation(via_edge_data.annotation_data); + + // implement a filter, taking out all roads of lower class or different names + auto const continues_on_name_with_higher_class = [&](auto const &road) { + // it needs to be possible to enter the road + if (!road.entry_allowed) + return true; + + // to continue on a name, we need to have one first + if (via_edge_annotation.name_id == EMPTY_NAMEID && + !via_edge_data.flags.road_classification.IsLowPriorityRoadClass()) + return true; + + // and we cannot yloose it (roads loosing their name will be handled after this check here) + auto const &road_data = node_based_graph.GetEdgeData(road.eid); + const auto &road_annotation = node_data_container.GetAnnotation(road_data.annotation_data); + if (road_annotation.name_id == EMPTY_NAMEID && + !road_data.flags.road_classification.IsLowPriorityRoadClass()) + return true; + + // if not both of the entries are empty, we do not consider this a continue + if ((via_edge_annotation.name_id == EMPTY_NAMEID) ^ + (road_annotation.name_id == EMPTY_NAMEID)) + return true; + + // the priority can only stay the same or increase. We don't consider a primary->residential + // or residential->service as a continuing road + if (strictlyLess(road_data.flags.road_classification, + via_edge_data.flags.road_classification)) + return true; + + // filter out link classes to our current class, since they should only be connectivity + if (isLinkTo(road_data.flags.road_classification, via_edge_data.flags.road_classification)) + return true; + + // most expensive check last (since we filter, we check whether the name changes + return util::guidance::requiresNameAnnounced(via_edge_annotation.name_id, + road_annotation.name_id, + name_table, + street_name_suffix_table); + }; + + // check if the current road continues at a given index + auto const road_continues_itr = + intersection.findClosestTurn(STRAIGHT_ANGLE, continues_on_name_with_higher_class); + + // this check is not part of the main conditions, so that if the turn looks obvious from all + // other perspectives, a mode change will not result in different classification + auto const to_index_if_valid = [&](auto const iterator) -> std::size_t { + auto const &from_data = node_based_graph.GetEdgeData(via_edge); + auto const &to_data = node_based_graph.GetEdgeData(iterator->eid); + + if (from_data.flags.roundabout != to_data.flags.roundabout) + return 0; + + auto const from_mode = + node_data_container.GetAnnotation(from_data.annotation_data).travel_mode; + auto const to_mode = node_data_container.GetAnnotation(to_data.annotation_data).travel_mode; + + if (from_mode == to_mode) + return std::distance(intersection.begin(), iterator); + else + return 0; + }; + + // in case the continuing road is distinct, we prefer continuing on the current road. Only if + // continue does not exist or we are not distinct, we look for other possible candidates + if (road_continues_itr != intersection.end() && + IsDistinctContinue( + std::distance(intersection.begin(), road_continues_itr), via_edge, intersection)) + { + return to_index_if_valid(road_continues_itr); + } + + // The road doesn't continue in an obvious fashion. At least we see the start of a new road + // here, which might be more obvious than (for example) a turning road of the same name. The + // next goal is to find a road which is going more or less straight, but is also a matching + // category. So if we are on a primary that has an alley right ahead, the alley will not + // quality. But if primary goes straight onto secondary / turns left into primary. We would + // consider the secondary a candidate. + + // opposed to before, we do not care about name changes, again: this is a filter, so internal + // false/true will be negated for selection + auto const valid_of_higher_or_same_category = [&](auto const &road) { + if (!road.entry_allowed) + return true; + + auto const &road_data = node_based_graph.GetEdgeData(road.eid); + if (strictlyLess(road_data.flags.road_classification, + via_edge_data.flags.road_classification)) + return true; + + if (isLinkTo(road_data.flags.road_classification, via_edge_data.flags.road_classification)) + return true; + + return false; + }; + + // check for roads that allow entry only + auto const straightmost_turn_itr = + intersection.findClosestTurn(STRAIGHT_ANGLE, valid_of_higher_or_same_category); + + if (straightmost_turn_itr != intersection.end() && + IsDistinctTurn( + std::distance(intersection.begin(), straightmost_turn_itr), via_edge, intersection)) + { + return to_index_if_valid(straightmost_turn_itr); + } + + auto const valid_turn = [&](auto const &road) { return !road.entry_allowed; }; + + // we cannot find a turn of same or higher priority, so we check if any straightmost turn could + // be obvious. We only consider somewhat narrow turns for these cases though + auto const straightmost_valid = intersection.findClosestTurn(STRAIGHT_ANGLE, valid_turn); + // no valid turns + if (straightmost_valid == intersection.end()) + return 0; + + auto const non_sharp_turns = intersection.Count( + [&](auto const &road) { return util::angularDeviation(road.angle, STRAIGHT_ANGLE) <= 90; }); + auto const straight_is_only_non_sharp = + (util::angularDeviation(straightmost_valid->angle, STRAIGHT_ANGLE) <= 90) && + (non_sharp_turns == 1); + + if ((straightmost_valid != straightmost_turn_itr) && + (straightmost_valid != intersection.end()) && + (util::angularDeviation(STRAIGHT_ANGLE, straightmost_valid->angle) <= GROUP_ANGLE || + straight_is_only_non_sharp) && + !node_based_graph.GetEdgeData(straightmost_valid->eid) + .flags.road_classification.IsLowPriorityRoadClass() && + IsDistinctTurn( + std::distance(intersection.begin(), straightmost_valid), via_edge, intersection)) + { + return to_index_if_valid(straightmost_valid); + } + + // special case handling for motorways, for which nearly narrow / only allowed turns are always + // obvious + if (node_based_graph.GetEdgeData(straightmost_valid->eid) + .flags.road_classification.IsMotorwayClass() && + util::angularDeviation(straightmost_valid->angle, STRAIGHT_ANGLE) <= GROUP_ANGLE && + intersection.countEnterable() == 1) + { + return to_index_if_valid(straightmost_valid); + } + + // Special case handling for roads splitting up, all the same name (exactly the same) + if (intersection.size() == 3 && + std::all_of(intersection.begin(), + intersection.end(), + [ id = via_edge_annotation.name_id, this ](auto const &road) { + auto const data_id = node_based_graph.GetEdgeData(road.eid).annotation_data; + auto const name_id = node_data_container.GetAnnotation(data_id).name_id; + return (name_id != EMPTY_NAMEID) && (name_id == id); + }) && + intersection.countEnterable() == 1 && + // ensure that we do not lookt at a end of the road turn in a segregated intersection + (util::angularDeviation(intersection[1].angle, 90) > NARROW_TURN_ANGLE || + util::angularDeviation(intersection[2].angle, 270) > NARROW_TURN_ANGLE)) + { + return to_index_if_valid(straightmost_valid); + } + + return 0; +} + +template // works with Intersection and IntersectionView +std::size_t IntersectionHandler::findObviousTurnOld(const EdgeID via_edge, + const IntersectionType &intersection) const { using Road = typename IntersectionType::value_type; using osrm::util::angularDeviation; diff --git a/profiles/lib/guidance.lua b/profiles/lib/guidance.lua index 6c25ed69b..d920a9c6c 100644 --- a/profiles/lib/guidance.lua +++ b/profiles/lib/guidance.lua @@ -6,19 +6,19 @@ local Guidance = {} -- Guidance: Default Mapping from roads to types/priorities highway_classes = { motorway = road_priority_class.motorway, - motorway_link = road_priority_class.link_road, + motorway_link = road_priority_class.motorway_link, trunk = road_priority_class.trunk, - trunk_link = road_priority_class.link_road, + trunk_link = road_priority_class.trunk_link, primary = road_priority_class.primary, - primary_link = road_priority_class.link_road, + primary_link = road_priority_class.primary_link, secondary = road_priority_class.secondary, - secondary_link = road_priority_class.link_road, + secondary_link = road_priority_class.secondary_link, tertiary = road_priority_class.tertiary, - tertiary_link = road_priority_class.link_road, - unclassified = road_priority_class.side_residential, - residential = road_priority_class.side_residential, - service = road_priority_class.connectivity, - living_street = road_priority_class.main_residential, + tertiary_link = road_priority_class.tertiary_link, + unclassified = road_priority_class.unclassified, + residential = road_priority_class.main_residential, + service = road_priority_class.alley, + living_street = road_priority_class.side_residential, track = road_priority_class.bike_path, path = road_priority_class.bike_path, footway = road_priority_class.foot_path, @@ -60,6 +60,14 @@ link_types = Set { 'tertiary_link' } +-- roads like parking lots are very unimportant for normal driving +parking_class = Set{ + 'parking_aisle', + 'driveway', + 'drive-through', + 'emergency_access' +} + function Guidance.set_classification (highway, result, input_way) if motorway_types[highway] then result.road_classification.motorway_class = true; @@ -67,10 +75,30 @@ function Guidance.set_classification (highway, result, input_way) if link_types[highway] then result.road_classification.link_class = true; end - if highway_classes[highway] ~= nil then - result.road_classification.road_priority_class = highway_classes[highway] + + -- we distinguish between different service types, if specified, we recognise parking and alleys. + -- If we see an unrecognised type, we assume a pure connectivity road. All unspecified are recognised as alley + if highway ~= nil and highway == 'service' then + local service_type = input_way:get_value_by_key('service'); + if service_type ~= nil and parking_class[service_type] then + result.road_classification.road_priority_class = road_priority_class.parking; + else + if service_type ~= nil and service_type == 'alley' then + result.road_classification.road_priority_class = road_priority_class.alley; + else + if serice_type == nil then + result.road_classification.road_priority_class = road_priority_class.alley; + else + result.road_classification.road_priority_class = highway_classes[highway] + end + end + end else - result.road_classification.road_priority_class = default_highway_class + if highway_classes[highway] ~= nil then + result.road_classification.road_priority_class = highway_classes[highway] + else + result.road_classification.road_priority_class = default_highway_class + end end if road_types[highway] then result.road_classification.may_be_ignored = false; @@ -137,7 +165,7 @@ function Guidance.get_turn_lanes(way,data) local psv_fw, psv_bw = get_psv_counts(way,data) local turn_lanes_fw, turn_lanes_bw = Tags.get_forward_backward_by_key(way,data,'turn:lanes') local vehicle_lanes_fw, vehicle_lanes_bw = Tags.get_forward_backward_by_key(way,data,'vehicle:lanes') - + --note: backward lanes swap psv_bw and psv_fw return process_lanes(turn_lanes_fw,vehicle_lanes_fw,psv_bw,psv_fw) or turn_lanes, process_lanes(turn_lanes_bw,vehicle_lanes_bw,psv_fw,psv_bw) or turn_lanes diff --git a/src/extractor/scripting_environment_lua.cpp b/src/extractor/scripting_environment_lua.cpp index e4513d411..ad7cb3b73 100644 --- a/src/extractor/scripting_environment_lua.cpp +++ b/src/extractor/scripting_environment_lua.cpp @@ -140,20 +140,36 @@ void Sol2ScriptingEnvironment::InitContext(LuaScriptingContext &context) context.state.new_enum("road_priority_class", "motorway", extractor::RoadPriorityClass::MOTORWAY, + "motorway_link", + extractor::RoadPriorityClass::MOTORWAY_LINK, "trunk", extractor::RoadPriorityClass::TRUNK, + "trunk_link", + extractor::RoadPriorityClass::TRUNK_LINK, "primary", extractor::RoadPriorityClass::PRIMARY, + "primary_link", + extractor::RoadPriorityClass::PRIMARY_LINK, "secondary", extractor::RoadPriorityClass::SECONDARY, + "secondary_link", + extractor::RoadPriorityClass::SECONDARY_LINK, "tertiary", extractor::RoadPriorityClass::TERTIARY, + "tertiary_link", + extractor::RoadPriorityClass::TERTIARY_LINK, "main_residential", extractor::RoadPriorityClass::MAIN_RESIDENTIAL, "side_residential", extractor::RoadPriorityClass::SIDE_RESIDENTIAL, + "alley", + extractor::RoadPriorityClass::ALLEY, + "parking", + extractor::RoadPriorityClass::PARKING, "link_road", extractor::RoadPriorityClass::LINK_ROAD, + "unclassified", + extractor::RoadPriorityClass::UNCLASSIFIED, "bike_path", extractor::RoadPriorityClass::BIKE_PATH, "foot_path", diff --git a/test/nodejs/constants.js b/test/nodejs/constants.js index 79aa90e6e..bd3c454cb 100644 --- a/test/nodejs/constants.js +++ b/test/nodejs/constants.js @@ -10,7 +10,7 @@ exports.three_test_coordinates = [[7.41337, 43.72956], exports.two_test_coordinates = exports.three_test_coordinates.slice(0, 2) -exports.test_tile = {'at': [17059, 11948, 15], 'size': 168605}; +exports.test_tile = {'at': [17059, 11948, 15], 'size': 168571}; // Test files generated by the routing engine; check test/data if (process.env.OSRM_DATA_PATH !== undefined) {