diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b909c33e..233e48a31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,12 +11,13 @@ - Guidance: - CHANGED #4929: Don't use obviousness for links bifurcations [#4929](https://github.com/Project-OSRM/osrm-backend/pull/4929) - FIXED #4929: Adjust Straight direction modifiers of side roads in driveway handler [#4929](https://github.com/Project-OSRM/osrm-backend/pull/4929) + - CHANGED #4925: Added post process logic to collapse segregated turn instructions [#4925](https://github.com/Project-OSRM/osrm-backend/pull/4925) - Tools: - `osrm-routed` accepts a new property `--memory_file` to store memory in a file on disk. - NodeJS: - `OSRM` object accepts a new option `memory_file` that stores the memory in a file on disk. - Internals - - CHANGED #4845 #4968: Updated segregated intersection identification + - CHANGED #4845 #4968: Updated segregated intersection identification [#4845](https://github.com/Project-OSRM/osrm-backend/pull/4845) [#4968](https://github.com/Project-OSRM/osrm-backend/pull/4968) - REMOVED: Remove `.timestamp` file since it was unused. - Documentation: - ADDED: Add documentation about OSM node ids in nearest service response [#4436](https://github.com/Project-OSRM/osrm-backend/pull/4436) diff --git a/features/car/conditional_restrictions.feature b/features/car/conditional_restrictions.feature index bbc6fde88..23a9e1efe 100644 --- a/features/car/conditional_restrictions.feature +++ b/features/car/conditional_restrictions.feature @@ -691,14 +691,14 @@ Feature: Car - Turn restrictions # """ Given the node locations | node | lat | lon | - | a | 38.9113 | -77.0091 | - | b | 38.9108 | -77.0091 | - | c | 38.9104 | -77.0091 | - | d | 38.9110 | -77.0096 | - | e | 38.9106 | -77.0086 | - | f | 38.9105 | -77.0090 | - | g | 38.9108 | -77.0090 | - | h | 38.9113 | -77.0090 | + | a | 38.91124 | -77.00909 | + | b | 38.91080 | -77.00909 | + | c | 38.91038 | -77.00909 | + | d | 38.91105 | -77.00967 | + | e | 38.91037 | -77.00807 | + | f | 38.91036 | -77.00899 | + | g | 38.91076 | -77.00901 | + | h | 38.91124 | -77.00900 | And the ways | nodes | oneway | name | @@ -719,7 +719,7 @@ Feature: Car - Turn restrictions When I route I should get | from | to | route | turns | | a | e | cap south,florida nw,florida nw,florida ne | depart,turn right,continue uturn,arrive | - | f | d | cap north,florida,florida nw | depart,turn left,arrive | + | f | d | cap north,florida nw,florida nw | depart,turn left,arrive | | e | c | florida ne,florida nw,cap south,cap south | depart,continue uturn,turn right,arrive | @no_turning @conditionals @@ -738,14 +738,14 @@ Feature: Car - Turn restrictions # """ Given the node locations | node | lat | lon | - | a | 38.9113 | -77.0091 | - | b | 38.9108 | -77.0091 | - | c | 38.9104 | -77.0091 | - | d | 38.9110 | -77.0096 | - | e | 38.9106 | -77.0086 | - | f | 38.9105 | -77.0090 | - | g | 38.9108 | -77.0090 | - | h | 38.9113 | -77.0090 | + | a | 38.91124 | -77.00909 | + | b | 38.91080 | -77.00909 | + | c | 38.91038 | -77.00909 | + | d | 38.91105 | -77.00967 | + | e | 38.91037 | -77.00807 | + | f | 38.91036 | -77.00899 | + | g | 38.91076 | -77.00901 | + | h | 38.91124 | -77.00900 | And the ways | nodes | oneway | name | @@ -765,7 +765,7 @@ Feature: Car - Turn restrictions When I route I should get | from | to | route | turns | - | a | e | cap south,florida,florida ne | depart,turn left,arrive | + | a | e | cap south,florida ne,florida ne | depart,turn left,arrive | | f | d | cap north,florida ne,florida ne,florida nw | depart,turn sharp right,continue uturn,arrive | | e | c | florida ne,cap south,cap south | depart,turn left,arrive | diff --git a/features/guidance/collapse.feature b/features/guidance/collapse.feature index ecc6e03aa..e7b4dad59 100644 --- a/features/guidance/collapse.feature +++ b/features/guidance/collapse.feature @@ -35,20 +35,20 @@ Feature: Collapse | waypoints | route | turns | locations | | a,l | first,second,second | depart,turn right,arrive | a,b,l | | a,d | first,first | depart,arrive | a,d | - | a,j | first,second,second | depart,turn left,arrive | a,c,j | - | a,h | first,first,first | depart,continue uturn,arrive | a,c,h | + | a,j | first,second,second | depart,turn left,arrive | a,b,j | + | a,h | first,first,first | depart,continue uturn,arrive | a,b,h | | e,j | first,second,second | depart,turn right,arrive | e,f,j | | e,h | first,first | depart,arrive | e,h | - | e,l | first,second,second | depart,turn left,arrive | e,g,l | - | e,d | first,first,first | depart,continue uturn,arrive | e,g,d | + | e,l | first,second,second | depart,turn left,arrive | e,f,l | + | e,d | first,first,first | depart,continue uturn,arrive | e,f,d | | k,h | second,first,first | depart,turn right,arrive | k,g,h | | k,l | second,second | depart,arrive | k,l | - | k,d | second,first,first | depart,turn left,arrive | k,b,d | - | k,j | second,second,second | depart,continue uturn,arrive | k,b,j | + | k,d | second,first,first | depart,turn left,arrive | k,g,d | + | k,j | second,second,second | depart,continue uturn,arrive | k,g,j | | i,d | second,first,first | depart,turn right,arrive | i,c,d | | i,j | second,second | depart,arrive | i,j | - | i,h | second,first,first | depart,turn left,arrive | i,f,h | - | i,l | second,second,second | depart,continue uturn,arrive | i,f,l | + | i,h | second,first,first | depart,turn left,arrive | i,c,h | + | i,l | second,second,second | depart,continue uturn,arrive | i,c,l | Scenario: Segregated Intersection, Cross Belonging to Single Street Given the node map @@ -74,7 +74,7 @@ Feature: Collapse When I route I should get | waypoints | route | turns | locations | - | a,i | first,second,third,third | depart,turn left,turn slight left,arrive | a,b,e,i | + | a,i | first,third,third | depart,turn sharp left,arrive | a,b,i | Scenario: Segregated Intersection, Cross Belonging to Correct Street Given the node map @@ -106,20 +106,20 @@ Feature: Collapse | waypoints | route | turns | locations | | a,l | first,second,second | depart,turn right,arrive | a,b,l | | a,d | first,first | depart,arrive | a,d | - | a,j | first,second,second | depart,turn left,arrive | a,c,j | - | a,h | first,first,first | depart,continue uturn,arrive | a,c,h | + | a,j | first,second,second | depart,turn left,arrive | a,b,j | + | a,h | first,first,first | depart,continue uturn,arrive | a,b,h | | e,j | first,second,second | depart,turn right,arrive | e,f,j | | e,h | first,first | depart,arrive | e,h | - | e,l | first,second,second | depart,turn left,arrive | e,g,l | - | e,d | first,first,first | depart,continue uturn,arrive | e,g,d | + | e,l | first,second,second | depart,turn left,arrive | e,f,l | + | e,d | first,first,first | depart,continue uturn,arrive | e,f,d | | k,h | second,first,first | depart,turn right,arrive | k,g,h | | k,l | second,second | depart,arrive | k,l | - | k,d | second,first,first | depart,turn left,arrive | k,b,d | - | k,j | second,second,second | depart,continue uturn,arrive | k,b,j | + | k,d | second,first,first | depart,turn left,arrive | k,g,d | + | k,j | second,second,second | depart,continue uturn,arrive | k,g,j | | i,d | second,first,first | depart,turn right,arrive | i,c,d | | i,j | second,second | depart,arrive | i,j | - | i,h | second,first,first | depart,turn left,arrive | i,f,h | - | i,l | second,second,second | depart,continue uturn,arrive | i,f,l | + | i,h | second,first,first | depart,turn left,arrive | i,c,h | + | i,l | second,second,second | depart,continue uturn,arrive | i,c,l | Scenario: Segregated Intersection, Cross Belonging to Mixed Streets Given the node map @@ -151,20 +151,20 @@ Feature: Collapse | waypoints | route | turns | locations | | a,l | first,second,second | depart,turn right,arrive | a,b,l | | a,d | first,first | depart,arrive | a,d | - | a,j | first,second,second | depart,turn left,arrive | a,c,j | - | a,h | first,first,first | depart,continue uturn,arrive | a,c,h | + | a,j | first,second,second | depart,turn left,arrive | a,b,j | + | a,h | first,first,first | depart,continue uturn,arrive | a,b,h | | e,j | first,second,second | depart,turn right,arrive | e,f,j | | e,h | first,first | depart,arrive | e,h | - | e,l | first,second,second | depart,turn left,arrive | e,g,l | - | e,d | first,first,first | depart,continue uturn,arrive | e,g,d | + | e,l | first,second,second | depart,turn left,arrive | e,f,l | + | e,d | first,first,first | depart,continue uturn,arrive | e,f,d | | k,h | second,first,first | depart,turn right,arrive | k,g,h | | k,l | second,second | depart,arrive | k,l | - | k,d | second,first,first | depart,turn left,arrive | k,b,d | - | k,j | second,second,second | depart,continue uturn,arrive | k,b,j | + | k,d | second,first,first | depart,turn left,arrive | k,g,d | + | k,j | second,second,second | depart,continue uturn,arrive | k,g,j | | i,d | second,first,first | depart,turn right,arrive | i,c,d | | i,j | second,second | depart,arrive | i,j | - | i,h | second,first,first | depart,turn left,arrive | i,f,h | - | i,l | second,second,second | depart,continue uturn,arrive | i,f,l | + | i,h | second,first,first | depart,turn left,arrive | i,c,h | + | i,l | second,second,second | depart,continue uturn,arrive | i,c,l | Scenario: Partly Segregated Intersection, Two Segregated Roads Given the node map @@ -209,11 +209,11 @@ Feature: Collapse | d,c | first,first,first | depart,continue uturn,arrive | d,e,c | | g,c | second,first,first | depart,turn right,arrive | g,b,c | | g,j | second,second | depart,arrive | g,j | - | g,f | second,first,first | depart,turn left,arrive | g,e,f | + | g,f | second,first,first | depart,turn left,arrive | g,b,f | | g,h | second,second,second | depart,continue uturn,arrive | g,b,h | | i,f | second,first,first | depart,turn right,arrive | i,e,f | | i,h | second,second | depart,arrive | i,h | - | i,c | second,first,first | depart,turn left,arrive | i,b,c | + | i,c | second,first,first | depart,turn left,arrive | i,e,c | | i,j | second,second,second | depart,continue uturn,arrive | i,e,j | Scenario: Partly Segregated Intersection, Two Segregated Roads, Intersection belongs to Second @@ -263,11 +263,11 @@ Feature: Collapse | d,c | first,first,first | depart,continue uturn,arrive | d,e,c | | g,c | second,first,first | depart,turn right,arrive | g,b,c | | g,j | second,second | depart,arrive | g,j | - | g,f | second,first,first | depart,turn left,arrive | g,e,f | + | g,f | second,first,first | depart,turn left,arrive | g,b,f | | g,h | second,second,second | depart,continue uturn,arrive | g,b,h | | i,f | second,first,first | depart,turn right,arrive | i,e,f | | i,h | second,second | depart,arrive | i,h | - | i,c | second,first,first | depart,turn left,arrive | i,b,c | + | i,c | second,first,first | depart,turn left,arrive | i,e,c | | i,j | second,second,second | depart,continue uturn,arrive | i,e,j | Scenario: Segregated Intersection, Cross Belonging to Mixed Streets - Slight Angles @@ -300,20 +300,20 @@ Feature: Collapse | waypoints | route | turns | locations | | a,l | first,second,second | depart,turn right,arrive | a,b,l | | a,d | first,first | depart,arrive | a,d | - | a,j | first,second,second | depart,turn left,arrive | a,c,j | - | a,h | first,first,first | depart,continue uturn,arrive | a,c,h | + | a,j | first,second,second | depart,turn left,arrive | a,b,j | + | a,h | first,first,first | depart,continue uturn,arrive | a,b,h | | e,j | first,second,second | depart,turn right,arrive | e,f,j | | e,h | first,first | depart,arrive | e,h | - | e,l | first,second,second | depart,turn left,arrive | e,g,l | - | e,d | first,first,first | depart,continue uturn,arrive | e,g,d | + | e,l | first,second,second | depart,turn left,arrive | e,f,l | + | e,d | first,first,first | depart,continue uturn,arrive | e,f,d | | k,h | second,first,first | depart,turn right,arrive | k,g,h | | k,l | second,second | depart,arrive | k,l | - | k,d | second,first,first | depart,turn left,arrive | k,b,d | - | k,j | second,second,second | depart,continue uturn,arrive | k,b,j | + | k,d | second,first,first | depart,turn left,arrive | k,g,d | + | k,j | second,second,second | depart,continue uturn,arrive | k,g,j | | i,d | second,first,first | depart,turn right,arrive | i,c,d | | i,j | second,second | depart,arrive | i,j | - | i,h | second,first,first | depart,turn left,arrive | i,f,h | - | i,l | second,second,second | depart,continue uturn,arrive | i,f,l | + | i,h | second,first,first | depart,turn left,arrive | i,c,h | + | i,l | second,second,second | depart,continue uturn,arrive | i,c,l | Scenario: Segregated Intersection, Cross Belonging to Mixed Streets - Slight Angles (2) Given the node map @@ -345,28 +345,29 @@ Feature: Collapse | waypoints | route | turns | locations | | a,l | first,second,second | depart,turn right,arrive | a,b,l | | a,d | first,first | depart,arrive | a,d | - | a,j | first,second,second | depart,turn left,arrive | a,c,j | - | a,h | first,first,first | depart,continue uturn,arrive | a,c,h | + | a,j | first,second,second | depart,turn left,arrive | a,b,j | + | a,h | first,first,first | depart,continue uturn,arrive | a,b,h | | e,j | first,second,second | depart,turn right,arrive | e,f,j | | e,h | first,first | depart,arrive | e,h | - | e,l | first,second,second | depart,turn left,arrive | e,g,l | - | e,d | first,first,first | depart,continue uturn,arrive | e,g,d | + | e,l | first,second,second | depart,turn left,arrive | e,f,l | + | e,d | first,first,first | depart,continue uturn,arrive | e,f,d | | k,h | second,first,first | depart,turn right,arrive | k,g,h | | k,l | second,second | depart,arrive | k,l | - | k,d | second,first,first | depart,turn left,arrive | k,b,d | - | k,j | second,second,second | depart,continue uturn,arrive | k,b,j | + | k,d | second,first,first | depart,turn left,arrive | k,g,d | + | k,j | second,second,second | depart,continue uturn,arrive | k,g,j | | i,d | second,first,first | depart,turn right,arrive | i,c,d | | i,j | second,second | depart,arrive | i,j | - | i,h | second,first,first | depart,turn left,arrive | i,f,h | - | i,l | second,second,second | depart,continue uturn,arrive | i,f,l | + | i,h | second,first,first | depart,turn left,arrive | i,c,h | + | i,l | second,second,second | depart,continue uturn,arrive | i,c,l | Scenario: Entering a segregated road Given the node map """ - a f g - | | . ' - b-e ' - / / + h + a f | g + | | i ' + b-e ' | + / / j / / c d """ @@ -376,7 +377,8 @@ Feature: Collapse | abc | primary | first | yes | | def | primary | first | yes | | be | primary | first | no | - | ge | primary | second | no | + | gie | primary | second | no | + | hij | primary | maple | no | When I route I should get | waypoints | route | turns | locations | @@ -385,7 +387,7 @@ Feature: Collapse | a,g | first,second,second | depart,turn left,arrive | a,b,g | | d,g | first,second,second | depart,turn right,arrive | d,e,g | | g,f | second,first,first | depart,turn right,arrive | g,e,f | - | g,c | second,first,first | depart,end of road left,arrive | g,b,c | + | g,c | second,first,first | depart,end of road left,arrive | g,e,c | Scenario: Do not collapse turning roads Given the node map @@ -603,7 +605,7 @@ Feature: Collapse When I route I should get | waypoints | route | turns | locations | - | i,h | in,road,road | depart,turn left,arrive | i,f,h | + | i,h | in,road,road | depart,turn slight left,arrive | i,c,h | | a,d | road,road | depart,arrive | a,d | | a,j | road,out,out | depart,turn slight right,arrive | a,b,j | diff --git a/features/guidance/divided-highways.feature b/features/guidance/divided-highways.feature index 6bfbca34d..2824293bc 100644 --- a/features/guidance/divided-highways.feature +++ b/features/guidance/divided-highways.feature @@ -13,19 +13,23 @@ Feature: Divided road entry d-------e-----f | | - g + i---g---j + | + | + h """ And the ways - | nodes | name | highway | oneway | - | abc | main st | residential | -1 | - | def | main st | residential | yes | - | be | main st | residential | | - | eg | side st | residential | | + | nodes | name | highway | oneway | + | abc | main st | residential | -1 | + | def | main st | residential | yes | + | be | main st | residential | | + | egh | side st | residential | | + | igj | maple st | residential | | When I route I should get | waypoints | route | turns | - | g,a | side st,main st,main st| depart,end of road left,arrive | + | h,a | side st,main st,main st| depart,end of road left,arrive | # Similar to previous one, but the joining way is tagged with the side-street name @@ -37,18 +41,22 @@ Feature: Divided road entry d-------e-----f | | - g + i---g---j + | + | + h """ And the ways - | nodes | name | highway | oneway | - | abc | main st | residential | -1 | - | def | main st | residential | yes | - | beg | side st | residential | | + | nodes | name | highway | oneway | + | abc | main st | residential | -1 | + | def | main st | residential | yes | + | begh | side st | residential | | + | igj | maple st | residential | | When I route I should get | waypoints | route | turns | - | g,a | side st,main st,main st| depart,end of road left,arrive | + | h,a | side st,main st,main st| depart,end of road left,arrive | # Center join named after crossroad @@ -100,3 +108,32 @@ Feature: Divided road entry When I route I should get | waypoints | route | turns | | g,a | side st,main st,main st| depart,turn left,arrive | + + # Verify end of road left turn across divided roads + Scenario: Join on a divided road, named after the side street + Given the node map + """ + a-----h--b-----c + | | + d-----i--e-----f + | | + | | + m---j--g---n + | | + | | + k l + """ + + And the ways + | nodes | name | highway | oneway | + | ahbc | main st | residential | -1 | + | dief | main st | residential | yes | + | begl | side st | residential | -1 | + | hijk | side st | residential | yes | + | mjgn | maple st| residential | no | + + When I route I should get + | waypoints | route | turns | + | l,a | side st,main st,main st| depart,end of road left,arrive | + + diff --git a/features/guidance/obvious-turn-discovery.feature b/features/guidance/obvious-turn-discovery.feature index c510bc790..414894e7c 100644 --- a/features/guidance/obvious-turn-discovery.feature +++ b/features/guidance/obvious-turn-discovery.feature @@ -1072,7 +1072,7 @@ Feature: Simple Turns When I route I should get | from | to | route | turns | | g | c | woll,brei,brei | depart,turn slight right,arrive | - | g | f | woll,scho,scho | depart,continue sharp left,arrive | + | g | f | woll,scho,scho | depart,turn sharp left,arrive | | a | c | scho,brei | depart,arrive | | d | f | brei,scho | depart,arrive | diff --git a/features/guidance/turn-lanes.feature b/features/guidance/turn-lanes.feature index bbc2a867a..f34774e9e 100644 --- a/features/guidance/turn-lanes.feature +++ b/features/guidance/turn-lanes.feature @@ -301,7 +301,7 @@ Feature: Turn Lane Guidance | e,l | road,cross,cross | depart,turn right,arrive | ,none:false straight:false straight;right:true, | | i,h | cross,road,road | depart,turn right,arrive | ,, | | i,j | cross,cross | depart,arrive | ;;left:false straight:true, | - | i,l | cross,cross,cross | depart,continue uturn,arrive | ;,left:true straight:false;left:true straight:false;left:false straight:true, | + | i,l | cross,cross,cross | depart,continue uturn,arrive | ,left:true straight:false;left:true straight:false;left:true straight:false;left:false straight:true, | @partition-lanes Scenario: Turn Lanes at Segregated Road @@ -937,48 +937,52 @@ Feature: Turn Lane Guidance Scenario: Partitioned turn, Slight Curve - maxspeed Given the node map """ - f e - | | - | | - | c - a - b ' | - g d + f e + | | + i | | + | | c + h - a - b ' | + j g d """ And the ways | nodes | name | highway | oneway | turn:lanes:forward | maxspeed | + | ha | road | primary | yes | | 1 | | ab | road | primary | yes | left\|right | 1 | | bc | cross | primary | yes | | 1 | | fbg | cross | primary | yes | | 1 | | dce | cross | primary | yes | | 1 | + | iaj | kross | primary | no | | 1 | When I route I should get | waypoints | route | turns | lanes | locations | - | a,g | road,cross,cross | depart,turn right,arrive | ,left:false right:true, | a,b,g | - | a,e | road,cross,cross | depart,end of road left,arrive | ;left:true right:false,left:true right:false, | a,c,e | + | h,g | road,cross,cross | depart,turn right,arrive | ;,left:false right:true, | h,b,g | + | h,e | road,cross,cross | depart,end of road left,arrive | ;,left:true right:false;left:true right:false, | h,b,e | Scenario: Partitioned turn, Slight Curve Given the node map """ - f e - | | - | | - | c - a - b ' | - g d + f e + | | + i | | + | | c + h - a - b ' | + j g d """ And the ways | nodes | name | highway | oneway | turn:lanes:forward | + | ha | road | primary | yes | | | ab | road | primary | yes | left\|right | | bc | cross | primary | yes | | | fbg | cross | primary | yes | | | dce | cross | primary | yes | | + | iaj | kross | primary | no | | When I route I should get - | waypoints | route | turns | lanes | locations | - | a,g | road,cross,cross | depart,turn right,arrive | ,left:false right:true, | a,b,g | - | a,e | road,cross,cross | depart,end of road left,arrive | ;left:true right:false,left:true right:false, | a,c,e | + | waypoints | route | turns | lanes | locations | + | h,g | road,cross,cross | depart,turn right,arrive | ;,left:false right:true, | h,b,g | + | h,e | road,cross,cross | depart,end of road left,arrive | ;,left:true right:false;left:true right:false, | h,b,e | Scenario: Lane Parsing Issue #2694 Given the node map @@ -1244,4 +1248,4 @@ Feature: Turn Lane Guidance When I route I should get | waypoints | route | turns | lanes | locations | - | a,f | road1,road1,road1 | depart,continue uturn,arrive | ;left:false straight:true straight;right:false,left:true straight:false straight;right:false;;, | a,d,f | + | a,f | road1,road1,road1 | depart,continue uturn,arrive | ,;left:true straight:false straight;right:false;;, | a,c,f | diff --git a/include/engine/api/route_api.hpp b/include/engine/api/route_api.hpp index 285bc5dfc..1e6c97647 100644 --- a/include/engine/api/route_api.hpp +++ b/include/engine/api/route_api.hpp @@ -148,6 +148,9 @@ class RouteAPI : public BaseAPI // processing is performed guidance::applyOverrides(BaseAPI::facade, steps, leg_geometry); + // Collapse segregated steps before others + steps = guidance::collapseSegregatedTurnInstructions(std::move(steps)); + /* Perform step-based post-processing. * * Using post-processing on basis of route-steps for a single leg at a time diff --git a/include/engine/guidance/collapse_turns.hpp b/include/engine/guidance/collapse_turns.hpp index 5ad5f47e3..b8b5d3198 100644 --- a/include/engine/guidance/collapse_turns.hpp +++ b/include/engine/guidance/collapse_turns.hpp @@ -13,13 +13,19 @@ namespace engine namespace guidance { +// Multiple possible reasons can result in unnecessary/confusing instructions +// Collapsing such turns into a single turn instruction, we give a clearer +// set of instructions that is not cluttered by unnecessary turns/name changes. +OSRM_ATTR_WARN_UNUSED +std::vector collapseTurnInstructions(std::vector steps); + // Multiple possible reasons can result in unnecessary/confusing instructions // A prime example would be a segregated intersection. Turning around at this // intersection would result in two instructions to turn left. // Collapsing such turns into a single turn instruction, we give a clearer -// set of instructionst that is not cluttered by unnecessary turns/name changes. +// set of instructions that is not cluttered by unnecessary turns/name changes. OSRM_ATTR_WARN_UNUSED -std::vector collapseTurnInstructions(std::vector steps); +std::vector collapseSegregatedTurnInstructions(std::vector steps); // A combined turn is a set of two instructions that actually form a single turn, as far as we // perceive it. A u-turn consisting of two left turns is one such example. But there are also lots @@ -93,6 +99,22 @@ struct StaggeredTurnStrategy : CombineStrategy const RouteStep &step_prior_to_intersection; }; +// Handling of consecutive segregated steps +struct CombineSegregatedStepsStrategy : CombineStrategy +{ + void operator()(RouteStep &step_at_turn_location, const RouteStep &transfer_from_step) const; +}; + +// Handling of segregated intersections +struct SegregatedTurnStrategy : CombineStrategy +{ + SegregatedTurnStrategy(const RouteStep &step_prior_to_intersection); + + void operator()(RouteStep &step_at_turn_location, const RouteStep &transfer_from_step) const; + + const RouteStep &step_prior_to_intersection; +}; + // Signage Strategies // Transfer the signage from the next step onto this step diff --git a/include/engine/guidance/collapsing_utility.hpp b/include/engine/guidance/collapsing_utility.hpp index b8760a7a8..51ed8999b 100644 --- a/include/engine/guidance/collapsing_utility.hpp +++ b/include/engine/guidance/collapsing_utility.hpp @@ -23,6 +23,7 @@ const constexpr std::size_t MIN_END_OF_ROAD_INTERSECTIONS = std::size_t{2}; const constexpr double MAX_COLLAPSE_DISTANCE = 30.0; // a bit larger than 100 to avoid oscillation in tests const constexpr double NAME_SEGMENT_CUTOFF_LENGTH = 105.0; +const double constexpr STRAIGHT_ANGLE = 180.; // check if a step is completely without turn type inline bool hasTurnType(const RouteStep &step) @@ -104,6 +105,12 @@ inline void setInstructionType(RouteStep &step, const osrm::guidance::TurnType:: step.maneuver.instruction.type = type; } +// alias for readability +inline void setModifier(RouteStep &step, const osrm::guidance::DirectionModifier::Enum modifier) +{ + step.maneuver.instruction.direction_modifier = modifier; +} + // alias for readability inline bool haveSameMode(const RouteStep &lhs, const RouteStep &rhs) { @@ -215,6 +222,86 @@ inline double totalTurnAngle(const RouteStep &entry_step, const RouteStep &exit_ return total_angle; } +// check bearings for u-turns. +// since bearings are wrapped around at 0 (we only support 0,360), we need to do some minor math to +// check if bearings `a` and `b` go in opposite directions. In general we accept some minor +// deviations for u-turns. +inline bool bearingsAreReversed(const double bearing_in, const double bearing_out) +{ + // Nearly perfectly reversed angles have a difference close to 180 degrees (straight) + const double left_turn_angle = [&]() { + if (0 <= bearing_out && bearing_out <= bearing_in) + return bearing_in - bearing_out; + return bearing_in + 360 - bearing_out; + }(); + return util::angularDeviation(left_turn_angle, 180) <= 35; +} + +// Returns true if the specified step has only one intersection +inline bool hasSingleIntersection(const RouteStep &step) +{ + return (step.intersections.size() == 1); +} + +// Returns true if the specified angle is a wider straight turn +inline bool isWiderStraight(const double angle) { return (angle >= 125 && angle <= 235); } + +// Returns the straightest intersecting edge turn for the specified step +inline double getStraightestIntersectingEdgeTurn(const RouteStep &step) +{ + const auto &intersection = step.intersections.front(); + const double bearing_in = util::bearing::reverse(intersection.bearings[intersection.in]); + double staightest_turn = 360.; + double staightest_delta = 360.; + + for (std::size_t i = 0; i < intersection.bearings.size(); ++i) + { + // Skip the in, out, and non-traversable edges + if ((i == intersection.in) || (i == intersection.out) || !intersection.entry.at(i)) + continue; + + double intersecting_turn = + util::bearing::angleBetween(bearing_in, intersection.bearings.at(i)); + double straight_delta = util::angularDeviation(intersecting_turn, STRAIGHT_ANGLE); + + if (straight_delta < staightest_delta) + { + staightest_delta = straight_delta; + staightest_turn = intersecting_turn; + } + } + return staightest_turn; +} + +// Returns true if the specified step has the straightest turn as compared to +// the intersecting edges +inline bool hasStraightestTurn(const RouteStep &step) +{ + const auto &intersection = step.intersections.front(); + const double path_turn = + util::bearing::angleBetween(util::bearing::reverse(intersection.bearings[intersection.in]), + intersection.bearings[intersection.out]); + + // Path turn must be a wider straight + if (isWiderStraight(path_turn)) + { + const double straightest_intersecting_turn = getStraightestIntersectingEdgeTurn(step); + const double path_straight_delta = util::angularDeviation(path_turn, STRAIGHT_ANGLE); + const double intersecting_straight_delta = + util::angularDeviation(straightest_intersecting_turn, STRAIGHT_ANGLE); + const double path_intersecting_delta = + util::angularDeviation(path_turn, straightest_intersecting_turn); + + // Add some fuzz - the delta between the path and intersecting turn must be greater + // than 10 in order to consider using the intersecting turn as the straightest + return ((path_intersecting_delta > 10.) + ? (path_straight_delta <= intersecting_straight_delta) + : true); + } + + return false; +} + } /* namespace guidance */ } /* namespace engine */ } /* namespace osrm */ diff --git a/src/engine/guidance/collapse_scenario_detection.cpp b/src/engine/guidance/collapse_scenario_detection.cpp index 7a984cc1e..27efe856d 100644 --- a/src/engine/guidance/collapse_scenario_detection.cpp +++ b/src/engine/guidance/collapse_scenario_detection.cpp @@ -17,21 +17,6 @@ using namespace osrm::guidance; namespace { -// check bearings for u-turns. -// since bearings are wrapped around at 0 (we only support 0,360), we need to do some minor math to -// check if bearings `a` and `b` go in opposite directions. In general we accept some minor -// deviations for u-turns. -bool bearingsAreReversed(const double bearing_in, const double bearing_out) -{ - // Nearly perfectly reversed angles have a difference close to 180 degrees (straight) - const double left_turn_angle = [&]() { - if (0 <= bearing_out && bearing_out <= bearing_in) - return bearing_in - bearing_out; - return bearing_in + 360 - bearing_out; - }(); - return util::angularDeviation(left_turn_angle, 180) <= 35; -} - // to collapse steps, we focus on short segments that don't interact with other roads. To collapse // two instructions into one, we need to look at to instrutions immediately after each other. bool noIntermediaryIntersections(const RouteStep &step) diff --git a/src/engine/guidance/collapse_turns.cpp b/src/engine/guidance/collapse_turns.cpp index 81c702ab4..be3680827 100644 --- a/src/engine/guidance/collapse_turns.cpp +++ b/src/engine/guidance/collapse_turns.cpp @@ -221,12 +221,16 @@ void AdjustToCombinedTurnStrategy::operator()(RouteStep &step_at_turn_location, if (hasTurnType(step_at_turn_location, TurnType::Suppressed)) { if (new_modifier == DirectionModifier::Straight) + { setInstructionType(step_at_turn_location, TurnType::NewName); + } else + { step_at_turn_location.maneuver.instruction.type = haveSameName(step_prior_to_intersection, transfer_from_step) ? TurnType::Continue : TurnType::Turn; + } } else if (hasTurnType(step_at_turn_location, TurnType::NewName) && hasTurnType(transfer_from_step, TurnType::Suppressed) && @@ -285,6 +289,110 @@ void StaggeredTurnStrategy::operator()(RouteStep &step_at_turn_location, : TurnType::NewName; } +void CombineSegregatedStepsStrategy::operator()(RouteStep &step_at_turn_location, + const RouteStep &transfer_from_step) const +{ + // Handle end of road + if (hasTurnType(step_at_turn_location, TurnType::EndOfRoad) || + hasTurnType(transfer_from_step, TurnType::EndOfRoad)) + { + setInstructionType(step_at_turn_location, TurnType::EndOfRoad); + } +} + +SegregatedTurnStrategy::SegregatedTurnStrategy(const RouteStep &step_prior_to_intersection) + : step_prior_to_intersection(step_prior_to_intersection) +{ +} + +void SegregatedTurnStrategy::operator()(RouteStep &step_at_turn_location, + const RouteStep &transfer_from_step) const +{ + // Used to control updating of the modifier based on turn direction + bool update_modifier_for_turn_direction = true; + + const auto calculate_turn_angle = [](const RouteStep &entry_step, const RouteStep &exit_step) { + return util::bearing::angleBetween(entry_step.maneuver.bearing_before, + exit_step.maneuver.bearing_after); + }; + + // Calculate turn angle and direction for segregated + const auto turn_angle = calculate_turn_angle(step_at_turn_location, transfer_from_step); + const auto turn_direction = getTurnDirection(turn_angle); + + const auto is_straight_step = [](const RouteStep &step) { + return ((hasTurnType(step, TurnType::NewName) || hasTurnType(step, TurnType::Continue) || + hasTurnType(step, TurnType::Suppressed) || hasTurnType(step, TurnType::Turn)) && + (hasModifier(step, DirectionModifier::Straight) || + hasModifier(step, DirectionModifier::SlightLeft) || + hasModifier(step, DirectionModifier::SlightRight))); + }; + + const auto is_turn_step = [](const RouteStep &step) { + return (hasTurnType(step, TurnType::Turn) || hasTurnType(step, TurnType::Continue) || + hasTurnType(step, TurnType::NewName) || hasTurnType(step, TurnType::Suppressed)); + }; + + // Process end of road step + if (hasTurnType(step_at_turn_location, TurnType::EndOfRoad) || + hasTurnType(transfer_from_step, TurnType::EndOfRoad)) + { + // Keep end of road + setInstructionType(step_at_turn_location, TurnType::EndOfRoad); + } + // Process fork step at turn + else if (hasTurnType(step_at_turn_location, TurnType::Fork)) + { + // Do not update modifier based on turn direction + update_modifier_for_turn_direction = false; + } + // Process straight step + else if ((turn_direction == guidance::DirectionModifier::Straight) && + is_straight_step(transfer_from_step)) + { + // Determine if continue or new name + setInstructionType(step_at_turn_location, + (haveSameName(step_prior_to_intersection, transfer_from_step) + ? TurnType::Suppressed + : TurnType::NewName)); + } + // Process wider straight step + else if (isWiderStraight(turn_angle) && hasSingleIntersection(step_at_turn_location) && + hasStraightestTurn(step_at_turn_location) && hasStraightestTurn(transfer_from_step)) + { + // Determine if continue or new name + setInstructionType(step_at_turn_location, + (haveSameName(step_prior_to_intersection, transfer_from_step) + ? TurnType::Suppressed + : TurnType::NewName)); + + // Set modifier to straight + setModifier(step_at_turn_location, osrm::guidance::DirectionModifier::Straight); + + // Do not update modifier based on turn direction + update_modifier_for_turn_direction = false; + } + // Process turn step + else if ((turn_direction != guidance::DirectionModifier::Straight) && + is_turn_step(transfer_from_step)) + { + // Mark as turn + setInstructionType(step_at_turn_location, TurnType::Turn); + } + // Process the others not covered above by using the transfer step turn type + else + { + // Set type from transfer step + setInstructionType(step_at_turn_location, transfer_from_step.maneuver.instruction.type); + } + + // Update modifier based on turn direction, if needed + if (update_modifier_for_turn_direction) + { + setModifier(step_at_turn_location, turn_direction); + } +} + SetFixedInstructionStrategy::SetFixedInstructionStrategy(const TurnInstruction instruction) : instruction(instruction) { @@ -478,6 +586,85 @@ RouteSteps collapseTurnInstructions(RouteSteps steps) return steps; } +// OTHER IMPLEMENTATIONS +OSRM_ATTR_WARN_UNUSED +RouteSteps collapseSegregatedTurnInstructions(RouteSteps steps) +{ + // make sure we can safely iterate over all steps (has depart/arrive with TurnType::NoTurn) + BOOST_ASSERT(!hasTurnType(steps.front()) && !hasTurnType(steps.back())); + BOOST_ASSERT(hasWaypointType(steps.front()) && hasWaypointType(steps.back())); + + if (steps.size() <= 2) + return steps; + + auto curr_step = steps.begin() + 1; + auto next_step = curr_step + 1; + const auto last_step = steps.end() - 1; + + // Loop over steps to collapse the segregated intersections; ignore first and last step + while (next_step != last_step) + { + const auto prev_step = findPreviousTurn(curr_step); + + // if current step and next step are both segregated then combine the steps with no turn + // adjustment + if (curr_step->is_segregated && next_step->is_segregated) + { + // Combine segregated steps + combineRouteSteps(*curr_step, + *next_step, + CombineSegregatedStepsStrategy(), + TransferSignageStrategy(), + TransferLanesStrategy()); + ++next_step; + } + // else if the current step is segregated and the next step is not then combine with turn + // adjustment + else if (curr_step->is_segregated && !next_step->is_segregated) + { + // Determine if u-turn + if (bearingsAreReversed( + util::bearing::reverse(curr_step->intersections.front() + .bearings[curr_step->intersections.front().in]), + next_step->intersections.front() + .bearings[next_step->intersections.front().out])) + { + // Collapse segregated u-turn + combineRouteSteps( + *curr_step, + *next_step, + SetFixedInstructionStrategy({TurnType::Continue, DirectionModifier::UTurn}), + TransferSignageStrategy(), + NoModificationStrategy()); + } + else + { + // Collapse segregated turn + combineRouteSteps(*curr_step, + *next_step, + SegregatedTurnStrategy(*prev_step), + TransferSignageStrategy(), + NoModificationStrategy()); + } + + // Segregated step has been removed + curr_step->is_segregated = false; + ++next_step; + } + // else next step + else + { + curr_step = next_step; + ++next_step; + } + } + + // Clean up steps + steps = removeNoTurnInstructions(std::move(steps)); + + return steps; +} + } // namespace guidance } // namespace engine } // namespace osrm