modify turn angles and instructions
This commit is contained in:
parent
ab9426e260
commit
f14352f494
@ -62,13 +62,13 @@ Feature: Bike - Mode flag
|
|||||||
| cd | primary | |
|
| cd | primary | |
|
||||||
|
|
||||||
When I route I should get
|
When I route I should get
|
||||||
| from | to | route | turns | modes |
|
| from | to | route | turns | modes |
|
||||||
| a | d | ab,bc,cd | head,right,left,destination | 1,1,1 |
|
| a | d | ab,bc,cd | head,straight,straight,destination | 1,1,1 |
|
||||||
| d | a | cd,bc,ab | head,right,left,destination | 1,2,1 |
|
| d | a | cd,bc,ab | head,right,left,destination | 1,2,1 |
|
||||||
| c | a | bc,ab | head,left,destination | 2,1 |
|
| c | a | bc,ab | head,left,destination | 2,1 |
|
||||||
| d | b | cd,bc | head,right,destination | 1,2 |
|
| d | b | cd,bc | head,right,destination | 1,2 |
|
||||||
| a | c | ab,bc | head,right,destination | 1,1 |
|
| a | c | ab,bc | head,straight,destination | 1,1 |
|
||||||
| b | d | bc,cd | head,left,destination | 1,1 |
|
| b | d | bc,cd | head,straight,destination | 1,1 |
|
||||||
|
|
||||||
Scenario: Bike - Mode when pushing on pedestrain streets
|
Scenario: Bike - Mode when pushing on pedestrain streets
|
||||||
Given the node map
|
Given the node map
|
||||||
|
@ -98,11 +98,11 @@ Feature: Bike - Accessability of different way types
|
|||||||
| cd | primary | |
|
| cd | primary | |
|
||||||
|
|
||||||
When I route I should get
|
When I route I should get
|
||||||
| from | to | route | turns |
|
| from | to | route | turns |
|
||||||
| a | d | ab,bc,cd | head,right,left,destination |
|
| a | d | ab,bc,cd | head,straight,straight,destination |
|
||||||
| d | a | cd,bc,ab | head,right,left,destination |
|
| d | a | cd,bc,ab | head,right,left,destination |
|
||||||
| c | a | bc,ab | head,left,destination |
|
| c | a | bc,ab | head,left,destination |
|
||||||
| d | b | cd,bc | head,right,destination |
|
| d | b | cd,bc | head,right,destination |
|
||||||
|
|
||||||
@todo
|
@todo
|
||||||
Scenario: Bike - Instructions when pushing bike on footway/pedestrian, etc.
|
Scenario: Bike - Instructions when pushing bike on footway/pedestrian, etc.
|
||||||
|
@ -155,8 +155,9 @@ def turn_list instructions
|
|||||||
13 => :stay_roundabout,
|
13 => :stay_roundabout,
|
||||||
14 => :start_end_of_street,
|
14 => :start_end_of_street,
|
||||||
15 => :destination,
|
15 => :destination,
|
||||||
16 => :enter_contraflow,
|
16 => :name_changes,
|
||||||
17 => :leave_contraflow
|
17 => :enter_contraflow,
|
||||||
|
18 => :leave_contraflow
|
||||||
}
|
}
|
||||||
# replace instructions codes with strings
|
# replace instructions codes with strings
|
||||||
# "11-3" (enter roundabout and leave a 3rd exit) gets converted to "enter_roundabout-3"
|
# "11-3" (enter roundabout and leave a 3rd exit) gets converted to "enter_roundabout-3"
|
||||||
|
@ -36,5 +36,5 @@ Feature: Basic Routing
|
|||||||
| fy | last |
|
| fy | last |
|
||||||
|
|
||||||
When I route I should get
|
When I route I should get
|
||||||
| from | to | route | turns |
|
| from | to | route | turns |
|
||||||
| x | y | first,compr,last | head,right,left,destination |
|
| x | y | first,compr,last | head,straight,straight,destination |
|
||||||
|
@ -92,6 +92,6 @@ Feature: Avoid weird loops caused by rounding errors
|
|||||||
| cf | primary |
|
| cf | primary |
|
||||||
|
|
||||||
When I route I should get
|
When I route I should get
|
||||||
| waypoints | route | turns |
|
| waypoints | route | turns |
|
||||||
| a,2,d | ab,be,ef,ef,cf,cd | head,left,right,via,right,left,destination |
|
| a,2,d | ab,be,ef,ef,cf,cd | head,left,straight,via,straight,left,destination |
|
||||||
| a,1,d | ab,be,ef,ef,cf,cd | head,left,right,via,right,left,destination |
|
| a,1,d | ab,be,ef,ef,cf,cd | head,left,straight,via,straight,left,destination |
|
||||||
|
@ -17,21 +17,13 @@ Feature: Turn directions/codes
|
|||||||
And the ways
|
And the ways
|
||||||
| nodes |
|
| nodes |
|
||||||
| xa |
|
| xa |
|
||||||
| xb |
|
|
||||||
| xc |
|
| xc |
|
||||||
| xd |
|
|
||||||
| xe |
|
| xe |
|
||||||
| xf |
|
|
||||||
| xg |
|
| xg |
|
||||||
| xh |
|
|
||||||
| xi |
|
| xi |
|
||||||
| xj |
|
|
||||||
| xk |
|
| xk |
|
||||||
| xl |
|
|
||||||
| xm |
|
| xm |
|
||||||
| xn |
|
|
||||||
| xo |
|
| xo |
|
||||||
| xp |
|
|
||||||
|
|
||||||
When I match I should get
|
When I match I should get
|
||||||
| trace | route | turns | matchings |
|
| trace | route | turns | matchings |
|
||||||
@ -40,7 +32,7 @@ Feature: Turn directions/codes
|
|||||||
| ia | xi,xa | head,straight,destination | ia |
|
| ia | xi,xa | head,straight,destination | ia |
|
||||||
| ic | xi,xc | head,slight_right,destination | ic |
|
| ic | xi,xc | head,slight_right,destination | ic |
|
||||||
| ie | xi,xe | head,right,destination | ie |
|
| ie | xi,xe | head,right,destination | ie |
|
||||||
|
|
||||||
| ko | xk,xo | head,left,destination | ko |
|
| ko | xk,xo | head,left,destination | ko |
|
||||||
| ka | xk,xa | head,slight_left,destination | ka |
|
| ka | xk,xa | head,slight_left,destination | ka |
|
||||||
| kc | xk,xc | head,straight,destination | kc |
|
| kc | xk,xc | head,straight,destination | kc |
|
||||||
@ -96,21 +88,13 @@ Feature: Turn directions/codes
|
|||||||
And the ways
|
And the ways
|
||||||
| nodes |
|
| nodes |
|
||||||
| xa |
|
| xa |
|
||||||
| xb |
|
|
||||||
| xc |
|
| xc |
|
||||||
| xd |
|
|
||||||
| xe |
|
| xe |
|
||||||
| xf |
|
|
||||||
| xg |
|
| xg |
|
||||||
| xh |
|
|
||||||
| xi |
|
| xi |
|
||||||
| xj |
|
|
||||||
| xk |
|
| xk |
|
||||||
| xl |
|
|
||||||
| xm |
|
| xm |
|
||||||
| xn |
|
|
||||||
| xo |
|
| xo |
|
||||||
| xp |
|
|
||||||
|
|
||||||
When I match I should get
|
When I match I should get
|
||||||
| trace | route | turns | matchings | duration |
|
| trace | route | turns | matchings | duration |
|
||||||
|
@ -174,14 +174,14 @@ Feature: Testbot - Travel mode
|
|||||||
| ef | primary | | |
|
| ef | primary | | |
|
||||||
|
|
||||||
When I route I should get
|
When I route I should get
|
||||||
| from | to | route | turns | modes |
|
| from | to | route | turns | modes |
|
||||||
| a | d | ab,bc,cd | head,right,left,destination | 1,2,1 |
|
| a | d | ab,bc,cd | head,right,left,destination | 1,2,1 |
|
||||||
| d | a | cd,bc,ab | head,right,left,destination | 1,2,1 |
|
| d | a | cd,bc,ab | head,right,left,destination | 1,2,1 |
|
||||||
| c | a | bc,ab | head,left,destination | 2,1 |
|
| c | a | bc,ab | head,left,destination | 2,1 |
|
||||||
| d | b | cd,bc | head,right,destination | 1,2 |
|
| d | b | cd,bc | head,right,destination | 1,2 |
|
||||||
| a | c | ab,bc | head,right,destination | 1,2 |
|
| a | c | ab,bc | head,right,destination | 1,2 |
|
||||||
| b | d | bc,cd | head,left,destination | 2,1 |
|
| b | d | bc,cd | head,left,destination | 2,1 |
|
||||||
| a | f | ab,bc,cd,de,ef | head,right,left,straight,straight,destination | 1,2,1,1,1 |
|
| a | f | ab,bc,cd,de,ef | head,right,left,straight,straight,destination | 1,2,1,1,1 |
|
||||||
|
|
||||||
Scenario: Testbot - Modes, triangle map
|
Scenario: Testbot - Modes, triangle map
|
||||||
Given the node map
|
Given the node map
|
||||||
|
@ -21,13 +21,13 @@ Feature: POST request
|
|||||||
| bc |
|
| bc |
|
||||||
| xy |
|
| xy |
|
||||||
| yz |
|
| yz |
|
||||||
|
|
||||||
When I route I should get
|
When I route I should get
|
||||||
| from | to | route | turns |
|
| from | to | route | turns |
|
||||||
| a | c | ab,bc | head,left,destination |
|
| a | c | ab,bc | head,straight,destination |
|
||||||
| c | a | bc,ab | head,right,destination |
|
| c | a | bc,ab | head,straight,destination |
|
||||||
| x | z | xy,yz | head,right,destination |
|
| x | z | xy,yz | head,straight,destination |
|
||||||
| z | x | yz,xy | head,left,destination |
|
| z | x | yz,xy | head,straight,destination |
|
||||||
|
|
||||||
Scenario: Testbot - match POST request
|
Scenario: Testbot - match POST request
|
||||||
Given a grid size of 10 meters
|
Given a grid size of 10 meters
|
||||||
|
74
features/testbot/turn_angles.feature
Normal file
74
features/testbot/turn_angles.feature
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
@routing @testbot @via
|
||||||
|
Feature: Via points
|
||||||
|
|
||||||
|
Background:
|
||||||
|
Given the profile "testbot"
|
||||||
|
|
||||||
|
And a grid size of 4 meters
|
||||||
|
|
||||||
|
Scenario: Basic Right Turn
|
||||||
|
Given the node map
|
||||||
|
| a | b | c | d | e | f | g |
|
||||||
|
| | | | | | h | |
|
||||||
|
| | | | | | i | |
|
||||||
|
| | | | | | j | |
|
||||||
|
| | | | | | k | |
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | oneway |
|
||||||
|
| abcdefg | yes |
|
||||||
|
| ehijk | yes |
|
||||||
|
|
||||||
|
When I route I should get
|
||||||
|
| from | to | route | distance | turns |
|
||||||
|
| a | k | abcdefg,ehijk | 34m +-1 | head,right,destination |
|
||||||
|
|
||||||
|
Scenario: Slight Turn
|
||||||
|
Given the node map
|
||||||
|
| a | b | c | d | e | f | g | |
|
||||||
|
| | | | | | h | i | |
|
||||||
|
| | | | | | | | j |
|
||||||
|
| | | | | | | | k |
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | oneway |
|
||||||
|
| abcdefg | yes |
|
||||||
|
| ehijk | yes |
|
||||||
|
|
||||||
|
When I route I should get
|
||||||
|
| from | to | route | distance | turns |
|
||||||
|
| a | k | abcdefg,ehijk | 34m +-1 | head,slight_right,destination |
|
||||||
|
|
||||||
|
Scenario: Nearly Slight Turn
|
||||||
|
Given the node map
|
||||||
|
| a | b | c | d | e | f | g | |
|
||||||
|
| | | | | | h | | |
|
||||||
|
| | | | | | | i | |
|
||||||
|
| | | | | | | | j |
|
||||||
|
| | | | | | | | k |
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | oneway |
|
||||||
|
| abcdefg | yes |
|
||||||
|
| ehijk | yes |
|
||||||
|
|
||||||
|
When I route I should get
|
||||||
|
| from | to | route | distance | turns |
|
||||||
|
| a | k | abcdefg,ehijk | 37m +-1 | head,right,destination |
|
||||||
|
|
||||||
|
Scenario: Nearly Slight Turn (Variation)
|
||||||
|
Given the node map
|
||||||
|
| a | b | c | d | e | f | g | |
|
||||||
|
| | | | | | h | | |
|
||||||
|
| | | | | | | i | |
|
||||||
|
| | | | | | | j | |
|
||||||
|
| | | | | | | | k |
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | oneway |
|
||||||
|
| abcdefg | yes |
|
||||||
|
| ehijk | yes |
|
||||||
|
|
||||||
|
When I route I should get
|
||||||
|
| from | to | route | distance | turns |
|
||||||
|
| a | k | abcdefg,ehijk | 37m +-1 | head,right,destination |
|
@ -14,22 +14,14 @@ Feature: Turn directions/codes
|
|||||||
|
|
||||||
And the ways
|
And the ways
|
||||||
| nodes |
|
| nodes |
|
||||||
| xa |
|
|
||||||
| xb |
|
|
||||||
| xc |
|
|
||||||
| xd |
|
|
||||||
| xe |
|
|
||||||
| xf |
|
|
||||||
| xg |
|
|
||||||
| xh |
|
|
||||||
| xi |
|
| xi |
|
||||||
| xj |
|
|
||||||
| xk |
|
| xk |
|
||||||
| xl |
|
|
||||||
| xm |
|
| xm |
|
||||||
| xn |
|
|
||||||
| xo |
|
| xo |
|
||||||
| xp |
|
| xa |
|
||||||
|
| xc |
|
||||||
|
| xe |
|
||||||
|
| xg |
|
||||||
|
|
||||||
When I route I should get
|
When I route I should get
|
||||||
| from | to | route | turns |
|
| from | to | route | turns |
|
||||||
@ -116,8 +108,8 @@ Feature: Turn directions/codes
|
|||||||
| yz |
|
| yz |
|
||||||
|
|
||||||
When I route I should get
|
When I route I should get
|
||||||
| from | to | route | turns |
|
| from | to | route | turns |
|
||||||
| a | c | ab,bc | head,left,destination |
|
| a | c | ab,bc | head,straight,destination |
|
||||||
| c | a | bc,ab | head,right,destination |
|
| c | a | bc,ab | head,straight,destination |
|
||||||
| x | z | xy,yz | head,right,destination |
|
| x | z | xy,yz | head,straight,destination |
|
||||||
| z | x | yz,xy | head,left,destination |
|
| z | x | yz,xy | head,straight,destination |
|
||||||
|
@ -20,8 +20,8 @@ Feature: U-turns at via points
|
|||||||
| fg |
|
| fg |
|
||||||
|
|
||||||
When I route I should get
|
When I route I should get
|
||||||
| waypoints | route | turns |
|
| waypoints | route | turns |
|
||||||
| a,e,c | ab,be,be,ef,fg,dg,cd | head,right,via,left,straight,left,left,destination |
|
| a,e,c | ab,be,be,ef,fg,dg,cd | head,right,via,straight,straight,straight,straight,destination |
|
||||||
|
|
||||||
Scenario: Query param to allow U-turns at all via points
|
Scenario: Query param to allow U-turns at all via points
|
||||||
Given the node map
|
Given the node map
|
||||||
|
@ -86,12 +86,12 @@ Feature: Via points
|
|||||||
| fa | yes |
|
| fa | yes |
|
||||||
|
|
||||||
When I route I should get
|
When I route I should get
|
||||||
| waypoints | route | distance | turns |
|
| waypoints | route | distance | turns |
|
||||||
| 1,3 | ab,bc,cd | 400m +-1 | head,straight,straight,destination |
|
| 1,3 | ab,bc,cd | 400m +-1 | head,straight,straight,destination |
|
||||||
| 3,1 | cd,de,ef,fa,ab | 1000m +-1 | head,right,right,right,right,destination |
|
| 3,1 | cd,de,ef,fa,ab | 1000m +-1 | head,straight,straight,straight,right,destination |
|
||||||
| 1,2,3 | ab,bc,bc,cd | 400m +-1 | head,straight,via,straight,destination |
|
| 1,2,3 | ab,bc,bc,cd | 400m +-1 | head,straight,via,straight,destination |
|
||||||
| 1,3,2 | ab,bc,cd,cd,de,ef,fa,ab,bc | 1600m +-1 | head,straight,straight,via,right,right,right,right,straight,destination |
|
| 1,3,2 | ab,bc,cd,cd,de,ef,fa,ab,bc | 1600m +-1 | head,straight,straight,via,straight,straight,straight,right,straight,destination |
|
||||||
| 3,2,1 | cd,de,ef,fa,ab,bc,bc,cd,de,ef,fa,ab | 2400m +-1 | head,right,right,right,right,straight,via,straight,right,right,right,right,destination |
|
| 3,2,1 | cd,de,ef,fa,ab,bc,bc,cd,de,ef,fa,ab | 2400m +-1 | head,straight,straight,straight,right,straight,via,straight,straight,straight,straight,right,destination |
|
||||||
|
|
||||||
Scenario: Via points on ring on the same oneway
|
Scenario: Via points on ring on the same oneway
|
||||||
# xa it to avoid only having a single ring, which cna trigger edge cases
|
# xa it to avoid only having a single ring, which cna trigger edge cases
|
||||||
@ -109,12 +109,12 @@ Feature: Via points
|
|||||||
| da | yes |
|
| da | yes |
|
||||||
|
|
||||||
When I route I should get
|
When I route I should get
|
||||||
| waypoints | route | distance | turns |
|
| waypoints | route | distance | turns |
|
||||||
| 1,3 | ab | 200m +-1 | head,destination |
|
| 1,3 | ab | 200m +-1 | head,destination |
|
||||||
| 3,1 | ab,bc,cd,da,ab | 800m +-1 | head,right,right,right,right,destination |
|
| 3,1 | ab,bc,cd,da,ab | 800m +-1 | head,straight,straight,straight,right,destination |
|
||||||
| 1,2,3 | ab,ab | 200m +-1 | head,via,destination |
|
| 1,2,3 | ab,ab | 200m +-1 | head,via,destination |
|
||||||
| 1,3,2 | ab,ab,bc,cd,da,ab | 1100m +-1 | head,via,right,right,right,right,destination |
|
| 1,3,2 | ab,ab,bc,cd,da,ab | 1100m +-1 | head,via,straight,straight,straight,right,destination |
|
||||||
| 3,2,1 | ab,bc,cd,da,ab,ab,bc,cd,da,ab | 1800m | head,right,right,right,right,via,right,right,right,right,destination |
|
| 3,2,1 | ab,bc,cd,da,ab,ab,bc,cd,da,ab | 1800m | head,straight,straight,straight,right,via,straight,straight,straight,right,destination |
|
||||||
|
|
||||||
# See issue #1896
|
# See issue #1896
|
||||||
Scenario: Via point at a dead end with oneway
|
Scenario: Via point at a dead end with oneway
|
||||||
@ -175,11 +175,11 @@ Feature: Via points
|
|||||||
| da | yes |
|
| da | yes |
|
||||||
|
|
||||||
When I route I should get
|
When I route I should get
|
||||||
| waypoints | route | distance | turns |
|
| waypoints | route | distance | turns |
|
||||||
| 2,1 | ab,bc,cd,da,ab | 1100m +-1 | head,right,right,right,right,destination |
|
| 2,1 | ab,bc,cd,da,ab | 1100m +-1 | head,straight,straight,straight,straight,destination |
|
||||||
| 4,3 | bc,cd,da,ab,bc | 1100m +-1 | head,right,right,right,right,destination |
|
| 4,3 | bc,cd,da,ab,bc | 1100m +-1 | head,straight,straight,straight,straight,destination |
|
||||||
| 6,5 | cd,da,ab,bc,cd | 1100m +-1 | head,right,right,right,right,destination |
|
| 6,5 | cd,da,ab,bc,cd | 1100m +-1 | head,straight,straight,straight,straight,destination |
|
||||||
| 8,7 | da,ab,bc,cd,da | 1100m +-1 | head,right,right,right,right,destination |
|
| 8,7 | da,ab,bc,cd,da | 1100m +-1 | head,straight,straight,straight,straight,destination |
|
||||||
|
|
||||||
Scenario: Multiple Via points on ring on the same oneway, forces one of the vertices to be top node
|
Scenario: Multiple Via points on ring on the same oneway, forces one of the vertices to be top node
|
||||||
Given the node map
|
Given the node map
|
||||||
@ -197,7 +197,7 @@ Feature: Via points
|
|||||||
| da | yes |
|
| da | yes |
|
||||||
|
|
||||||
When I route I should get
|
When I route I should get
|
||||||
| waypoints | route | distance | turns |
|
| waypoints | route | distance | turns |
|
||||||
| 3,2,1 | ab,bc,cd,da,ab,ab,bc,cd,da,ab | 3000m +-1 | head,right,right,right,right,via,right,right,right,right,destination |
|
| 3,2,1 | ab,bc,cd,da,ab,ab,bc,cd,da,ab | 3000m +-1 | head,straight,straight,straight,straight,via,straight,straight,straight,straight,destination |
|
||||||
| 6,5,4 | bc,cd,da,ab,bc,bc,cd,da,ab,bc | 3000m +-1 | head,right,right,right,right,via,right,right,right,right,destination |
|
| 6,5,4 | bc,cd,da,ab,bc,bc,cd,da,ab,bc | 3000m +-1 | head,straight,straight,straight,straight,via,straight,straight,straight,straight,destination |
|
||||||
| 9,8,7 | cd,da,ab,bc,cd,cd,da,ab,bc,cd | 3000m +-1 | head,right,right,right,right,via,right,right,right,right,destination |
|
| 9,8,7 | cd,da,ab,bc,cd,cd,da,ab,bc,cd | 3000m +-1 | head,straight,straight,straight,straight,via,straight,straight,straight,straight,destination |
|
||||||
|
@ -106,7 +106,8 @@ SegmentList<DataFacadeT>::SegmentList(const InternalRouteResult &raw_route,
|
|||||||
{
|
{
|
||||||
const auto &source_phantom =
|
const auto &source_phantom =
|
||||||
raw_route.segment_end_coordinates[raw_index].target_phantom;
|
raw_route.segment_end_coordinates[raw_index].target_phantom;
|
||||||
if (raw_route.target_traversed_in_reverse[raw_index] != raw_route.source_traversed_in_reverse[raw_index+1])
|
if (raw_route.target_traversed_in_reverse[raw_index] !=
|
||||||
|
raw_route.source_traversed_in_reverse[raw_index + 1])
|
||||||
{
|
{
|
||||||
bool traversed_in_reverse = raw_route.target_traversed_in_reverse[raw_index];
|
bool traversed_in_reverse = raw_route.target_traversed_in_reverse[raw_index];
|
||||||
const extractor::TravelMode travel_mode =
|
const extractor::TravelMode travel_mode =
|
||||||
@ -264,6 +265,12 @@ void SegmentList<DataFacadeT>::Finalize(const bool extract_alternative,
|
|||||||
segment_length = 0;
|
segment_length = 0;
|
||||||
segment_duration = 0;
|
segment_duration = 0;
|
||||||
segment_start_index = i;
|
segment_start_index = i;
|
||||||
|
|
||||||
|
if (segments[i].turn_instruction == extractor::TurnInstruction::NameChanges)
|
||||||
|
{
|
||||||
|
segments[i].turn_instruction =
|
||||||
|
extractor::TurnInstruction::GoStraight; // to not break the api
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
#include "engine/search_engine.hpp"
|
#include "engine/search_engine.hpp"
|
||||||
#include "util/for_each_pair.hpp"
|
#include "util/for_each_pair.hpp"
|
||||||
#include "util/integer_range.hpp"
|
#include "util/integer_range.hpp"
|
||||||
#include "util/json_renderer.hpp"
|
|
||||||
#include "util/make_unique.hpp"
|
#include "util/make_unique.hpp"
|
||||||
#include "util/simple_logger.hpp"
|
#include "util/simple_logger.hpp"
|
||||||
#include "util/timing_util.hpp"
|
#include "util/timing_util.hpp"
|
||||||
@ -166,7 +165,6 @@ template <class DataFacadeT> class ViaRoutePlugin final : public BasePlugin
|
|||||||
return Status::Error;
|
return Status::Error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Status::Ok;
|
return Status::Ok;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
#include "extractor/restriction_map.hpp"
|
#include "extractor/restriction_map.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstddef>
|
||||||
#include <iosfwd>
|
#include <iosfwd>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
@ -23,6 +25,7 @@
|
|||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include <boost/filesystem/fstream.hpp>
|
#include <boost/filesystem/fstream.hpp>
|
||||||
|
|
||||||
@ -70,10 +73,18 @@ class EdgeBasedGraphFactory
|
|||||||
|
|
||||||
unsigned GetHighestEdgeID();
|
unsigned GetHighestEdgeID();
|
||||||
|
|
||||||
TurnInstruction
|
// Basic analysis of a turn (u --(e1)-- v --(e2)-- w)
|
||||||
AnalyzeTurn(const NodeID u, const NodeID v, const NodeID w, const double angle) const;
|
// with known angle.
|
||||||
|
// Handles special cases like u-turns and roundabouts
|
||||||
|
// For basic turns, the turn based on the angle-classification is returned
|
||||||
|
TurnInstruction AnalyzeTurn(const NodeID u,
|
||||||
|
const EdgeID e1,
|
||||||
|
const NodeID v,
|
||||||
|
const EdgeID e2,
|
||||||
|
const NodeID w,
|
||||||
|
const double angle) const;
|
||||||
|
|
||||||
int GetTurnPenalty(double angle, lua_State *lua_state) const;
|
std::int32_t GetTurnPenalty(double angle, lua_State *lua_state) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using EdgeData = util::NodeBasedDynamicGraph::EdgeData;
|
using EdgeData = util::NodeBasedDynamicGraph::EdgeData;
|
||||||
@ -123,8 +134,54 @@ class EdgeBasedGraphFactory
|
|||||||
|
|
||||||
void FlushVectorToStream(std::ofstream &edge_data_file,
|
void FlushVectorToStream(std::ofstream &edge_data_file,
|
||||||
std::vector<OriginalEdgeData> &original_edge_data_vector) const;
|
std::vector<OriginalEdgeData> &original_edge_data_vector) const;
|
||||||
|
|
||||||
|
struct TurnCandidate
|
||||||
|
{
|
||||||
|
EdgeID eid; // the id of the arc
|
||||||
|
bool valid; // a turn may be relevant to good instructions, even if we cannot take the road
|
||||||
|
double angle; // the approximated angle of the turn
|
||||||
|
TurnInstruction instruction; // a proposed instruction
|
||||||
|
double confidence; // how close to the border is the turn?
|
||||||
|
|
||||||
|
std::string toString() const
|
||||||
|
{
|
||||||
|
std::string result = "[turn] ";
|
||||||
|
result += std::to_string(eid);
|
||||||
|
result += " valid: ";
|
||||||
|
result += std::to_string(valid);
|
||||||
|
result += " angle: ";
|
||||||
|
result += std::to_string(angle);
|
||||||
|
result += " instruction: ";
|
||||||
|
result += std::to_string(static_cast<std::int32_t>(instruction));
|
||||||
|
result += " confidence: ";
|
||||||
|
result += std::to_string(confidence);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Use In Order to generate base turns
|
||||||
|
|
||||||
|
// cannot be const due to the counters...
|
||||||
|
std::vector<TurnCandidate> getTurnCandidates(NodeID from, EdgeID via_edge);
|
||||||
|
std::vector<TurnCandidate> optimizeCandidates(NodeID via_edge,
|
||||||
|
std::vector<TurnCandidate> turn_candidates) const;
|
||||||
|
std::vector<TurnCandidate> suppressTurns(EdgeID via_edge,
|
||||||
|
std::vector<TurnCandidate> turn_candidates) const;
|
||||||
|
|
||||||
|
QueryNode getRepresentativeCoordinate(const NodeID src,
|
||||||
|
const NodeID tgt,
|
||||||
|
const EdgeID via_eid,
|
||||||
|
bool INVERTED) const;
|
||||||
|
|
||||||
|
bool isObviousChoice(EdgeID coming_from_eid,
|
||||||
|
std::size_t turn_index,
|
||||||
|
const std::vector<TurnCandidate> &turn_candidates) const;
|
||||||
|
|
||||||
|
std::size_t restricted_turns_counter;
|
||||||
|
std::size_t skipped_uturns_counter;
|
||||||
|
std::size_t skipped_barrier_turns_counter;
|
||||||
};
|
};
|
||||||
}
|
} // namespace extractor
|
||||||
}
|
} // namespace osrm
|
||||||
|
|
||||||
#endif /* EDGE_BASED_GRAPH_FACTORY_HPP_ */
|
#endif /* EDGE_BASED_GRAPH_FACTORY_HPP_ */
|
||||||
|
@ -17,7 +17,7 @@ struct QueryNode
|
|||||||
using key_type = OSMNodeID; // type of NodeID
|
using key_type = OSMNodeID; // type of NodeID
|
||||||
using value_type = int; // type of lat,lons
|
using value_type = int; // type of lat,lons
|
||||||
|
|
||||||
explicit QueryNode(int lat, int lon, OSMNodeID node_id)
|
explicit QueryNode(int lat, int lon, key_type node_id)
|
||||||
: lat(lat), lon(lon), node_id(std::move(node_id))
|
: lat(lat), lon(lon), node_id(std::move(node_id))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -29,7 +29,7 @@ struct QueryNode
|
|||||||
|
|
||||||
int lat;
|
int lat;
|
||||||
int lon;
|
int lon;
|
||||||
OSMNodeID node_id;
|
key_type node_id;
|
||||||
|
|
||||||
static QueryNode min_value()
|
static QueryNode min_value()
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
#ifndef TURN_INSTRUCTIONS_HPP
|
#ifndef TURN_INSTRUCTIONS_HPP
|
||||||
#define TURN_INSTRUCTIONS_HPP
|
#define TURN_INSTRUCTIONS_HPP
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
#include <boost/assert.hpp>
|
||||||
|
|
||||||
namespace osrm
|
namespace osrm
|
||||||
{
|
{
|
||||||
namespace extractor
|
namespace extractor
|
||||||
@ -24,6 +29,7 @@ enum class TurnInstruction : unsigned char
|
|||||||
StayOnRoundAbout,
|
StayOnRoundAbout,
|
||||||
StartAtEndOfStreet,
|
StartAtEndOfStreet,
|
||||||
ReachedYourDestination,
|
ReachedYourDestination,
|
||||||
|
NameChanges,
|
||||||
EnterAgainstAllowedDirection,
|
EnterAgainstAllowedDirection,
|
||||||
LeaveAgainstAllowedDirection,
|
LeaveAgainstAllowedDirection,
|
||||||
InverseAccessRestrictionFlag = 127,
|
InverseAccessRestrictionFlag = 127,
|
||||||
@ -31,37 +37,87 @@ enum class TurnInstruction : unsigned char
|
|||||||
AccessRestrictionPenalty = 129
|
AccessRestrictionPenalty = 129
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// shiftable turns to left and right
|
||||||
|
const constexpr bool shiftable_left[] = {false, false, true, true, true, false, false, true, true};
|
||||||
|
const constexpr bool shiftable_right[] = {false, false, true, true, false, false, true, true, true};
|
||||||
|
|
||||||
|
inline TurnInstruction shiftTurnToLeft(TurnInstruction turn)
|
||||||
|
{
|
||||||
|
BOOST_ASSERT_MSG(static_cast<int>(turn) < 9,
|
||||||
|
"Shift turn only supports basic turn instructions");
|
||||||
|
if (turn > TurnInstruction::TurnSlightLeft)
|
||||||
|
return turn;
|
||||||
|
else
|
||||||
|
return shiftable_left[static_cast<int>(turn)]
|
||||||
|
? (static_cast<TurnInstruction>(static_cast<int>(turn) - 1))
|
||||||
|
: turn;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline TurnInstruction shiftTurnToRight(TurnInstruction turn)
|
||||||
|
{
|
||||||
|
BOOST_ASSERT_MSG(static_cast<int>(turn) < 9,
|
||||||
|
"Shift turn only supports basic turn instructions");
|
||||||
|
if (turn > TurnInstruction::TurnSlightLeft)
|
||||||
|
return turn;
|
||||||
|
else
|
||||||
|
return shiftable_right[static_cast<int>(turn)]
|
||||||
|
? (static_cast<TurnInstruction>(static_cast<int>(turn) + 1))
|
||||||
|
: turn;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline double angularDeviation(const double angle, const double from)
|
||||||
|
{
|
||||||
|
const double deviation = std::abs(angle - from);
|
||||||
|
return std::min(360 - deviation, deviation);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline double getAngularPenalty(const double angle, TurnInstruction instruction)
|
||||||
|
{
|
||||||
|
BOOST_ASSERT_MSG(static_cast<int>(instruction) < 9,
|
||||||
|
"Angular penalty only supports basic turn instructions");
|
||||||
|
const double center[] = {180, 180, 135, 90, 45,
|
||||||
|
0, 315, 270, 225}; // centers of turns from getTurnDirection
|
||||||
|
return angularDeviation(center[static_cast<int>(instruction)], angle);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline double getTurnConfidence(const double angle, TurnInstruction instruction)
|
||||||
|
{
|
||||||
|
|
||||||
|
// special handling of U-Turns and Roundabout
|
||||||
|
if (instruction >= TurnInstruction::HeadOn || instruction == TurnInstruction::UTurn ||
|
||||||
|
instruction == TurnInstruction::NoTurn || instruction == TurnInstruction::EnterRoundAbout ||
|
||||||
|
instruction == TurnInstruction::StayOnRoundAbout || instruction == TurnInstruction::LeaveRoundAbout )
|
||||||
|
return 1.0;
|
||||||
|
|
||||||
|
BOOST_ASSERT_MSG(static_cast<int>(instruction) < 9,
|
||||||
|
"Turn confidence only supports basic turn instructions");
|
||||||
|
const double deviations[] = {10, 10, 35, 50, 45, 0, 45, 50, 35};
|
||||||
|
const double difference = getAngularPenalty(angle, instruction);
|
||||||
|
const double max_deviation = deviations[static_cast<int>(instruction)];
|
||||||
|
return 1.0 - (difference / max_deviation) * (difference / max_deviation);
|
||||||
|
}
|
||||||
|
|
||||||
// Translates between angles and their human-friendly directional representation
|
// Translates between angles and their human-friendly directional representation
|
||||||
inline TurnInstruction getTurnDirection(const double angle)
|
inline TurnInstruction getTurnDirection(const double angle)
|
||||||
{
|
{
|
||||||
if (angle >= 23 && angle < 67)
|
// An angle of zero is a u-turn
|
||||||
{
|
// 180 goes perfectly straight
|
||||||
|
// 0-180 are right turns
|
||||||
|
// 180-360 are left turns
|
||||||
|
if (angle > 0 && angle < 60)
|
||||||
return TurnInstruction::TurnSharpRight;
|
return TurnInstruction::TurnSharpRight;
|
||||||
}
|
if (angle >= 60 && angle < 140)
|
||||||
if (angle >= 67 && angle < 113)
|
|
||||||
{
|
|
||||||
return TurnInstruction::TurnRight;
|
return TurnInstruction::TurnRight;
|
||||||
}
|
if (angle >= 140 && angle < 170)
|
||||||
if (angle >= 113 && angle < 158)
|
|
||||||
{
|
|
||||||
return TurnInstruction::TurnSlightRight;
|
return TurnInstruction::TurnSlightRight;
|
||||||
}
|
if (angle >= 170 && angle <= 190)
|
||||||
if (angle >= 158 && angle < 202)
|
|
||||||
{
|
|
||||||
return TurnInstruction::GoStraight;
|
return TurnInstruction::GoStraight;
|
||||||
}
|
if (angle > 190 && angle <= 220)
|
||||||
if (angle >= 202 && angle < 248)
|
|
||||||
{
|
|
||||||
return TurnInstruction::TurnSlightLeft;
|
return TurnInstruction::TurnSlightLeft;
|
||||||
}
|
if (angle > 220 && angle <= 300)
|
||||||
if (angle >= 248 && angle < 292)
|
|
||||||
{
|
|
||||||
return TurnInstruction::TurnLeft;
|
return TurnInstruction::TurnLeft;
|
||||||
}
|
if (angle > 300 && angle < 360)
|
||||||
if (angle >= 292 && angle < 336)
|
|
||||||
{
|
|
||||||
return TurnInstruction::TurnSharpLeft;
|
return TurnInstruction::TurnSharpLeft;
|
||||||
}
|
|
||||||
return TurnInstruction::UTurn;
|
return TurnInstruction::UTurn;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,6 +131,51 @@ inline bool isTurnNecessary(const TurnInstruction turn_instruction)
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool resolve(TurnInstruction &to_resolve, const TurnInstruction neighbor, bool resolve_right)
|
||||||
|
{
|
||||||
|
const auto shifted_turn =
|
||||||
|
resolve_right ? shiftTurnToRight(to_resolve) : shiftTurnToLeft(to_resolve);
|
||||||
|
if (shifted_turn == neighbor || shifted_turn == to_resolve)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
to_resolve = shifted_turn;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool resolveTransitive(TurnInstruction &first,
|
||||||
|
TurnInstruction &second,
|
||||||
|
const TurnInstruction third,
|
||||||
|
bool resolve_right)
|
||||||
|
{
|
||||||
|
if (resolve(second, third, resolve_right))
|
||||||
|
{
|
||||||
|
first = resolve_right ? shiftTurnToRight(first) : shiftTurnToLeft(first);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool isSlightTurn(const TurnInstruction turn)
|
||||||
|
{
|
||||||
|
return turn == TurnInstruction::GoStraight || turn == TurnInstruction::TurnSlightRight ||
|
||||||
|
turn == TurnInstruction::TurnSlightLeft || turn == TurnInstruction::NoTurn;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool isSharpTurn(const TurnInstruction turn)
|
||||||
|
{
|
||||||
|
return turn == TurnInstruction::TurnSharpLeft || turn == TurnInstruction::TurnSharpRight;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool isStraight(const TurnInstruction turn)
|
||||||
|
{
|
||||||
|
return turn == TurnInstruction::GoStraight || turn == TurnInstruction::NoTurn;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool isConflict(const TurnInstruction first, const TurnInstruction second)
|
||||||
|
{
|
||||||
|
return first == second || (isStraight(first) && isStraight(second));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include "extractor/edge_based_edge.hpp"
|
#include "extractor/edge_based_edge.hpp"
|
||||||
#include "extractor/edge_based_graph_factory.hpp"
|
#include "extractor/edge_based_graph_factory.hpp"
|
||||||
|
#include "util/coordinate.hpp"
|
||||||
#include "util/coordinate_calculation.hpp"
|
#include "util/coordinate_calculation.hpp"
|
||||||
#include "util/percent.hpp"
|
#include "util/percent.hpp"
|
||||||
#include "util/integer_range.hpp"
|
#include "util/integer_range.hpp"
|
||||||
@ -12,15 +13,38 @@
|
|||||||
|
|
||||||
#include <boost/assert.hpp>
|
#include <boost/assert.hpp>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace osrm
|
namespace osrm
|
||||||
{
|
{
|
||||||
namespace extractor
|
namespace extractor
|
||||||
{
|
{
|
||||||
|
|
||||||
|
// configuration of turn classification
|
||||||
|
const bool constexpr INVERT = true;
|
||||||
|
const bool constexpr RESOLVE_TO_RIGHT = true;
|
||||||
|
const bool constexpr RESOLVE_TO_LEFT = false;
|
||||||
|
|
||||||
|
// what angle is interpreted as going straight
|
||||||
|
const double constexpr STRAIGHT_ANGLE = 180.;
|
||||||
|
// if a turn deviates this much from going straight, it will be kept straight
|
||||||
|
const double constexpr MAXIMAL_ALLOWED_NO_TURN_DEVIATION = 2.;
|
||||||
|
// angle that lies between two nearly indistinguishable roads
|
||||||
|
const double constexpr NARROW_TURN_ANGLE = 25.;
|
||||||
|
// angle difference that can be classified as straight, if its the only narrow turn
|
||||||
|
const double constexpr FUZZY_STRAIGHT_ANGLE = 15.;
|
||||||
|
const double constexpr DISTINCTION_RATIO = 2;
|
||||||
|
|
||||||
|
// Configuration to find representative candidate for turn angle calculations
|
||||||
|
const double constexpr MINIMAL_SEGMENT_LENGTH = 1.;
|
||||||
|
const double constexpr DESIRED_SEGMENT_LENGTH = 10.;
|
||||||
|
|
||||||
EdgeBasedGraphFactory::EdgeBasedGraphFactory(
|
EdgeBasedGraphFactory::EdgeBasedGraphFactory(
|
||||||
std::shared_ptr<util::NodeBasedDynamicGraph> node_based_graph,
|
std::shared_ptr<util::NodeBasedDynamicGraph> node_based_graph,
|
||||||
const CompressedEdgeContainer &compressed_edge_container,
|
const CompressedEdgeContainer &compressed_edge_container,
|
||||||
@ -361,8 +385,12 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
|||||||
{
|
{
|
||||||
util::SimpleLogger().Write() << "generating edge-expanded edges";
|
util::SimpleLogger().Write() << "generating edge-expanded edges";
|
||||||
|
|
||||||
unsigned node_based_edge_counter = 0;
|
std::size_t node_based_edge_counter = 0;
|
||||||
unsigned original_edges_counter = 0;
|
std::size_t original_edges_counter = 0;
|
||||||
|
restricted_turns_counter = 0;
|
||||||
|
skipped_uturns_counter = 0;
|
||||||
|
skipped_barrier_turns_counter = 0;
|
||||||
|
std::size_t compressed = 0;
|
||||||
|
|
||||||
std::ofstream edge_data_file(original_edge_data_filename.c_str(), std::ios::binary);
|
std::ofstream edge_data_file(original_edge_data_filename.c_str(), std::ios::binary);
|
||||||
std::ofstream edge_segment_file;
|
std::ofstream edge_segment_file;
|
||||||
@ -383,11 +411,6 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
|||||||
// Loop over all turns and generate new set of edges.
|
// Loop over all turns and generate new set of edges.
|
||||||
// Three nested loop look super-linear, but we are dealing with a (kind of)
|
// Three nested loop look super-linear, but we are dealing with a (kind of)
|
||||||
// linear number of turns only.
|
// linear number of turns only.
|
||||||
unsigned restricted_turns_counter = 0;
|
|
||||||
unsigned skipped_uturns_counter = 0;
|
|
||||||
unsigned skipped_barrier_turns_counter = 0;
|
|
||||||
unsigned compressed = 0;
|
|
||||||
|
|
||||||
util::Percent progress(m_node_based_graph->GetNumberOfNodes());
|
util::Percent progress(m_node_based_graph->GetNumberOfNodes());
|
||||||
|
|
||||||
#ifdef DEBUG_GEOMETRY
|
#ifdef DEBUG_GEOMETRY
|
||||||
@ -397,79 +420,30 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
|||||||
for (const auto node_u : util::irange(0u, m_node_based_graph->GetNumberOfNodes()))
|
for (const auto node_u : util::irange(0u, m_node_based_graph->GetNumberOfNodes()))
|
||||||
{
|
{
|
||||||
// progress.printStatus(node_u);
|
// progress.printStatus(node_u);
|
||||||
for (const EdgeID e1 : m_node_based_graph->GetAdjacentEdgeRange(node_u))
|
for (const EdgeID edge_form_u : m_node_based_graph->GetAdjacentEdgeRange(node_u))
|
||||||
{
|
{
|
||||||
if (m_node_based_graph->GetEdgeData(e1).reversed)
|
if (m_node_based_graph->GetEdgeData(edge_form_u).reversed)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
++node_based_edge_counter;
|
++node_based_edge_counter;
|
||||||
const NodeID node_v = m_node_based_graph->GetTarget(e1);
|
auto turn_candidates = getTurnCandidates(node_u, edge_form_u);
|
||||||
const NodeID only_restriction_to_node =
|
turn_candidates = optimizeCandidates(edge_form_u, turn_candidates);
|
||||||
m_restriction_map->CheckForEmanatingIsOnlyTurn(node_u, node_v);
|
turn_candidates = suppressTurns(edge_form_u, turn_candidates);
|
||||||
const bool is_barrier_node = m_barrier_nodes.find(node_v) != m_barrier_nodes.end();
|
|
||||||
|
|
||||||
for (const EdgeID e2 : m_node_based_graph->GetAdjacentEdgeRange(node_v))
|
const NodeID node_v = m_node_based_graph->GetTarget(edge_form_u);
|
||||||
|
|
||||||
|
for (const auto turn : turn_candidates)
|
||||||
{
|
{
|
||||||
if (m_node_based_graph->GetEdgeData(e2).reversed)
|
if (!turn.valid)
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
const NodeID node_w = m_node_based_graph->GetTarget(e2);
|
|
||||||
|
|
||||||
if ((only_restriction_to_node != SPECIAL_NODEID) &&
|
const double turn_angle = turn.angle;
|
||||||
(node_w != only_restriction_to_node))
|
|
||||||
{
|
|
||||||
// We are at an only_-restriction but not at the right turn.
|
|
||||||
++restricted_turns_counter;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_barrier_node)
|
|
||||||
{
|
|
||||||
if (node_u != node_w)
|
|
||||||
{
|
|
||||||
++skipped_barrier_turns_counter;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (node_u == node_w && m_node_based_graph->GetOutDegree(node_v) > 1)
|
|
||||||
{
|
|
||||||
auto number_of_emmiting_bidirectional_edges = 0;
|
|
||||||
for (auto edge : m_node_based_graph->GetAdjacentEdgeRange(node_v))
|
|
||||||
{
|
|
||||||
auto target = m_node_based_graph->GetTarget(edge);
|
|
||||||
auto reverse_edge = m_node_based_graph->FindEdge(target, node_v);
|
|
||||||
if (!m_node_based_graph->GetEdgeData(reverse_edge).reversed)
|
|
||||||
{
|
|
||||||
++number_of_emmiting_bidirectional_edges;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (number_of_emmiting_bidirectional_edges > 1)
|
|
||||||
{
|
|
||||||
++skipped_uturns_counter;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// only add an edge if turn is not a U-turn except when it is
|
|
||||||
// at the end of a dead-end street
|
|
||||||
if (m_restriction_map->CheckIfTurnIsRestricted(node_u, node_v, node_w) &&
|
|
||||||
(only_restriction_to_node == SPECIAL_NODEID) &&
|
|
||||||
(node_w != only_restriction_to_node))
|
|
||||||
{
|
|
||||||
// We are at an only_-restriction but not at the right turn.
|
|
||||||
++restricted_turns_counter;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// only add an edge if turn is not prohibited
|
// only add an edge if turn is not prohibited
|
||||||
const EdgeData &edge_data1 = m_node_based_graph->GetEdgeData(e1);
|
const EdgeData &edge_data1 = m_node_based_graph->GetEdgeData(edge_form_u);
|
||||||
const EdgeData &edge_data2 = m_node_based_graph->GetEdgeData(e2);
|
const EdgeData &edge_data2 = m_node_based_graph->GetEdgeData(turn.eid);
|
||||||
|
|
||||||
BOOST_ASSERT(edge_data1.edge_id != edge_data2.edge_id);
|
BOOST_ASSERT(edge_data1.edge_id != edge_data2.edge_id);
|
||||||
BOOST_ASSERT(!edge_data1.reversed);
|
BOOST_ASSERT(!edge_data1.reversed);
|
||||||
@ -485,23 +459,9 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
|||||||
speed_profile.traffic_signal_penalty);
|
speed_profile.traffic_signal_penalty);
|
||||||
}
|
}
|
||||||
|
|
||||||
// unpack last node of first segment if packed
|
|
||||||
const auto first_coordinate =
|
|
||||||
m_node_info_list[(m_compressed_edge_container.HasEntryForID(e1)
|
|
||||||
? m_compressed_edge_container.GetLastEdgeSourceID(e1)
|
|
||||||
: node_u)];
|
|
||||||
|
|
||||||
// unpack first node of second segment if packed
|
|
||||||
const auto third_coordinate =
|
|
||||||
m_node_info_list[(m_compressed_edge_container.HasEntryForID(e2)
|
|
||||||
? m_compressed_edge_container.GetFirstEdgeTargetID(e2)
|
|
||||||
: node_w)];
|
|
||||||
|
|
||||||
const double turn_angle = util::coordinate_calculation::computeAngle(
|
|
||||||
first_coordinate, m_node_info_list[node_v], third_coordinate);
|
|
||||||
|
|
||||||
const int turn_penalty = GetTurnPenalty(turn_angle, lua_state);
|
const int turn_penalty = GetTurnPenalty(turn_angle, lua_state);
|
||||||
TurnInstruction turn_instruction = AnalyzeTurn(node_u, node_v, node_w, turn_angle);
|
const TurnInstruction turn_instruction = turn.instruction;
|
||||||
|
|
||||||
if (turn_instruction == TurnInstruction::UTurn)
|
if (turn_instruction == TurnInstruction::UTurn)
|
||||||
{
|
{
|
||||||
distance += speed_profile.u_turn_penalty;
|
distance += speed_profile.u_turn_penalty;
|
||||||
@ -509,12 +469,10 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
|||||||
util::DEBUG_UTURN(node_v, m_node_info_list, speed_profile.u_turn_penalty);
|
util::DEBUG_UTURN(node_v, m_node_info_list, speed_profile.u_turn_penalty);
|
||||||
}
|
}
|
||||||
|
|
||||||
util::DEBUG_TURN(node_v, m_node_info_list, first_coordinate, turn_angle,
|
|
||||||
turn_penalty);
|
|
||||||
|
|
||||||
distance += turn_penalty;
|
distance += turn_penalty;
|
||||||
|
|
||||||
const bool edge_is_compressed = m_compressed_edge_container.HasEntryForID(e1);
|
const bool edge_is_compressed =
|
||||||
|
m_compressed_edge_container.HasEntryForID(edge_form_u);
|
||||||
|
|
||||||
if (edge_is_compressed)
|
if (edge_is_compressed)
|
||||||
{
|
{
|
||||||
@ -522,7 +480,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
|||||||
}
|
}
|
||||||
|
|
||||||
original_edge_data_vector.emplace_back(
|
original_edge_data_vector.emplace_back(
|
||||||
(edge_is_compressed ? m_compressed_edge_container.GetPositionForID(e1)
|
(edge_is_compressed ? m_compressed_edge_container.GetPositionForID(edge_form_u)
|
||||||
: node_v),
|
: node_v),
|
||||||
edge_data1.name_id, turn_instruction, edge_is_compressed,
|
edge_data1.name_id, turn_instruction, edge_is_compressed,
|
||||||
edge_data2.travel_mode);
|
edge_data2.travel_mode);
|
||||||
@ -563,7 +521,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
|||||||
if (edge_is_compressed)
|
if (edge_is_compressed)
|
||||||
{
|
{
|
||||||
const auto node_based_edges =
|
const auto node_based_edges =
|
||||||
m_compressed_edge_container.GetBucketReference(e1);
|
m_compressed_edge_container.GetBucketReference(edge_form_u);
|
||||||
NodeID previous = node_u;
|
NodeID previous = node_u;
|
||||||
|
|
||||||
const unsigned node_count = node_based_edges.size() + 1;
|
const unsigned node_count = node_based_edges.size() + 1;
|
||||||
@ -638,6 +596,470 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
|||||||
<< " turns over barriers";
|
<< " turns over barriers";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// requires sorted candidates
|
||||||
|
std::vector<EdgeBasedGraphFactory::TurnCandidate>
|
||||||
|
EdgeBasedGraphFactory::optimizeCandidates(NodeID via_eid,
|
||||||
|
std::vector<TurnCandidate> turn_candidates) const
|
||||||
|
{
|
||||||
|
BOOST_ASSERT_MSG(std::is_sorted(turn_candidates.begin(), turn_candidates.end(),
|
||||||
|
[](const TurnCandidate &left, const TurnCandidate &right)
|
||||||
|
{
|
||||||
|
return left.angle < right.angle;
|
||||||
|
}),
|
||||||
|
"Turn Candidates not sorted by angle.");
|
||||||
|
if (turn_candidates.size() <= 1)
|
||||||
|
return turn_candidates;
|
||||||
|
|
||||||
|
const auto getLeft = [&turn_candidates](std::size_t index)
|
||||||
|
{
|
||||||
|
return (index + 1) % turn_candidates.size();
|
||||||
|
};
|
||||||
|
const auto getRight = [&turn_candidates](std::size_t index)
|
||||||
|
{
|
||||||
|
return (index + turn_candidates.size() - 1) % turn_candidates.size();
|
||||||
|
};
|
||||||
|
|
||||||
|
// handle availability of multiple u-turns (e.g. street with separated small parking roads)
|
||||||
|
if (turn_candidates[0].instruction == TurnInstruction::UTurn && turn_candidates[0].angle == 0)
|
||||||
|
{
|
||||||
|
if (turn_candidates[getLeft(0)].instruction == TurnInstruction::UTurn)
|
||||||
|
turn_candidates[getLeft(0)].instruction = TurnInstruction::TurnSharpLeft;
|
||||||
|
if (turn_candidates[getRight(0)].instruction == TurnInstruction::UTurn)
|
||||||
|
turn_candidates[getRight(0)].instruction = TurnInstruction::TurnSharpRight;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto keepStraight = [](double angle)
|
||||||
|
{
|
||||||
|
return std::abs(angle - 180) < 5;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (std::size_t turn_index = 0; turn_index < turn_candidates.size(); ++turn_index)
|
||||||
|
{
|
||||||
|
auto &turn = turn_candidates[turn_index];
|
||||||
|
if (turn.instruction > TurnInstruction::TurnSlightLeft ||
|
||||||
|
turn.instruction == TurnInstruction::UTurn)
|
||||||
|
continue;
|
||||||
|
auto &left = turn_candidates[getLeft(turn_index)];
|
||||||
|
if (turn.angle == left.angle)
|
||||||
|
{
|
||||||
|
util::SimpleLogger().Write(logDEBUG)
|
||||||
|
<< "[warning] conflicting turn angles, identical road duplicated? "
|
||||||
|
<< m_node_info_list[m_node_based_graph->GetTarget(via_eid)].lat << " "
|
||||||
|
<< m_node_info_list[m_node_based_graph->GetTarget(via_eid)].lon << std::endl;
|
||||||
|
}
|
||||||
|
if (isConflict(turn.instruction, left.instruction))
|
||||||
|
{
|
||||||
|
// begin of a conflicting region
|
||||||
|
std::size_t conflict_begin = turn_index;
|
||||||
|
std::size_t conflict_end = getLeft(turn_index);
|
||||||
|
std::size_t conflict_size = 2;
|
||||||
|
while (
|
||||||
|
isConflict(turn_candidates[getLeft(conflict_end)].instruction, turn.instruction) &&
|
||||||
|
conflict_size < turn_candidates.size())
|
||||||
|
{
|
||||||
|
conflict_end = getLeft(conflict_end);
|
||||||
|
++conflict_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
turn_index = (conflict_end < conflict_begin) ? turn_candidates.size() : conflict_end;
|
||||||
|
|
||||||
|
if (conflict_size > 3)
|
||||||
|
{
|
||||||
|
// check if some turns are invalid to find out about good handling
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &instruction_left_of_end = turn_candidates[getLeft(conflict_end)].instruction;
|
||||||
|
auto &instruction_right_of_begin =
|
||||||
|
turn_candidates[getRight(conflict_begin)].instruction;
|
||||||
|
auto &candidate_at_end = turn_candidates[conflict_end];
|
||||||
|
auto &candidate_at_begin = turn_candidates[conflict_begin];
|
||||||
|
if (conflict_size == 2)
|
||||||
|
{
|
||||||
|
if (turn.instruction == TurnInstruction::GoStraight)
|
||||||
|
{
|
||||||
|
if (instruction_left_of_end != TurnInstruction::TurnSlightLeft &&
|
||||||
|
instruction_right_of_begin != TurnInstruction::TurnSlightRight)
|
||||||
|
{
|
||||||
|
std::int32_t resolved_count = 0;
|
||||||
|
//uses side-effects in resolve
|
||||||
|
if (!keepStraight(candidate_at_end.angle) &&
|
||||||
|
!resolve(candidate_at_end.instruction, instruction_left_of_end,
|
||||||
|
RESOLVE_TO_LEFT))
|
||||||
|
util::SimpleLogger().Write(logDEBUG) << "[warning] failed to resolve conflict";
|
||||||
|
else
|
||||||
|
++resolved_count;
|
||||||
|
//uses side-effects in resolve
|
||||||
|
if (!keepStraight(candidate_at_begin.angle) &&
|
||||||
|
!resolve(candidate_at_begin.instruction, instruction_right_of_begin,
|
||||||
|
RESOLVE_TO_RIGHT))
|
||||||
|
util::SimpleLogger().Write(logDEBUG) << "[warning] failed to resolve conflict";
|
||||||
|
else
|
||||||
|
++resolved_count;
|
||||||
|
if (resolved_count >= 1 &&
|
||||||
|
(!keepStraight(candidate_at_begin.angle) ||
|
||||||
|
!keepStraight(candidate_at_end.angle))) // should always be the
|
||||||
|
// case, theoretically
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (candidate_at_begin.confidence < candidate_at_end.confidence)
|
||||||
|
{ // if right shift is cheaper, or only option
|
||||||
|
if (resolve(candidate_at_begin.instruction, instruction_right_of_begin,
|
||||||
|
RESOLVE_TO_RIGHT))
|
||||||
|
continue;
|
||||||
|
else if (resolve(candidate_at_end.instruction, instruction_left_of_end,
|
||||||
|
RESOLVE_TO_LEFT))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (resolve(candidate_at_end.instruction, instruction_left_of_end,
|
||||||
|
RESOLVE_TO_LEFT))
|
||||||
|
continue;
|
||||||
|
else if (resolve(candidate_at_begin.instruction, instruction_right_of_begin,
|
||||||
|
RESOLVE_TO_RIGHT))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (isSlightTurn(turn.instruction) || isSharpTurn(turn.instruction))
|
||||||
|
{
|
||||||
|
auto resolve_direction =
|
||||||
|
(turn.instruction == TurnInstruction::TurnSlightRight ||
|
||||||
|
turn.instruction == TurnInstruction::TurnSharpLeft)
|
||||||
|
? RESOLVE_TO_RIGHT
|
||||||
|
: RESOLVE_TO_LEFT;
|
||||||
|
if (resolve_direction == RESOLVE_TO_RIGHT &&
|
||||||
|
resolveTransitive(
|
||||||
|
candidate_at_begin.instruction, instruction_right_of_begin,
|
||||||
|
turn_candidates[getRight(getRight(conflict_begin))].instruction,
|
||||||
|
RESOLVE_TO_RIGHT))
|
||||||
|
continue;
|
||||||
|
else if (resolve_direction == RESOLVE_TO_LEFT &&
|
||||||
|
resolveTransitive(
|
||||||
|
candidate_at_end.instruction, instruction_left_of_end,
|
||||||
|
turn_candidates[getLeft(getLeft(conflict_end))].instruction,
|
||||||
|
RESOLVE_TO_LEFT))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (conflict_size >= 3)
|
||||||
|
{
|
||||||
|
// a conflict of size larger than three cannot be handled with the current
|
||||||
|
// model.
|
||||||
|
// Handle it as best as possible and keep the rest of the conflicting turns
|
||||||
|
if (conflict_size > 3)
|
||||||
|
{
|
||||||
|
NodeID conflict_location = m_node_based_graph->GetTarget(via_eid);
|
||||||
|
util::SimpleLogger().Write(logDEBUG)
|
||||||
|
<< "[warning] found conflict larget than size three at "
|
||||||
|
<< m_node_info_list[conflict_location].lat << ", "
|
||||||
|
<< m_node_info_list[conflict_location].lon;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!resolve(candidate_at_begin.instruction, instruction_right_of_begin,
|
||||||
|
RESOLVE_TO_RIGHT))
|
||||||
|
{
|
||||||
|
if (isSlightTurn(turn.instruction))
|
||||||
|
resolveTransitive(
|
||||||
|
candidate_at_begin.instruction, instruction_right_of_begin,
|
||||||
|
turn_candidates[getRight(getRight(conflict_begin))].instruction,
|
||||||
|
RESOLVE_TO_RIGHT);
|
||||||
|
else if (isSharpTurn(turn.instruction))
|
||||||
|
resolveTransitive(
|
||||||
|
candidate_at_end.instruction, instruction_left_of_end,
|
||||||
|
turn_candidates[getLeft(getLeft(conflict_end))].instruction,
|
||||||
|
RESOLVE_TO_LEFT);
|
||||||
|
}
|
||||||
|
if (!resolve(candidate_at_end.instruction, instruction_left_of_end,
|
||||||
|
RESOLVE_TO_LEFT))
|
||||||
|
{
|
||||||
|
if (isSlightTurn(turn.instruction))
|
||||||
|
resolveTransitive(
|
||||||
|
candidate_at_end.instruction, instruction_left_of_end,
|
||||||
|
turn_candidates[getLeft(getLeft(conflict_end))].instruction,
|
||||||
|
RESOLVE_TO_LEFT);
|
||||||
|
else if (isSharpTurn(turn.instruction))
|
||||||
|
resolveTransitive(
|
||||||
|
candidate_at_begin.instruction, instruction_right_of_begin,
|
||||||
|
turn_candidates[getRight(getRight(conflict_begin))].instruction,
|
||||||
|
RESOLVE_TO_RIGHT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return turn_candidates;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EdgeBasedGraphFactory::isObviousChoice(EdgeID via_eid,
|
||||||
|
std::size_t turn_index,
|
||||||
|
const std::vector<TurnCandidate> &turn_candidates) const
|
||||||
|
{
|
||||||
|
const auto getLeft = [&turn_candidates](std::size_t index)
|
||||||
|
{
|
||||||
|
return (index + 1) % turn_candidates.size();
|
||||||
|
};
|
||||||
|
const auto getRight = [&turn_candidates](std::size_t index)
|
||||||
|
{
|
||||||
|
return (index + turn_candidates.size() - 1) % turn_candidates.size();
|
||||||
|
};
|
||||||
|
const auto &candidate = turn_candidates[turn_index];
|
||||||
|
const EdgeData &in_data = m_node_based_graph->GetEdgeData(via_eid);
|
||||||
|
const EdgeData &out_data = m_node_based_graph->GetEdgeData(candidate.eid);
|
||||||
|
const auto &candidate_to_the_left = turn_candidates[getLeft(turn_index)];
|
||||||
|
|
||||||
|
const auto &candidate_to_the_right = turn_candidates[getRight(turn_index)];
|
||||||
|
|
||||||
|
const auto hasValidRatio = [](const TurnCandidate &left, const TurnCandidate ¢er,
|
||||||
|
const TurnCandidate &right)
|
||||||
|
{
|
||||||
|
auto angle_left = (left.angle > 180) ? angularDeviation(left.angle, STRAIGHT_ANGLE) : 180;
|
||||||
|
auto angle_right =
|
||||||
|
(right.angle < 180) ? angularDeviation(right.angle, STRAIGHT_ANGLE) : 180;
|
||||||
|
auto self_angle = angularDeviation(center.angle, STRAIGHT_ANGLE);
|
||||||
|
return angularDeviation(center.angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE &&
|
||||||
|
((center.angle < STRAIGHT_ANGLE)
|
||||||
|
? (angle_right > self_angle && angle_left / self_angle > DISTINCTION_RATIO)
|
||||||
|
: (angle_left > self_angle && angle_right / self_angle > DISTINCTION_RATIO));
|
||||||
|
};
|
||||||
|
// only valid turn
|
||||||
|
|
||||||
|
return turn_candidates.size() == 1 ||
|
||||||
|
// only non u-turn
|
||||||
|
(turn_candidates.size() == 2 &&
|
||||||
|
candidate_to_the_left.instruction == TurnInstruction::UTurn) || // nearly straight turn
|
||||||
|
angularDeviation(candidate.angle, STRAIGHT_ANGLE) < MAXIMAL_ALLOWED_NO_TURN_DEVIATION ||
|
||||||
|
hasValidRatio(candidate_to_the_left, candidate, candidate_to_the_right) ||
|
||||||
|
(in_data.name_id != 0 && in_data.name_id == out_data.name_id &&
|
||||||
|
angularDeviation(candidate.angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<EdgeBasedGraphFactory::TurnCandidate>
|
||||||
|
EdgeBasedGraphFactory::suppressTurns(EdgeID via_eid,
|
||||||
|
std::vector<TurnCandidate> turn_candidates) const
|
||||||
|
{
|
||||||
|
// remove invalid candidates
|
||||||
|
BOOST_ASSERT_MSG(std::is_sorted(turn_candidates.begin(), turn_candidates.end(),
|
||||||
|
[](const TurnCandidate &left, const TurnCandidate &right)
|
||||||
|
{
|
||||||
|
return left.angle < right.angle;
|
||||||
|
}),
|
||||||
|
"Turn Candidates not sorted by angle.");
|
||||||
|
const auto end_valid = std::remove_if(turn_candidates.begin(), turn_candidates.end(),
|
||||||
|
[](const TurnCandidate &candidate)
|
||||||
|
{
|
||||||
|
return !candidate.valid;
|
||||||
|
});
|
||||||
|
turn_candidates.erase(end_valid, turn_candidates.end());
|
||||||
|
|
||||||
|
const auto getLeft = [&turn_candidates](std::size_t index)
|
||||||
|
{
|
||||||
|
return (index + 1) % turn_candidates.size();
|
||||||
|
};
|
||||||
|
const auto getRight = [&turn_candidates](std::size_t index)
|
||||||
|
{
|
||||||
|
return (index + turn_candidates.size() - 1) % turn_candidates.size();
|
||||||
|
};
|
||||||
|
|
||||||
|
const EdgeData &in_data = m_node_based_graph->GetEdgeData(via_eid);
|
||||||
|
|
||||||
|
bool has_obvious_with_same_name = false;
|
||||||
|
double obvious_with_same_name_angle = 0;
|
||||||
|
for (std::size_t turn_index = 0; turn_index < turn_candidates.size(); ++turn_index)
|
||||||
|
{
|
||||||
|
if (m_node_based_graph->GetEdgeData(turn_candidates[turn_index].eid).name_id ==
|
||||||
|
in_data.name_id &&
|
||||||
|
isObviousChoice(via_eid, turn_index, turn_candidates))
|
||||||
|
{
|
||||||
|
has_obvious_with_same_name = true;
|
||||||
|
obvious_with_same_name_angle = turn_candidates[turn_index].angle;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::size_t turn_index = 0; turn_index < turn_candidates.size(); ++turn_index)
|
||||||
|
{
|
||||||
|
auto &candidate = turn_candidates[turn_index];
|
||||||
|
const EdgeData &out_data = m_node_based_graph->GetEdgeData(candidate.eid);
|
||||||
|
if (candidate.valid && candidate.instruction != TurnInstruction::UTurn)
|
||||||
|
{
|
||||||
|
// TODO road category would be useful to indicate obviousness of turn
|
||||||
|
// check if turn can be omitted or at least changed
|
||||||
|
const auto &left = turn_candidates[getLeft(turn_index)];
|
||||||
|
const auto &right = turn_candidates[getRight(turn_index)];
|
||||||
|
|
||||||
|
// make very slight instructions straight, if they are the only valid choice going with
|
||||||
|
// at most a slight turn
|
||||||
|
if (candidate.instruction < TurnInstruction::ReachViaLocation &&
|
||||||
|
(!isSlightTurn(getTurnDirection(left.angle)) || !left.valid) &&
|
||||||
|
(!isSlightTurn(getTurnDirection(right.angle)) || !right.valid) &&
|
||||||
|
angularDeviation(candidate.angle, STRAIGHT_ANGLE) < FUZZY_STRAIGHT_ANGLE)
|
||||||
|
candidate.instruction = TurnInstruction::GoStraight;
|
||||||
|
|
||||||
|
// TODO this smaller comparison for turns is DANGEROUS, has to be revised if turn
|
||||||
|
// instructions change
|
||||||
|
if (candidate.instruction < TurnInstruction::ReachViaLocation)
|
||||||
|
{
|
||||||
|
if (in_data.travel_mode ==
|
||||||
|
out_data.travel_mode) // make sure to always announce mode changes
|
||||||
|
{
|
||||||
|
if (isObviousChoice(via_eid, turn_index, turn_candidates))
|
||||||
|
{
|
||||||
|
|
||||||
|
if (in_data.name_id == out_data.name_id) // same road
|
||||||
|
{
|
||||||
|
candidate.instruction = TurnInstruction::NoTurn;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (!has_obvious_with_same_name)
|
||||||
|
{
|
||||||
|
// TODO discuss, we might want to keep the current name of the turn. But
|
||||||
|
// this would mean emitting a turn when you just keep on a road
|
||||||
|
candidate.instruction = TurnInstruction::NameChanges;
|
||||||
|
}
|
||||||
|
else if (candidate.angle < obvious_with_same_name_angle)
|
||||||
|
candidate.instruction = TurnInstruction::TurnSlightRight;
|
||||||
|
else
|
||||||
|
candidate.instruction = TurnInstruction::TurnSlightLeft;
|
||||||
|
}
|
||||||
|
else if (candidate.instruction == TurnInstruction::GoStraight &&
|
||||||
|
has_obvious_with_same_name)
|
||||||
|
{
|
||||||
|
if (candidate.angle < obvious_with_same_name_angle)
|
||||||
|
candidate.instruction = TurnInstruction::TurnSlightRight;
|
||||||
|
else
|
||||||
|
candidate.instruction = TurnInstruction::TurnSlightLeft;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return turn_candidates;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<EdgeBasedGraphFactory::TurnCandidate>
|
||||||
|
EdgeBasedGraphFactory::getTurnCandidates(NodeID from_node, EdgeID via_eid)
|
||||||
|
{
|
||||||
|
std::vector<TurnCandidate> turn_candidates;
|
||||||
|
const NodeID turn_node = m_node_based_graph->GetTarget(via_eid);
|
||||||
|
const NodeID only_restriction_to_node =
|
||||||
|
m_restriction_map->CheckForEmanatingIsOnlyTurn(from_node, turn_node);
|
||||||
|
const bool is_barrier_node = m_barrier_nodes.find(turn_node) != m_barrier_nodes.end();
|
||||||
|
|
||||||
|
for (const EdgeID onto_edge : m_node_based_graph->GetAdjacentEdgeRange(turn_node))
|
||||||
|
{
|
||||||
|
bool turn_is_valid = true;
|
||||||
|
if (m_node_based_graph->GetEdgeData(onto_edge).reversed)
|
||||||
|
{
|
||||||
|
turn_is_valid = false;
|
||||||
|
}
|
||||||
|
const NodeID to_node = m_node_based_graph->GetTarget(onto_edge);
|
||||||
|
|
||||||
|
if (turn_is_valid && (only_restriction_to_node != SPECIAL_NODEID) &&
|
||||||
|
(to_node != only_restriction_to_node))
|
||||||
|
{
|
||||||
|
// We are at an only_-restriction but not at the right turn.
|
||||||
|
++restricted_turns_counter;
|
||||||
|
turn_is_valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (turn_is_valid)
|
||||||
|
{
|
||||||
|
if (is_barrier_node)
|
||||||
|
{
|
||||||
|
if (from_node != to_node)
|
||||||
|
{
|
||||||
|
++skipped_barrier_turns_counter;
|
||||||
|
turn_is_valid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (from_node == to_node && m_node_based_graph->GetOutDegree(turn_node) > 1)
|
||||||
|
{
|
||||||
|
auto number_of_emmiting_bidirectional_edges = 0;
|
||||||
|
for (auto edge : m_node_based_graph->GetAdjacentEdgeRange(turn_node))
|
||||||
|
{
|
||||||
|
auto target = m_node_based_graph->GetTarget(edge);
|
||||||
|
auto reverse_edge = m_node_based_graph->FindEdge(target, turn_node);
|
||||||
|
if (!m_node_based_graph->GetEdgeData(reverse_edge).reversed)
|
||||||
|
{
|
||||||
|
++number_of_emmiting_bidirectional_edges;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (number_of_emmiting_bidirectional_edges > 1)
|
||||||
|
{
|
||||||
|
++skipped_uturns_counter;
|
||||||
|
turn_is_valid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// only add an edge if turn is not a U-turn except when it is
|
||||||
|
// at the end of a dead-end street
|
||||||
|
if (m_restriction_map->CheckIfTurnIsRestricted(from_node, turn_node, to_node) &&
|
||||||
|
(only_restriction_to_node == SPECIAL_NODEID) && (to_node != only_restriction_to_node))
|
||||||
|
{
|
||||||
|
// We are at an only_-restriction but not at the right turn.
|
||||||
|
++restricted_turns_counter;
|
||||||
|
turn_is_valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// unpack first node of second segment if packed
|
||||||
|
|
||||||
|
const auto first_coordinate =
|
||||||
|
getRepresentativeCoordinate(from_node, turn_node, via_eid, INVERT);
|
||||||
|
const auto third_coordinate =
|
||||||
|
getRepresentativeCoordinate(turn_node, to_node, onto_edge, !INVERT);
|
||||||
|
|
||||||
|
const auto angle = util::coordinate_calculation::computeAngle(
|
||||||
|
first_coordinate, m_node_info_list[turn_node], third_coordinate);
|
||||||
|
|
||||||
|
const auto turn = AnalyzeTurn(from_node, via_eid, turn_node, onto_edge, to_node, angle);
|
||||||
|
|
||||||
|
auto confidence = getTurnConfidence(angle, turn);
|
||||||
|
if (!turn_is_valid)
|
||||||
|
confidence *= 0.8; // makes invalid turns more likely to be resolved in conflicts
|
||||||
|
|
||||||
|
turn_candidates.push_back({onto_edge, turn_is_valid, angle, turn, confidence});
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto ByAngle = [](const TurnCandidate &first, const TurnCandidate second)
|
||||||
|
{
|
||||||
|
return first.angle < second.angle;
|
||||||
|
};
|
||||||
|
std::sort(std::begin(turn_candidates), std::end(turn_candidates), ByAngle);
|
||||||
|
|
||||||
|
const auto getLeft = [&](std::size_t index)
|
||||||
|
{
|
||||||
|
return (index + 1) % turn_candidates.size();
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto getRight = [&](std::size_t index)
|
||||||
|
{
|
||||||
|
return (index + turn_candidates.size() - 1) % turn_candidates.size();
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto isInvalidEquivalent = [&](std::size_t this_turn, std::size_t valid_turn)
|
||||||
|
{
|
||||||
|
if (!turn_candidates[valid_turn].valid || turn_candidates[this_turn].valid)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return angularDeviation(turn_candidates[this_turn].angle,
|
||||||
|
turn_candidates[valid_turn].angle) < NARROW_TURN_ANGLE;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (std::size_t index = 0; index < turn_candidates.size(); ++index)
|
||||||
|
{
|
||||||
|
if (isInvalidEquivalent(index, getRight(index)) ||
|
||||||
|
isInvalidEquivalent(index, getLeft(index)))
|
||||||
|
{
|
||||||
|
turn_candidates.erase(turn_candidates.begin() + index);
|
||||||
|
--index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return turn_candidates;
|
||||||
|
};
|
||||||
|
|
||||||
int EdgeBasedGraphFactory::GetTurnPenalty(double angle, lua_State *lua_state) const
|
int EdgeBasedGraphFactory::GetTurnPenalty(double angle, lua_State *lua_state) const
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -658,22 +1080,22 @@ int EdgeBasedGraphFactory::GetTurnPenalty(double angle, lua_State *lua_state) co
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// node_u -- (edge_1) --> node_v -- (edge_2) --> node_w
|
||||||
TurnInstruction EdgeBasedGraphFactory::AnalyzeTurn(const NodeID node_u,
|
TurnInstruction EdgeBasedGraphFactory::AnalyzeTurn(const NodeID node_u,
|
||||||
|
const EdgeID edge1,
|
||||||
const NodeID node_v,
|
const NodeID node_v,
|
||||||
|
const EdgeID edge2,
|
||||||
const NodeID node_w,
|
const NodeID node_w,
|
||||||
const double angle) const
|
const double angle) const
|
||||||
{
|
{
|
||||||
|
|
||||||
|
const EdgeData &data1 = m_node_based_graph->GetEdgeData(edge1);
|
||||||
|
const EdgeData &data2 = m_node_based_graph->GetEdgeData(edge2);
|
||||||
if (node_u == node_w)
|
if (node_u == node_w)
|
||||||
{
|
{
|
||||||
return TurnInstruction::UTurn;
|
return TurnInstruction::UTurn;
|
||||||
}
|
}
|
||||||
|
|
||||||
const EdgeID edge1 = m_node_based_graph->FindEdge(node_u, node_v);
|
|
||||||
const EdgeID edge2 = m_node_based_graph->FindEdge(node_v, node_w);
|
|
||||||
|
|
||||||
const EdgeData &data1 = m_node_based_graph->GetEdgeData(edge1);
|
|
||||||
const EdgeData &data2 = m_node_based_graph->GetEdgeData(edge2);
|
|
||||||
|
|
||||||
// roundabouts need to be handled explicitely
|
// roundabouts need to be handled explicitely
|
||||||
if (data1.roundabout && data2.roundabout)
|
if (data1.roundabout && data2.roundabout)
|
||||||
{
|
{
|
||||||
@ -700,19 +1122,89 @@ TurnInstruction EdgeBasedGraphFactory::AnalyzeTurn(const NodeID node_u,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If street names stay the same and if we are certain that it is not a
|
// assign a designated turn angle instruction purely based on the angle
|
||||||
// a segment of a roundabout, we skip it.
|
|
||||||
if (data1.name_id == data2.name_id && data1.travel_mode == data2.travel_mode)
|
|
||||||
{
|
|
||||||
// TODO: Here we should also do a small graph exploration to check for
|
|
||||||
// more complex situations
|
|
||||||
if (0 != data1.name_id || m_node_based_graph->GetOutDegree(node_v) <= 2)
|
|
||||||
{
|
|
||||||
return TurnInstruction::NoTurn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return getTurnDirection(angle);
|
return getTurnDirection(angle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QueryNode EdgeBasedGraphFactory::getRepresentativeCoordinate(const NodeID src,
|
||||||
|
const NodeID tgt,
|
||||||
|
const EdgeID via_eid,
|
||||||
|
bool INVERTED) const
|
||||||
|
{
|
||||||
|
if (m_compressed_edge_container.HasEntryForID(via_eid))
|
||||||
|
{
|
||||||
|
util::FixedPointCoordinate prev = util::FixedPointCoordinate(
|
||||||
|
m_node_info_list[INVERTED ? tgt : src].lat,
|
||||||
|
m_node_info_list[INVERTED ? tgt : src].lon),
|
||||||
|
cur;
|
||||||
|
// walk along the edge for the first 5 meters
|
||||||
|
const auto &geometry = m_compressed_edge_container.GetBucketReference(via_eid);
|
||||||
|
double dist = 0;
|
||||||
|
double this_dist = 0;
|
||||||
|
NodeID prev_id = INVERTED ? tgt : src;
|
||||||
|
|
||||||
|
const auto selectBestCandidate = [this](const NodeID current, const double current_distance,
|
||||||
|
const NodeID previous,
|
||||||
|
const double previous_distance)
|
||||||
|
{
|
||||||
|
if (current_distance < DESIRED_SEGMENT_LENGTH ||
|
||||||
|
current_distance - DESIRED_SEGMENT_LENGTH <
|
||||||
|
DESIRED_SEGMENT_LENGTH - previous_distance ||
|
||||||
|
previous_distance < MINIMAL_SEGMENT_LENGTH)
|
||||||
|
{
|
||||||
|
return m_node_info_list[current];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return m_node_info_list[previous];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (INVERTED)
|
||||||
|
{
|
||||||
|
for (auto itr = geometry.rbegin(), end = geometry.rend(); itr != end; ++itr)
|
||||||
|
{
|
||||||
|
const auto compressed_node = *itr;
|
||||||
|
cur = util::FixedPointCoordinate(m_node_info_list[compressed_node.first].lat,
|
||||||
|
m_node_info_list[compressed_node.first].lon);
|
||||||
|
this_dist = util::coordinate_calculation::haversineDistance(prev, cur);
|
||||||
|
if (dist + this_dist > DESIRED_SEGMENT_LENGTH)
|
||||||
|
{
|
||||||
|
return selectBestCandidate(compressed_node.first, dist + this_dist, prev_id,
|
||||||
|
dist);
|
||||||
|
}
|
||||||
|
dist += this_dist;
|
||||||
|
prev = cur;
|
||||||
|
prev_id = compressed_node.first;
|
||||||
|
}
|
||||||
|
cur = util::FixedPointCoordinate(m_node_info_list[src].lat, m_node_info_list[src].lon);
|
||||||
|
this_dist = util::coordinate_calculation::haversineDistance(prev, cur);
|
||||||
|
return selectBestCandidate(src, dist + this_dist, prev_id, dist);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (auto itr = geometry.begin(), end = geometry.end(); itr != end; ++itr)
|
||||||
|
{
|
||||||
|
const auto compressed_node = *itr;
|
||||||
|
cur = util::FixedPointCoordinate(m_node_info_list[compressed_node.first].lat,
|
||||||
|
m_node_info_list[compressed_node.first].lon);
|
||||||
|
this_dist = util::coordinate_calculation::haversineDistance(prev, cur);
|
||||||
|
if (dist + this_dist > DESIRED_SEGMENT_LENGTH)
|
||||||
|
{
|
||||||
|
return selectBestCandidate(compressed_node.first, dist + this_dist, prev_id,
|
||||||
|
dist);
|
||||||
|
}
|
||||||
|
dist += this_dist;
|
||||||
|
prev = cur;
|
||||||
|
prev_id = compressed_node.first;
|
||||||
|
}
|
||||||
|
cur = util::FixedPointCoordinate(m_node_info_list[tgt].lat, m_node_info_list[tgt].lon);
|
||||||
|
this_dist = util::coordinate_calculation::haversineDistance(prev, cur);
|
||||||
|
return selectBestCandidate(tgt, dist + this_dist, prev_id, dist);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// default: If the edge is very short, or we do not have a compressed geometry
|
||||||
|
return m_node_info_list[INVERTED ? src : tgt];
|
||||||
}
|
}
|
||||||
}
|
} // namespace extractor
|
||||||
|
} // namespace osrm
|
||||||
|
Loading…
Reference in New Issue
Block a user