Compare commits

...

56 Commits

Author SHA1 Message Date
karenzshea 607fbce1ef bump version to 5.15.1 2018-01-29 15:46:13 -05:00
Karen Shea 06330a694e Only run step collapsing based on original waypoints parameter (#4829) 2018-01-29 15:43:32 -05:00
Michael Krasnyk cbecdf8a4f Set type of trivial intersections where classes change to Suppressed
... instead of NoTurn
2018-01-29 15:39:25 -05:00
Michael Krasnyk dc26f7d8b9 Release 5.15.0 2018-01-22 07:35:00 +01:00
Michael Krasnyk 791f6d02e1 Make 5.15.0-rc.3 2018-01-19 20:05:24 +01:00
Kajari Ghosh 7b7871d5aa Refactor isThroughStreet/Intersection options (#4751)
* refactor isThroughStreet 
* refactor HaveIdenticalName
* fix a typo in the unit tests
2018-01-19 20:01:47 +01:00
Michael Krasnyk f7613d77d5 Make 5.15.0-rc.2 2018-01-19 16:04:59 +01:00
Michael Krasnyk c80edef46c Ignore no_*_on_red turn restrictions (#4804) 2018-01-19 16:04:31 +01:00
Kajari Ghosh cfae4a1923 add tunnel as a class in lua (#4812) 2018-01-19 16:03:42 +01:00
Michael Krasnyk fd9d5af7e0 Fix formatting 2018-01-19 14:29:51 +01:00
Michael Krasnyk c81942c167 Add assertion and adjust unit test expectations check 2018-01-19 14:29:46 +01:00
karenzshea c5c4a1b4fe add unit test for split submatch waypoints indices 2018-01-19 14:29:41 +01:00
karenzshea eb13041784 only adjust waypoint index in tracepoints when waypoints parameter is specified 2018-01-19 14:29:35 +01:00
Huyen Chau Nguyen fa1f121b02 Remove "can't parse value" log messages (#4810)
* remove log output of measure.lua
* remove unneccessary return
* quick fix of #4794
2018-01-19 12:19:09 +01:00
Patrick Niklaus c26642de6e Fix overflow on zero duration segments, fixes #4283.
As a form of smoothing we use the previous speed value instead.
This makes sense because the zero duration segments have to be very
short, potentially also zero length.
2018-01-18 16:31:21 +00:00
Michael Krasnyk ef1fc8a757 Make 5.15.0-rc.1 2018-01-16 13:02:32 +01:00
Patrick Niklaus 168e313f73 Correctly mark edges going to/from core with exclude flags 2018-01-11 12:46:49 +00:00
Patrick Niklaus 30f910e861 Add regression test from @fijemax 2018-01-11 12:46:49 +00:00
Huyen Chau Nguyen e998c1193d Update docs for process_turn in the profile docs (#4786)
* update correct attributes available in process turn

* make travel mode in ExtractedTurn const

* fix mismatch of struct and class
2018-01-10 11:31:24 +01:00
Michael Krasnyk a8f3474996 Use unsigned type in percent_encoding to prevent overflow for %80..%ff
Related fix in Boost.Spirit https://github.com/boostorg/spirit/commit/80414bc68868b27e1fd865cbbbc1a6db229825a5
2018-01-08 12:09:33 +00:00
karenzshea 55cc06fd8b Add waypoints parameter to matching service, returns map matching result
with selective input coordinates as waypoints
2018-01-08 12:28:20 +01:00
Karen Shea 8883d8cc56 Update http.md 2018-01-08 12:21:41 +01:00
Mateusz Loskot 5b2af6ef09 Restore use of user-provided osmium::thread::Pool instance
Pool instance has been removed from Reader ctor parameters
list in PR #4438, presumably unintentionally.
It is required to prevent potential deadlock during
Pool shutdown as explained in PR #4452.
2018-01-07 18:29:43 +01:00
Patrick Niklaus 6d801e7086 Bumped version to latest.4 2018-01-05 17:35:11 +01:00
Michael Krasnyk 17eb7052ba Make distance_between_roads symmetrical 2018-01-05 17:35:11 +01:00
Michael Krasnyk 330f25eddb Reduce extraction distance to 120 meters
For intersection at https://www.openstreetmap.org/node/65299217
`are_parallel` in MergableRoadDetector::HaveSameDirection is false
for South Van Ness Avenue with 150 meters
2018-01-05 17:35:11 +01:00
Michael Krasnyk 08b88bad63 Still use low precision bearings 2018-01-05 17:35:11 +01:00
Michael Krasnyk 153f9b02a5 Add low precision intersection views back 2018-01-05 17:35:11 +01:00
Michael Krasnyk 0568dca4a3 Adjust to review findings 2018-01-05 17:35:11 +01:00
Michael Krasnyk 60ef179d18 Remove handling of U-turns on motorways 2018-01-05 17:35:11 +01:00
Michael Krasnyk c64904f5ea Move roads re-ordering to convertToIntersectionView 2018-01-05 17:35:11 +01:00
Michael Krasnyk 4b9e3a8068 Remove IntersectionGenerator 2018-01-05 17:35:11 +01:00
Michael Krasnyk db7c76d04d Remove GetConnectedRoads from IntersectionGenerator 2018-01-05 17:35:11 +01:00
Michael Krasnyk cc1a5ea78d Remove usage of IntersectionGenerator in EBGF 2018-01-05 17:35:11 +01:00
Michael Krasnyk 9c033ff461 Free functions for guidance intersections analysis 2018-01-05 17:35:11 +01:00
Michael Krasnyk 3c3322173c Allow to specify empty bearings string in input parameters 2018-01-05 17:35:11 +01:00
Michael Krasnyk e805f85407 Print statistics only for allowed turns 2018-01-05 17:35:11 +01:00
Michael Krasnyk 4d54456f66 Don't fail if a registered printer has no name 2018-01-05 17:35:11 +01:00
Daniel J. Hofmann 7359d0542f Updates readme link for demo server wiki page, closes #4773 2018-01-05 15:19:33 +01:00
Mateusz Loskot da4fb13aa3 Tidy up #include-s for Intel TBB headers
Remove unused and add missing headers.
2018-01-05 11:00:31 +01:00
Michael Krasnyk 3649ab4d31 Use default maxspeed value for ua:urban 2018-01-02 20:43:42 +01:00
Michael Krasnyk 9e9e3fb1e4 Change default urban speed in Ukraine to 50kmh
From 1/1/2018 60kmh speed limit changed to 50kmh
http://zakon3.rada.gov.ua/laws/show/883-2017-%D0%BF
2018-01-02 15:34:05 +01:00
Darafei Praliaskouski e73aa01725 Belarus speed limits (#4764)
* Belarus speed limits

https://en.wikipedia.org/wiki/Speed_limits_in_Belarus

* taginfo.json update

* Update CHANGELOG.md
2018-01-02 14:49:36 +01:00
Michael Krasnyk a5353c7179 Use consistent EBG node weights in duplicated via nodes 2018-01-02 14:26:35 +01:00
Michael Krasnyk a5e0d7011b Update CHANGELOG entry 2017-12-26 16:18:27 +01:00
Michael Krasnyk 742c32d936 Don't use to_string conversion in requiresNameAnnounced 2017-12-26 16:18:27 +01:00
Michael Krasnyk 3dec680058 Remove check unnamed check in sliproad handler 2017-12-26 16:18:27 +01:00
Michael Krasnyk 9237430be2 Test for sliproads to roads with empty names 2017-12-26 16:18:27 +01:00
Patrick Niklaus 12d1d84b11 Add test case for sliproads converted from forks 2017-12-26 16:18:27 +01:00
Patrick Niklaus 6dd029e6ea Restore original intend of roundabout test
When doing the new Lua version refactor we changed the expectation to
use `continue uturn` instead of making a whole circle in the roundabout
as the original test case. This was only the shortest path since there
was no roundabout penalty.
2017-12-26 13:28:12 +00:00
Patrick Niklaus e45d44cb8e Make sure we only count turns as UTurns for the turn_function 2017-12-26 13:28:12 +00:00
Kajari Ghosh 84b6ef4340 add osrm-routed and node-osrm flags to configure mapmatching radius limit (#4721) 2017-12-20 16:53:43 +05:30
Frédéric Rodrigo 5af776d963 Car Profile. Route by supporting physical limitation of height and weight 2017-12-19 13:51:33 +00:00
Frédéric Rodrigo ddf11cc2cc Profile: stronger unit parsing 2017-12-19 13:51:33 +00:00
Denis Koronchik c7b1d0c131 Add support of maxweight in profiles 2017-12-19 13:51:33 +00:00
Denis Koronchik 423a4ef326 Add maxheight/maxwidth support into profiles 2017-12-19 13:51:33 +00:00
110 changed files with 3560 additions and 2014 deletions
+1 -1
View File
@@ -12,7 +12,7 @@ notifications:
branches:
only:
- master
- 5.15
# enable building tags
- /^v\d+\.\d+(\.\d+)?(-\S*)?$/
+42 -3
View File
@@ -1,7 +1,46 @@
# UNRELEASED
- Changes from 5.14.1:
- Bugfixes:
# 5.15.1
- Changes from 5.15.0:
- Bugfixes:
- FIXED: Segfault in map matching when RouteLeg collapsing code is run on a match with multiple submatches
- Guidance:
- Set type of trivial intersections where classes change to Suppressed instead of NoTurn
# 5.15.0
- Changes from 5.14.3:
- Bugfixes:
- FIXED #4704: Fixed regression in bearings reordering introduced in 5.13 [#4704](https://github.com/Project-OSRM/osrm-backend/issues/4704)
- FIXED #4781: Fixed overflow exceptions in percent-encoding parsing
- FIXED #4770: Fixed exclude flags for single toll road scenario
- FIXED #4283: Fix overflow on zero duration segments
- FIXED #4804: Ignore no_*_on_red turn restrictions
- Guidance:
- CHANGED #4706: Guidance refactoring step to decouple intersection connectivity analysis and turn instructions generation [#4706](https://github.com/Project-OSRM/osrm-backend/pull/4706)
- CHANGED #3491: Refactor `isThroughStreet`/Intersection options
- Profile:
- ADDED: `tunnel` as a new class in car profile so that sections of the route with tunnel tags will be marked as such
# 5.14.3
- Changes from 5.14.2:
- Features:
- Added a `waypoints` parameter to the match service plugin that accepts indices to input coordinates and treats only those points as waypoints in the response format.
- Bugfixes:
- FIXED #4754: U-Turn penalties are applied to straight turns.
- FIXED #4756: Removed too restrictive road name check in the sliproad handler
- FIXED #4731: Use correct weights for edge-based graph duplicated via nodes.
- Profile:
- CHANGED: added Belarus speed limits
- CHANGED: set default urban speed in Ukraine to 50kmh
# 5.14.2
- Changes from 5.14.1:
- Bugfixes:
- FIXED #4727: Erroring when a old .core file is present.
- FIXED #4642: Update checks for EMPTY_NAMEID to check for empty name strings
- FIXED #4738: Fix potential segmentation fault
- Node.js Bindings:
- ADDED: Exposed new `max_radiuses_map_matching` option from `EngingConfig` options
- Tools:
- ADDED: New osrm-routed `max_radiuses_map_matching` command line flag to optionally set a maximum radius for map matching
# 5.14.1
- Changes from 5.14.0
+1 -1
View File
@@ -62,7 +62,7 @@ endif()
project(OSRM C CXX)
set(OSRM_VERSION_MAJOR 5)
set(OSRM_VERSION_MINOR 15)
set(OSRM_VERSION_PATCH 0)
set(OSRM_VERSION_PATCH 1)
set(OSRM_VERSION "${OSRM_VERSION_MAJOR}.${OSRM_VERSION_MINOR}.${OSRM_VERSION_PATCH}")
add_definitions(-DOSRM_PROJECT_DIR="${CMAKE_CURRENT_SOURCE_DIR}")
+2 -1
View File
@@ -114,7 +114,8 @@ sudo cmake --build . --target install
### Request Against the Demo Server
Read the [API usage policy](https://github.com/Project-OSRM/osrm-backend/wiki/Api-usage-policy).
Read the [API usage policy](https://github.com/Project-OSRM/osrm-backend/wiki/Demo-server).
Simple query with instructions and alternatives on Berlin:
```
+1
View File
@@ -288,6 +288,7 @@ In addition to the [general options](#general-options) the following options are
|radiuses |`{radius};{radius}[;{radius} ...]` |Standard deviation of GPS precision used for map matching. If applicable use GPS accuracy.|
|gaps |`split` (default), `ignore` |Allows the input track splitting based on huge timestamp gaps between points. |
|tidy |`true`, `false` (default) |Allows the input track modification to obtain better matching quality for noisy tracks. |
|waypoints | `{index};{index};{index}...` |Treats input coordinates indicated by given indices as waypoints in returned Match object. Default is to treat all input coordinates as waypoints. |
|Parameter |Values |
|------------|-----------------------------------|
+13 -10
View File
@@ -208,16 +208,19 @@ The `process_turn` function is called for every possible turn in the network. Ba
The following attributes can be read and set on the result in `process_turn`:
Attribute | Read/write? | Type | Notes
-------------------|-------------|---------|------------------------------------------------------
direction_modifier | Read | Enum | Geometry of turn. Defined in `include/extractor/guidance/turn_instruction.hpp`
turn_type | Read | Enum | Priority of turn. Defined in `include/extractor/guidance/turn_instruction.hpp`
has_traffic_light | Read | Boolean | Is a traffic light present at this turn?
source_restricted | Read | Boolean | Is it from a restricted access road? (See definition in `process_way`)
target_restricted | Read | Boolean | Is it to a restricted access road? (See definition in `process_way`)
angle | Read | Float | Angle of turn in degrees (`0-360`: `0`=u-turn, `180`=straight on)
duration | Read/write | Float | Penalty to be applied for this turn (duration in deciseconds)
weight | Read/write | Float | Penalty to be applied for this turn (routing weight)
Attribute | Read/write? | Type | Notes
---------------------|-------------|---------|------------------------------------------------------
angle | Read | Float | Angle of turn in degrees (`0-360`: `0`=u-turn, `180`=straight on)
number_of_roads | Read | Integer | Number of ways at the intersection of the turn
is_u_turn | Read | Boolean | Is the turn a u-turn?
has_traffic_light | Read | Boolean | Is a traffic light present at this turn?
source_restricted | Read | Boolean | Is it from a restricted access road? (See definition in `process_way`)
target_restricted | Read | Boolean | Is it to a restricted access road? (See definition in `process_way`)
is_left_hand_driving | Read | Boolean | Is left-hand traffic?
weight | Read/write | Float | Penalty to be applied for this turn (routing weight)
duration | Read/write | Float | Penalty to be applied for this turn (duration in deciseconds)
source_mode | Read | Enum | Travel mode before the turn. Defined in `include/extractor/travel_mode.hpp`
target_mode | Read | Enum | Travel mode after the turn. Defined in `include/extractor/travel_mode.hpp`
## Guidance
The guidance parameters in profiles are currently a work in progress. They can and will change.
+40 -2
View File
@@ -82,7 +82,7 @@ Feature: Car - Mode flag
| from | to | route | turns | classes |
| a | d | ab,cd | depart,arrive| [(restricted),(motorway,restricted),()],[()] |
Scenario: Car - We toll restricted with a class
Scenario: Car - We tag toll with a class
Given the node map
"""
a b
@@ -99,6 +99,45 @@ Feature: Car - Mode flag
| from | to | route | turns | classes |
| a | d | ab,cd | depart,arrive | [(toll),(motorway,toll),()],[()] |
Scenario: Car - We tag tunnel with a class
Background:
Given a grid size of 200 meters
Given the node map
"""
a b
c d
"""
And the ways
| nodes | tunnel |
| ab | no |
| bc | yes |
| cd | |
When I route I should get
| from | to | route | turns | classes |
| a | d | ab,bc,cd,cd | depart,new name right,new name left,arrive | [()],[(tunnel)],[()],[()] |
Scenario: Car - We tag classes without intersections
Background:
Given a grid size of 200 meters
Given the node map
"""
a b c d
"""
And the ways
| nodes | name | tunnel |
| ab | road | |
| bc | road | yes |
| cd | road | |
When I route I should get
| from | to | route | turns | classes |
| a | d | road,road | depart,arrive | [(),(tunnel),()],[()] |
Scenario: Car - From roundabout on toll road
Given the node map
"""
@@ -124,4 +163,3 @@ Feature: Car - Mode flag
When I route I should get
| from | to | route | turns | classes |
| a | f | ab,df,df,df | depart,roundabout-exit-2,exit roundabout slight right,arrive | [()],[(),(motorway)],[(toll,motorway)],[()] |
@@ -884,7 +884,7 @@ Feature: Car - Turn restrictions
| a | c | albic,dobe,dobe,albic,albic | depart,turn left,continue uturn,turn left,arrive |
| a | e | albic,dobe,dobe | depart,turn left,arrive |
@no_turning @conditionals
@no_turning @conditionals @restriction-way
Scenario: Car - Conditional restriction with multiple time windows
Given the extract extra arguments "--parse-conditional-restrictions"
# 5pm Wed 02 May, 2017 GMT
@@ -1054,4 +1054,3 @@ Feature: Car - Turn restrictions
| a | f | ab,be,ef,ef | depart,turn right,turn left,arrive | a,b,e,f |
| c | d | bc,be,de,de | depart,turn left,turn right,arrive | c,b,e,d |
| c | f | bc,be,ef,ef | depart,turn left,turn left,arrive | c,b,e,f |
+45
View File
@@ -0,0 +1,45 @@
@routing @car
Feature: Car - Handle physical limitation
Background:
Given the profile "car"
Scenario: Car - Use a narrow way
Then routability should be
| highway | width | narrow | bothw |
| primary | | | x |
| primary | narrow | | x |
| primary | | yes | x |
| primary | 1.8 | | |
| primary | 1.9 | | |
| primary | 2.0 | | x |
| primary | 2.1 | | x |
| primary | 1m | | |
| primary | 1 m | | |
| primary | 3 m | | x |
| primary | 6' | | |
| primary | 6'0" | | |
| primary | 6'2" | | |
| primary | 6'3" | | x |
| primary | 7' | | x |
| primary | 7'0" | | x |
Scenario: Car - Limited by width
Then routability should be
| highway | maxwidth:physical | maxwidth | width | est_width | bothw |
| primary | 1 | | | | |
| primary | 3 | | | | x |
| primary | | 1 | | | |
| primary | | 3 | | | x |
| primary | | | 1 | | |
| primary | | | 3 | | x |
| primary | | | | 1 | |
| primary | | | | 3 | x |
Scenario: Car - Limited by height
Then routability should be
| highway | maxheight:physical | maxheight | bothw |
| primary | 1 | | |
| primary | 3 | | x |
| primary | | 1 | |
| primary | | 3 | x |
+37 -10
View File
@@ -141,6 +141,33 @@ Feature: Car - Turn restrictions
| c | a | cj,aj,aj |
| c | b | cj,bj,bj |
@no_turning
Scenario: Car - Ignore no_*_on_red relations
Given the node map
"""
a
d j b
c
"""
And the ways
| nodes | oneway |
| cj | yes |
| aj | -1 |
| dj | -1 |
| bj | -1 |
And the relations
| type | way:from | way:to | node:via | restriction |
| restriction | cj | dj | j | no_turn_on_red |
| restriction | cj | bj | j | no_right_turn_on_red |
When I route I should get
| from | to | route |
| c | d | cj,dj,dj |
| c | a | cj,aj,aj |
| c | b | cj,bj,bj |
@only_turning
Scenario: Car - Only left turn
Given the node map
@@ -575,7 +602,7 @@ Feature: Car - Turn restrictions
| c | d | bc,be,de,de | depart,turn left,turn right,arrive | c,b,e,d |
| c | f | bc,be,ef,ef | depart,turn left,turn left,arrive | c,b,e,f |
@restriction @overlap
@restriction-way @overlap
Scenario: Car - prohibit turn
Given the node map
"""
@@ -710,7 +737,7 @@ Feature: Car - Turn restrictions
| a | j | left,first,right,right |
| f | e | right,third,left,left |
@restriction
@restriction-way
Scenario: Car - allow only turn
Given the node map
"""
@@ -742,7 +769,7 @@ Feature: Car - Turn restrictions
| c | d | bc,be,de,de | depart,turn left,turn right,arrive | c,b,e,d |
| c | f | bc,be,ef,ef | depart,turn left,turn left,arrive | c,b,e,f |
@restriction
@restriction-way
Scenario: Car - allow only turn
Given the node map
"""
@@ -771,7 +798,7 @@ Feature: Car - Turn restrictions
| from | to | route |
| a | d | ab,be,de,de |
@restriction
@restriction-way
Scenario: Multi Way restriction
Given the node map
"""
@@ -808,7 +835,7 @@ Feature: Car - Turn restrictions
| from | to | route |
| a | h | horiz,vert,horiz,horiz |
@restriction
@restriction-way
Scenario: Multi-Way overlapping single-way
Given the node map
"""
@@ -847,7 +874,7 @@ Feature: Car - Turn restrictions
| h | d | hfb,abcd,abcd | depart,end of road right,arrive | h,b,d |
@restriction
@restriction-way
Scenario: Car - prohibit turn, traffic lights
Given the node map
"""
@@ -890,7 +917,7 @@ Feature: Car - Turn restrictions
| c | f | bc,be,ef,ef | depart,turn left,turn left,arrive | c,b,e,f |
@restriction @overlap @geometry
@restriction-way @overlap @geometry
Scenario: Geometry
Given the node map
"""
@@ -925,7 +952,7 @@ Feature: Car - Turn restrictions
| c | d | bc,bge,de,de |
| c | f | bc,bge,de,de,ef,ef |
@restriction @overlap @geometry @traffic-signals
@restriction-way @overlap @geometry @traffic-signals
Scenario: Geometry
Given the node map
"""
@@ -967,7 +994,7 @@ Feature: Car - Turn restrictions
| c | f | bc,bge,de,de,ef,ef |
# don't crash hard on invalid restrictions
@restriction @invalid
@restriction-way @invalid
Scenario: Geometry
Given the node map
"""
@@ -999,7 +1026,7 @@ Feature: Car - Turn restrictions
| a | f | ab,be,ef,ef |
@restriction @overlap @geometry
@restriction @restriction-way @overlap @geometry
Scenario: Duplicated restriction
Given the node map
"""
@@ -98,11 +98,11 @@ Feature: Car - Guidance - Bridges and Tunnels
| dce | primary | | Nebenstraße |
When I route I should get
| from | to | route | turns |
| a | d | Hauptstraße,Nebenstraße,Nebenstraße | depart,turn left,arrive |
| a | e | Hauptstraße,Nebenstraße,Nebenstraße | depart,turn right,arrive |
| e | a | Nebenstraße,Hauptstraßentunnel,Hauptstraße | depart,turn left,arrive |
| d | a | Nebenstraße,Hauptstraßentunnel,Hauptstraße | depart,turn right,arrive |
| from | to | route | turns |
| a | d | Hauptstraße,Nebenstraße,Nebenstraße | depart,end of road left,arrive |
| a | e | Hauptstraße,Nebenstraße,Nebenstraße | depart,end of road right,arrive |
| e | a | Nebenstraße,Hauptstraßentunnel,Hauptstraße | depart,turn left,arrive |
| d | a | Nebenstraße,Hauptstraßentunnel,Hauptstraße | depart,turn right,arrive |
Scenario: Tunnel with Immediate Turn Front and Back
Given the node map
@@ -129,4 +129,3 @@ Feature: Car - Guidance - Bridges and Tunnels
| e | g | Nebenstraße,Hauptstraßentunnel,Anderestraße,Anderestraße | depart,turn left,turn left,arrive |
| d | f | Nebenstraße,Hauptstraßentunnel,Anderestraße,Anderestraße | depart,turn right,turn right,arrive |
| d | g | Nebenstraße,Hauptstraßentunnel,Anderestraße,Anderestraße | depart,turn right,turn left,arrive |
+49 -3
View File
@@ -992,7 +992,6 @@ Feature: Slipways and Dedicated Turn Lanes
| dbef | primary | dbef | |
| ae | primary_link | ae | yes |
When I route I should get
| waypoints | route | turns | locations |
| s,f | sabc,dbef,dbef | depart,turn right,arrive | s,a,f |
@@ -1019,7 +1018,6 @@ Feature: Slipways and Dedicated Turn Lanes
| dbcf | primary | dbcf | |
| ac | primary_link | ae | yes |
When I route I should get
| waypoints | route | turns | locations |
| s,f | sab,dbcf,dbcf | depart,turn right,arrive | s,a,f |
@@ -1047,7 +1045,55 @@ Feature: Slipways and Dedicated Turn Lanes
| ae | primary_link | sab | yes |
| cg | primary | cg | |
When I route I should get
| waypoints | route | turns | locations |
| s,f | sab,dbcef,dbcef | depart,turn right,arrive | s,a,f |
@sliproads
Scenario: Sliproad converted from a fork
Given the node map
"""
d
.
b
s . a '.
`c
.
f
"""
And the ways
| nodes | highway | name | ref | oneway |
| sa | tertiary | | D 60A | yes |
| ab | tertiary | ab | D 60A | yes |
| ac | tertiary | | D 60A | yes |
| dbcf | tertiary | dbcf | D 543 | yes |
When I route I should get
| waypoints | route | turns | locations |
| s,f | ,dbcf,dbcf | depart,turn right,arrive | s,a,f |
@sliproads
Scenario: Sliproad to a road with a reference only
Given the node map
"""
s . a . b . d
` .
' .
..
c
.
f
"""
And the ways
| nodes | highway | name | ref | oneway |
| sabd | primary | road | | |
| bcf | primary | | K108 | |
| ac | primary_link | | | yes |
When I route I should get
| waypoints | route | turns | locations |
| s,f | road,, | depart,turn right,arrive | s,a,f |
@@ -393,31 +393,44 @@ Feature: Merge Segregated Roads
"""
a
|
b
b-----z
/ \
c h
| |
| |
| |
| |
| |
| |
| |
| |
| |
d g
\ /
e
|
f
"""
And the ways
| nodes | name | oneway |
| ab | road | no |
| ef | road | no |
| bcde | road | yes |
| eghb | road | yes |
| nodes | name | oneway |
| ab | road | no |
| ef | road | no |
| bcde | road | yes |
| eghb | road | yes |
| bz | cross | no |
And the relations
| type | way:from | way:to | node:via | restriction |
| restriction | bz | bcde | b | no_left_turn |
When I route I should get
| waypoints | turns | route |
| a,f | depart,arrive | road,road |
| c,f | depart,arrive | road,road |
| f,a | depart,arrive | road,road |
| g,a | depart,arrive | road,road |
| waypoints | turns | route |
| a,f | depart,arrive | road,road |
| c,f | depart,arrive | road,road |
| f,a | depart,arrive | road,road |
| g,a | depart,arrive | road,road |
| z,a | depart,turn right,arrive | cross,road,road |
Scenario: Traffic Island
Given the node map
@@ -588,10 +601,10 @@ Feature: Merge Segregated Roads
When I route I should get
| waypoints | route | turns |
| a,c | germ,ober | depart,arrive |
| a,g | germ,germ,germ | depart,continue right,arrive |
| a,1 | germ,germ,germ | depart,continue left,arrive |
| d,g | ober,germ,germ | depart,turn left,arrive |
| a,c | germ,ober | depart,arrive |
| a,g | germ,germ,germ | depart,continue right,arrive |
| a,1 | germ,germ,germ | depart,continue left,arrive |
| d,g | ober,germ,germ | depart,turn left,arrive |
# https://www.openstreetmap.org/#map=19/51.32888/6.57059
Scenario: Places in presence of oneways
@@ -623,16 +636,16 @@ Feature: Merge Segregated Roads
| cf | albrecht | yes |
When I route I should get
| waypoints | route | turns |
| a,l | schwert,albrecht,marianne,marianne | depart,new name straight,turn left,arrive |
| a,j | schwert,luise,luise | depart,turn right,arrive |
| a,1 | schwert,albrecht,albrecht,albrecht | depart,new name straight,continue uturn,arrive |
| k,l | marianne,marianne | depart,arrive |
| k,j | marianne,albrecht,luise,luise | depart,turn left,turn left,arrive |
| k,d | marianne,schwert,schwert | depart,turn right,arrive |
| i,j | luise,luise | depart,arrive |
| i,d | luise,albrecht,schwert,schwert | depart,turn left,turn straight,arrive |
| i,l | luise,albrecht,marianne,marianne | depart,turn left,turn left,arrive |
| waypoints | route | turns |
| a,l | schwert,albrecht,marianne,marianne | depart,new name straight,turn left,arrive |
| a,j | schwert,luise,luise | depart,turn right,arrive |
| a,1 | schwert,albrecht,albrecht,albrecht | depart,new name straight,continue uturn,arrive |
| k,l | marianne,marianne | depart,arrive |
| k,j | marianne,albrecht,luise,luise | depart,turn left,turn left,arrive |
| k,d | marianne,schwert,schwert | depart,turn right,arrive |
| i,j | luise,luise | depart,arrive |
| i,d | luise,albrecht,schwert,schwert | depart,turn left,turn straight,arrive |
| i,l | luise,albrecht,marianne,marianne | depart,turn left,turn left,arrive |
# https://www.openstreetmap.org/#map=19/52.46339/13.40272
Scenario: Do not merge links between segregated roads
+13 -10
View File
@@ -745,12 +745,15 @@ Feature: Basic Roundabout
Scenario: Drive through roundabout
Given a grid size of 5 meters
Given the node map
"""
a
b e d f
c
g h
. a .
. .
b e --- d ---- f
. .
.c.
g h
"""
And the ways
@@ -760,12 +763,12 @@ Feature: Basic Roundabout
| gch | | yes |
When I route I should get
| waypoints | bearings | route | turns |
| e,f | 90 90 | edf,edf | depart,arrive |
| e,h | 90 135 | edf,gch,gch,gch | depart,roundabout-exit-2,exit roundabout straight,arrive |
| g,f | 45 90 | gch,edf,edf,edf | depart,roundabout-exit-2,exit roundabout right,arrive |
| g,h | 45 135 | gch,gch,gch | depart,exit roundabout right,arrive |
| e,e | 90 270 | edf,edf,edf | depart,continue uturn,arrive |
| waypoints | bearings | route | turns |
| e,f | 90 90 | edf,edf | depart,arrive |
| e,h | 90 130 | edf,gch,gch,gch | depart,roundabout-exit-2,exit roundabout straight,arrive |
| g,f | 50 90 | gch,edf,edf,edf | depart,roundabout-exit-2,exit roundabout slight right,arrive |
| g,h | 50 130 | gch,gch,gch | depart,exit roundabout right,arrive |
| e,e | 90 270 | edf,edf,edf,edf | depart,roundabout-exit-3,exit roundabout sharp left,arrive |
Scenario: CCW and CW roundabouts with overlaps
Given the node map
+6 -6
View File
@@ -961,12 +961,12 @@ Feature: Simple Turns
g
.
.
.
.
f
h .
. .
. j
.
.
h f
.
. .
. j
. .
c
. . .
+30 -1
View File
@@ -150,7 +150,8 @@ module.exports = function () {
}
var ok = true;
var encodedResult = '',
extendedTarget = '';
extendedTarget = '',
resultWaypoints = [];
var testSubMatching = (sub, si) => {
var testSubNode = (ni) => {
@@ -186,6 +187,29 @@ module.exports = function () {
});
}
if (headers.has('waypoints')) {
var got_loc = [];
for (let i = 0; i < json.tracepoints.length; i++) {
if (!json.tracepoints[i]) continue;
if (json.tracepoints[i].waypoint_index != null)
got_loc.push(json.tracepoints[i].location);
}
if (row.waypoints.length != got_loc.length)
return cb(new Error(`Expected ${row.waypoints.length} waypoints, got ${got_loc.length}`));
for (i = 0; i < row.waypoints.length; i++)
{
var want_node = this.findNodeByName(row.waypoints[i]);
if (!this.FuzzyMatch.matchLocation(got_loc[i], want_node)) {
resultWaypoints.push(util.format('? [%s,%s]', got_loc[i][0], got_loc[i][1]));
ok = false;
} else {
resultWaypoints.push(row.waypoints[i]);
}
}
}
if (ok) {
if (headers.has('matchings')) {
got.matchings = row.matchings;
@@ -194,7 +218,12 @@ module.exports = function () {
if (headers.has('timestamps')) {
got.timestamps = row.timestamps;
}
if (headers.has('waypoints')) {
got.waypoints = row.waypoints;
}
} else {
got.waypoints = resultWaypoints.join(';');
got.matchings = encodedResult;
row.matchings = extendedTarget;
}
+4
View File
@@ -12,6 +12,10 @@ module.exports = {
FuzzyMatch: class {
match (got, want) {
// don't fail if bearings input and extected string is empty and actual result is undefined
if (want === '' && (got === '' || got === undefined))
return true;
var matchPercent = want.match(/(.*)\s+~(.+)%$/),
matchAbs = want.match(/(.*)\s+\+\-(.+)$/),
matchRe = want.match(/^\/(.*)\/$/),
+24
View File
@@ -59,3 +59,27 @@ Feature: Annotations
| from | to | route | a:datasources | a:speed |
| a | i | abcdefghi,abcdefghi | 1:0:1:0:1:0:0:0 | 50:10:50:10:50:10:10:10 |
| i | a | abcdefghi,abcdefghi | 0:1:0:0:0:0:0:1 | 10:50:10:10:10:10:10:50 |
Scenario: Speed annotations should handle zero segments
Given the profile "testbot"
Given the node map
"""
a -- b --- c
|
d
"""
And the ways
| nodes |
| abc |
| cd |
# This test relies on the snapping to the EBN cd to introduce a zero segment after the turn
And the query options
| annotations | speed,distance,duration,nodes |
| bearings | 90,5;180,5 |
When I route I should get
| from | to | route | a:speed | a:distance | a:duration | a:nodes |
| a | c | abc,abc | 10:10:10 | 249.998641:299.931643:0 | 25:30:0 | 1:2:3 |
@@ -0,0 +1,68 @@
@routing @testbot @exclude
Feature: Testbot - Exclude flags regression tests
Background:
Given the profile "testbot"
Scenario: Testbot - Exclude toll regression 1
Given the node map
"""
a g
. .
b....d-$-$-e....f
. .
c h
"""
And the ways
| nodes | highway | toll | # |
| ab | primary | | always drivable |
| cb | primary | | always drivable |
| bd | primary | | always drivable |
| de | motorway | yes | not drivable for exclude=toll |
| ef | primary | | always drivable |
| fg | primary | | always drivable |
| fh | primary | | always drivable |
Given the query options
| exclude | toll |
When I route I should get
| from | to | route |
| a | h | |
| a | g | |
| g | a | |
| d | e | |
Scenario: Testbot - Exclude toll regression 2
Given the profile "testbot"
Given the node map
"""
a g
. .
b....d-$-$-e....f
. .
c h..i
"""
And the ways
| nodes | highway | toll | # |
| ab | primary | | always drivable |
| cb | primary | | always drivable |
| bd | primary | | always drivable |
| de | motorway | yes | not drivable for exclude=toll |
| ef | primary | | always drivable |
| fg | primary | | always drivable |
| fh | primary | | always drivable |
| hi | primary | | always drivable |
Given the query options
| exclude | toll |
When I route I should get
| from | to | route |
| a | h | |
| a | g | |
| g | a | |
| d | e | |
| d | i | |
+146
View File
@@ -480,3 +480,149 @@ Feature: Basic Map Matching
| trace | a:nodes |
| 12 | 1:2:3:4:5:6 |
| 21 | 6:5:4:3:2:1 |
Scenario: Matching with waypoints param for start/end
Given the node map
"""
a-----b---c
|
|
d
|
|
e
"""
And the ways
| nodes | oneway |
| abc | no |
| bde | no |
Given the query options
| waypoints | 0;3 |
When I match I should get
| trace | code | matchings | waypoints |
| abde | Ok | abde | ae |
Scenario: Matching with waypoints param that were tidied away
Given the node map
"""
a - b - c - e
|
f
|
g
"""
And the ways
| nodes | oneway |
| abce | no |
| cfg | no |
Given the query options
| tidy | true |
| waypoints | 0;2;5 |
When I match I should get
| trace | code | matchings | waypoints |
| abccfg | Ok | abcfg | acg |
Scenario: Testbot - Map matching refuses to use waypoints with trace splitting
Given the node map
"""
a b c d
e
"""
Given the query options
| waypoints | 0;3 |
And the ways
| nodes | oneway |
| abcd | no |
When I match I should get
| trace | timestamps | code |
| abcd | 0 1 62 63 | NoMatch |
Scenario: Testbot - Map matching invalid waypoints
Given the node map
"""
a b c d
e
"""
Given the query options
| waypoints | 0;4 |
And the ways
| nodes | oneway |
| abcd | no |
When I match I should get
| trace | code |
| abcd | InvalidOptions |
Scenario: Matching fail with waypoints param missing start/end
Given the node map
"""
a-----b---c
|
|
d
|
|
e
"""
And the ways
| nodes | oneway |
| abc | no |
| bde | no |
Given the query options
| waypoints | 1;3 |
When I match I should get
| trace | code |
| abde | InvalidValue |
Scenario: Testbot - Map matching with outlier that has no candidate and waypoint parameter
Given a grid size of 100 meters
Given the node map
"""
a b c d
1
"""
And the ways
| nodes | oneway |
| abcd | no |
Given the query options
| waypoints | 0;2;3 |
When I match I should get
| trace | timestamps | code |
| ab1d | 0 1 2 3 | NoMatch |
Scenario: Regression test - avoid collapsing legs of a tidied split trace
Given a grid size of 20 meters
Given the node map
"""
a--b--f
|
|
e--c---d--g
"""
Given the query options
| tidy | true |
And the ways
| nodes | oneway |
| abf | no |
| be | no |
| ecdg | no |
When I match I should get
| trace | timestamps | matchings | code |
| abbecd | 10 11 27 1516914902 1516914913 1516914952 | ab,ecd | Ok |
@@ -69,6 +69,11 @@ inline auto contractExcludableGraph(ContractorGraph contractor_graph_,
});
non_core_edges.resize(new_end - non_core_edges.begin());
edge_container.Insert(std::move(non_core_edges));
for (const auto filter_index : util::irange<std::size_t>(0, filters.size()))
{
edge_container.Filter(filters[filter_index], filter_index);
}
}
// Extract core graph for further contraction
@@ -60,6 +60,25 @@ struct ContractedEdgeContainer
flags.resize(edges.size(), ALL_FLAGS);
}
void Filter(const std::vector<bool> &filter, std::size_t index)
{
BOOST_ASSERT(index < sizeof(MergedFlags) * CHAR_BIT);
const MergedFlags flag = 1 << index;
for (auto edge_index : util::irange<std::size_t>(0, edges.size()))
{
auto allowed = filter[edges[edge_index].source] && filter[edges[edge_index].target];
if (allowed)
{
flags[edge_index] |= flag;
}
else
{
flags[edge_index] &= ~flag;
}
}
}
void Merge(std::vector<QueryEdge> new_edges)
{
BOOST_ASSERT(index < sizeof(MergedFlags) * CHAR_BIT);
+1
View File
@@ -6,6 +6,7 @@
#include "util/query_heap.hpp"
#include <tbb/enumerable_thread_specific.h>
#include <tbb/parallel_for.h>
#include <unordered_set>
+21
View File
@@ -86,6 +86,10 @@ class MatchAPI final : public RouteAPI
for (auto point_index : util::irange(
0u, static_cast<unsigned>(sub_matchings[sub_matching_index].indices.size())))
{
// tidied_to_original: index of the input coordinate that a tidied coordinate
// corresponds to.
// sub_matching indices: index of the coordinate passed to map matching plugin that
// a matched node corresponds to.
trace_idx_to_matching_idx[tidy_result
.tidied_to_original[sub_matchings[sub_matching_index]
.indices[point_index]]] =
@@ -93,6 +97,9 @@ class MatchAPI final : public RouteAPI
}
}
BOOST_ASSERT(parameters.waypoints.empty() || sub_matchings.size() == 1);
std::size_t was_waypoint_idx = 0;
for (auto trace_index : util::irange<std::size_t>(0UL, parameters.coordinates.size()))
{
if (tidy_result.can_be_removed[trace_index])
@@ -114,6 +121,20 @@ class MatchAPI final : public RouteAPI
waypoint.values["alternatives_count"] =
sub_matchings[matching_index.sub_matching_index]
.alternatives_count[matching_index.point_index];
// waypoint indices need to be adjusted if route legs were collapsed
// waypoint parameter assumes there is only one match object
if (!parameters.waypoints.empty())
{
if (tidy_result.was_waypoint[trace_index])
{
waypoint.values["waypoint_index"] = was_waypoint_idx;
was_waypoint_idx++;
}
else
{
waypoint.values["waypoint_index"] = util::json::Null();
}
}
waypoints.values.push_back(std::move(waypoint));
}
+18 -3
View File
@@ -63,25 +63,40 @@ struct MatchParameters : public RouteParameters
RouteParameters::GeometriesType::Polyline,
RouteParameters::OverviewType::Simplified,
{}),
gaps(GapsType::Split), tidy(false)
gaps(GapsType::Split), tidy(false), waypoints()
{
}
template <typename... Args>
MatchParameters(std::vector<unsigned> timestamps_, GapsType gaps_, bool tidy_, Args... args_)
: MatchParameters(std::move(timestamps_), gaps_, tidy_, {}, std::forward<Args>(args_)...)
{
}
template <typename... Args>
MatchParameters(std::vector<unsigned> timestamps_,
GapsType gaps_,
bool tidy_,
std::vector<std::size_t> waypoints_,
Args... args_)
: RouteParameters{std::forward<Args>(args_)...}, timestamps{std::move(timestamps_)},
gaps(gaps_), tidy(tidy_)
gaps(gaps_), tidy(tidy_), waypoints{std::move(waypoints_)}
{
}
std::vector<unsigned> timestamps;
GapsType gaps;
bool tidy;
std::vector<std::size_t> waypoints;
bool IsValid() const
{
const auto valid_waypoints =
std::all_of(waypoints.begin(), waypoints.end(), [this](const auto &w) {
return w < coordinates.size();
});
return RouteParameters::IsValid() &&
(timestamps.empty() || timestamps.size() == coordinates.size());
(timestamps.empty() || timestamps.size() == coordinates.size()) && valid_waypoints;
}
};
}
+39 -2
View File
@@ -37,6 +37,9 @@ struct Result
Mask can_be_removed;
// Maps the MatchParameter's original items to items which should not be removed.
Mapping tidied_to_original;
// Masking the MatchParameter coordinates for items whose indices were present in the
// `waypoints` parameter.
Mask was_waypoint;
};
inline Result keep_all(const MatchParameters &params)
@@ -44,6 +47,17 @@ inline Result keep_all(const MatchParameters &params)
Result result;
result.can_be_removed.resize(params.coordinates.size(), false);
result.was_waypoint.resize(params.coordinates.size(), true);
// by default all input coordinates are treated as waypoints
if (!params.waypoints.empty())
{
for (const auto p : params.waypoints)
{
result.was_waypoint.set(p, false);
}
// logic is a little funny, uses inversion to set the bitfield
result.was_waypoint.flip();
}
result.tidied_to_original.reserve(params.coordinates.size());
for (std::size_t current = 0; current < params.coordinates.size(); ++current)
{
@@ -61,6 +75,8 @@ inline Result keep_all(const MatchParameters &params)
{
result.parameters.coordinates.push_back(params.coordinates[i]);
if (result.was_waypoint[i])
result.parameters.waypoints.push_back(result.parameters.coordinates.size() - 1);
if (!params.hints.empty())
result.parameters.hints.push_back(params.hints[i]);
@@ -74,6 +90,8 @@ inline Result keep_all(const MatchParameters &params)
result.parameters.timestamps.push_back(params.timestamps[i]);
}
}
if (params.waypoints.empty())
result.parameters.waypoints.clear();
return result;
}
@@ -85,6 +103,15 @@ inline Result tidy(const MatchParameters &params, Thresholds cfg = {15., 5})
Result result;
result.can_be_removed.resize(params.coordinates.size(), false);
result.was_waypoint.resize(params.coordinates.size(), true);
if (!params.waypoints.empty())
{
for (const auto p : params.waypoints)
{
result.was_waypoint.set(p, false);
}
result.was_waypoint.flip();
}
result.tidied_to_original.push_back(0);
@@ -138,13 +165,14 @@ inline Result tidy(const MatchParameters &params, Thresholds cfg = {15., 5})
// We have to filter parallel arrays that may be empty or the exact same size.
// result.parameters contains an empty MatchParameters at this point: conditionally fill.
for (std::size_t i = 0; i < result.can_be_removed.size(); ++i)
{
if (!result.can_be_removed[i])
{
result.parameters.coordinates.push_back(params.coordinates[i]);
if (result.was_waypoint[i])
result.parameters.waypoints.push_back(result.parameters.coordinates.size() - 1);
if (!params.hints.empty())
result.parameters.hints.push_back(params.hints[i]);
@@ -157,8 +185,17 @@ inline Result tidy(const MatchParameters &params, Thresholds cfg = {15., 5})
if (!params.timestamps.empty())
result.parameters.timestamps.push_back(params.timestamps[i]);
}
else
{
// one of the coordinates meant to be used as a waypoint was marked for removal
// update the original waypoint index to the new representative coordinate
const auto last_idx = result.parameters.coordinates.size() - 1;
if (result.was_waypoint[i] && (result.parameters.waypoints.back() != last_idx))
{
result.parameters.waypoints.push_back(last_idx);
}
}
}
BOOST_ASSERT(result.tidied_to_original.size() == result.parameters.coordinates.size());
return result;
}
+22 -13
View File
@@ -88,11 +88,12 @@ class RouteAPI : public BaseAPI
{
util::json::Array annotations_store;
annotations_store.values.reserve(leg.annotations.size());
std::for_each(leg.annotations.begin(),
leg.annotations.end(),
[Get, &annotations_store](const auto &step) {
annotations_store.values.push_back(Get(step));
});
for (const auto &step : leg.annotations)
{
annotations_store.values.push_back(Get(step));
}
return annotations_store;
}
@@ -255,10 +256,19 @@ class RouteAPI : public BaseAPI
// AnnotationsType uses bit flags, & operator checks if a property is set
if (parameters.annotations_type & RouteParameters::AnnotationsType::Speed)
{
double prev_speed = 0;
annotation.values["speed"] = GetAnnotations(
leg_geometry, [](const guidance::LegGeometry::Annotation &anno) {
auto val = std::round(anno.distance / anno.duration * 10.) / 10.;
return util::json::clamp_float(val);
leg_geometry, [&prev_speed](const guidance::LegGeometry::Annotation &anno) {
if (anno.duration < std::numeric_limits<double>::min())
{
return prev_speed;
}
else
{
auto speed = std::round(anno.distance / anno.duration * 10.) / 10.;
prev_speed = speed;
return util::json::clamp_float(speed);
}
});
}
@@ -293,11 +303,10 @@ class RouteAPI : public BaseAPI
{
util::json::Array nodes;
nodes.values.reserve(leg_geometry.osm_node_ids.size());
std::for_each(leg_geometry.osm_node_ids.begin(),
leg_geometry.osm_node_ids.end(),
[this, &nodes](const OSMNodeID &node_id) {
nodes.values.push_back(static_cast<std::uint64_t>(node_id));
});
for (const auto node_id : leg_geometry.osm_node_ids)
{
nodes.values.push_back(static_cast<std::uint64_t>(node_id));
}
annotation.values["nodes"] = std::move(nodes);
}
+6 -6
View File
@@ -53,12 +53,12 @@ template <typename Algorithm> class Engine final : public EngineInterface
{
public:
explicit Engine(const EngineConfig &config)
: route_plugin(config.max_locations_viaroute, config.max_alternatives), //
table_plugin(config.max_locations_distance_table), //
nearest_plugin(config.max_results_nearest), //
trip_plugin(config.max_locations_trip), //
match_plugin(config.max_locations_map_matching), //
tile_plugin() //
: route_plugin(config.max_locations_viaroute, config.max_alternatives), //
table_plugin(config.max_locations_distance_table), //
nearest_plugin(config.max_results_nearest), //
trip_plugin(config.max_locations_trip), //
match_plugin(config.max_locations_map_matching, config.max_radius_map_matching), //
tile_plugin() //
{
if (config.use_shared_memory)
+1
View File
@@ -84,6 +84,7 @@ struct EngineConfig final
int max_locations_viaroute = -1;
int max_locations_distance_table = -1;
int max_locations_map_matching = -1;
double max_radius_map_matching = -1.0;
int max_results_nearest = -1;
int max_alternatives = 3; // set an arbitrary upper bound; can be adjusted by user
bool use_shared_memory = true;
+52 -2
View File
@@ -7,11 +7,11 @@
#include "engine/phantom_node.hpp"
#include "osrm/coordinate.hpp"
#include "util/coordinate.hpp"
#include "util/guidance/entry_class.hpp"
#include "util/guidance/turn_bearing.hpp"
#include "util/guidance/turn_lanes.hpp"
#include "util/integer_range.hpp"
#include "util/typedefs.hpp"
#include <vector>
@@ -102,6 +102,56 @@ struct InternalManyRoutesResult
std::vector<InternalRouteResult> routes;
};
inline InternalRouteResult CollapseInternalRouteResult(const InternalRouteResult &leggy_result,
const std::vector<bool> &is_waypoint)
{
BOOST_ASSERT(leggy_result.is_valid());
BOOST_ASSERT(is_waypoint[0]); // first and last coords
BOOST_ASSERT(is_waypoint.back()); // should always be waypoints
// Nothing to collapse! return result as is
if (leggy_result.unpacked_path_segments.size() == 1)
return leggy_result;
BOOST_ASSERT(leggy_result.segment_end_coordinates.size() > 1);
InternalRouteResult collapsed;
collapsed.shortest_path_weight = leggy_result.shortest_path_weight;
for (auto i : util::irange<std::size_t>(0, leggy_result.unpacked_path_segments.size()))
{
if (is_waypoint[i])
{
// start another leg vector
collapsed.unpacked_path_segments.push_back(leggy_result.unpacked_path_segments[i]);
// save new phantom node pair
collapsed.segment_end_coordinates.push_back(leggy_result.segment_end_coordinates[i]);
// save data about phantom nodes
collapsed.source_traversed_in_reverse.push_back(
leggy_result.source_traversed_in_reverse[i]);
collapsed.target_traversed_in_reverse.push_back(
leggy_result.target_traversed_in_reverse[i]);
}
else
// no new leg, collapse the next segment into the last leg
{
BOOST_ASSERT(!collapsed.unpacked_path_segments.empty());
auto &last_segment = collapsed.unpacked_path_segments.back();
// deduplicate last segment (needs to be checked for empty for the same node query edge
// case)
if (!last_segment.empty())
last_segment.pop_back();
// update target phantom node of leg
BOOST_ASSERT(!collapsed.segment_end_coordinates.empty());
collapsed.segment_end_coordinates.back().target_phantom =
leggy_result.segment_end_coordinates[i].target_phantom;
// copy path segments into current leg
last_segment.insert(last_segment.end(),
leggy_result.unpacked_path_segments[i].begin(),
leggy_result.unpacked_path_segments[i].end());
}
}
return collapsed;
}
}
}
+4 -2
View File
@@ -24,8 +24,9 @@ class MatchPlugin : public BasePlugin
using CandidateLists = routing_algorithms::CandidateLists;
static const constexpr double RADIUS_MULTIPLIER = 3;
MatchPlugin(const int max_locations_map_matching)
: max_locations_map_matching(max_locations_map_matching)
MatchPlugin(const int max_locations_map_matching, const double max_radius_map_matching)
: max_locations_map_matching(max_locations_map_matching),
max_radius_map_matching(max_radius_map_matching)
{
}
@@ -35,6 +36,7 @@ class MatchPlugin : public BasePlugin
private:
const int max_locations_map_matching;
const double max_radius_map_matching;
};
}
}
+2 -2
View File
@@ -39,8 +39,8 @@ struct ExtractionTurn
const bool is_left_hand_driving;
double weight;
double duration;
TravelMode source_mode;
TravelMode target_mode;
const TravelMode source_mode;
const TravelMode target_mode;
};
}
}
+1 -1
View File
@@ -60,7 +60,7 @@ operator()(const NodeID intersection_node,
const boost::optional<util::json::Object> &way_style) const
{
// request the number of lanes. This process needs to be in sync with what happens over at
// intersection_generator
// intersection analysis
const auto intersection_lanes =
intersection.FindMaximum(guidance::makeExtractLanesForRoad(node_based_graph));
@@ -14,10 +14,13 @@ namespace guidance
class DrivewayHandler final : public IntersectionHandler
{
public:
DrivewayHandler(const IntersectionGenerator &intersection_generator,
const util::NodeBasedDynamicGraph &node_based_graph,
DrivewayHandler(const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &coordinates,
const extractor::CompressedEdgeContainer &compressed_geometries,
const RestrictionMap &node_restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table);
@@ -0,0 +1,27 @@
#ifndef OSRM_EXTRACTOR_GUIDANCE_HAVE_IDENTICAL_NAMES_HPP_
#define OSRM_EXTRACTOR_GUIDANCE_HAVE_IDENTICAL_NAMES_HPP_
#include "extractor/guidance/constants.hpp"
#include "extractor/suffix_table.hpp"
#include "util/name_table.hpp"
namespace osrm
{
namespace extractor
{
namespace guidance
{
// check if two name ids can be seen as identical (in presence of refs/others)
// in our case this translates into no name announcement in either direction (lhs->rhs and
// rhs->lhs)
bool HaveIdenticalNames(const NameID lhs,
const NameID rhs,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table);
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif /*OSRM_EXTRACTOR_GUIDANCE_HAVE_IDENTICAL_NAMES_HPP_*/
+3 -10
View File
@@ -44,14 +44,6 @@ inline auto makeCompareShapeDataByBearing(const double base_bearing)
};
}
inline auto makeCompareShapeDataAngleToBearing(const double base_bearing)
{
return [base_bearing](const auto &lhs, const auto &rhs) {
return util::bearing::angleBetween(lhs.bearing, base_bearing) <
util::bearing::angleBetween(rhs.bearing, base_bearing);
};
}
inline auto makeCompareAngularDeviation(const double angle)
{
return [angle](const auto &lhs, const auto &rhs) {
@@ -301,10 +293,11 @@ struct IntersectionView final : std::vector<IntersectionViewData>, //
};
// `Intersection` is a relative view of an intersection by an incoming edge.
// `Intersection` are streets at an intersection ordered from from sharp right counter-clockwise to
// `Intersection` are streets at an intersection stored as an ordered list of connected roads
// ordered from sharp right counter-clockwise to
// sharp left where `intersection[0]` is _always_ a u-turn
// An intersection is an ordered list of connected roads ordered from from sharp right
// An intersection is an ordered list of connected roads ordered from sharp right
// counter-clockwise to sharp left where `intersection[0]` is always a u-turn
//
// |
@@ -1,127 +0,0 @@
#ifndef OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_GENERATOR_HPP_
#define OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_GENERATOR_HPP_
#include "extractor/compressed_edge_container.hpp"
#include "extractor/guidance/coordinate_extractor.hpp"
#include "extractor/guidance/intersection.hpp"
#include "extractor/guidance/intersection_normalization_operation.hpp"
#include "extractor/query_node.hpp"
#include "extractor/restriction_index.hpp"
#include "util/attributes.hpp"
#include "util/node_based_graph.hpp"
#include "util/typedefs.hpp"
#include <unordered_set>
#include <utility>
#include <vector>
#include <boost/optional.hpp>
namespace osrm
{
namespace extractor
{
namespace guidance
{
struct IntersectionGenerationParameters
{
NodeID nid;
EdgeID via_eid;
};
// The Intersection Generator is given a turn location and generates an intersection representation
// from it. For this all turn possibilities are analysed.
// We consider turn restrictions to indicate possible turns. U-turns are generated based on profile
// decisions.
class IntersectionGenerator
{
public:
IntersectionGenerator(const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const RestrictionMap &restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const std::vector<util::Coordinate> &coordinates,
const CompressedEdgeContainer &compressed_edge_container);
// For a source node `a` and a via edge `ab` creates an intersection at target `b`.
//
// a . . . b . .
// .
// .
//
IntersectionView operator()(const NodeID nid, const EdgeID via_eid) const;
/*
* Compute the shape of an intersection, returning a set of connected roads, without any further
* concern for which of the entries are actually allowed.
* The shape also only comes with turn bearings, not with turn angles. All turn angles will be
* set to zero
*/
OSRM_ATTR_WARN_UNUSED
IntersectionShape
ComputeIntersectionShape(const NodeID center_node,
const boost::optional<NodeID> sorting_base = boost::none,
bool use_low_precision_angles = false) const;
// Graph Compression cannot compress every setting. For example any barrier/traffic light cannot
// be compressed. As a result, a simple road of the form `a ----- b` might end up as having an
// intermediate intersection, if there is a traffic light in between. If we want to look farther
// down a road, finding the next actual decision requires the look at multiple intersections.
// Here we follow the road until we either reach a dead end or find the next intersection with
// more than a single next road. This function skips over degree two nodes to find coorect input
// for GetConnectedRoads.
OSRM_ATTR_WARN_UNUSED
IntersectionGenerationParameters SkipDegreeTwoNodes(const NodeID starting_node,
const EdgeID via_edge) const;
// Allow access to the coordinate extractor for all owners
const CoordinateExtractor &GetCoordinateExtractor() const;
// Check for restrictions/barriers and generate a list of valid and invalid turns present at
// the node reached from `from_node` via `via_eid`. The resulting candidates have to be analysed
// for their actual instructions later on.
// The switch for `use_low_precision_angles` enables a faster mode that will procude less
// accurate coordinates. It should be good enough to check order of turns, find straightmost
// turns. Even good enough to do some simple angle verifications. It is mostly available to
// allow for faster graph traversal in the extraction phase.
OSRM_ATTR_WARN_UNUSED
IntersectionView GetConnectedRoads(const NodeID from_node,
const EdgeID via_eid,
const bool use_low_precision_angles = false) const;
/*
* To be used in the road network, we need to check for valid/restricted turns. These two
* functions transform a basic intersection / a normalised intersection into the
* correct view when entering via a given edge.
*/
OSRM_ATTR_WARN_UNUSED
IntersectionView
TransformIntersectionShapeIntoView(const NodeID previous_node,
const EdgeID entering_via_edge,
const IntersectionShape &intersection) const;
// version for normalised intersection
OSRM_ATTR_WARN_UNUSED
IntersectionView TransformIntersectionShapeIntoView(
const NodeID previous_node,
const EdgeID entering_via_edge,
const IntersectionShape &normalised_intersection,
const IntersectionShape &intersection,
const std::vector<IntersectionNormalizationOperation> &merging_map) const;
private:
const util::NodeBasedDynamicGraph &node_based_graph;
const EdgeBasedNodeDataContainer &node_data_container;
const RestrictionMap &restriction_map;
const std::unordered_set<NodeID> &barrier_nodes;
const std::vector<util::Coordinate> &coordinates;
// own state, used to find the correct coordinates along a road
const CoordinateExtractor coordinate_extractor;
};
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif /* OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_GENERATOR_HPP_ */
@@ -2,8 +2,8 @@
#define OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_HANDLER_HPP_
#include "extractor/guidance/intersection.hpp"
#include "extractor/guidance/intersection_generator.hpp"
#include "extractor/guidance/node_based_graph_walker.hpp"
#include "extractor/intersection/intersection_analysis.hpp"
#include "extractor/query_node.hpp"
#include "extractor/suffix_table.hpp"
@@ -34,10 +34,13 @@ class IntersectionHandler
public:
IntersectionHandler(const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &coordinates,
const std::vector<util::Coordinate> &node_coordinates,
const extractor::CompressedEdgeContainer &compressed_geometries,
const RestrictionMap &node_restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table,
const IntersectionGenerator &intersection_generator);
const SuffixTable &street_name_suffix_table);
virtual ~IntersectionHandler() = default;
@@ -52,15 +55,20 @@ class IntersectionHandler
protected:
const util::NodeBasedDynamicGraph &node_based_graph;
const EdgeBasedNodeDataContainer &node_data_container;
const std::vector<util::Coordinate> &coordinates;
const std::vector<util::Coordinate> &node_coordinates;
const extractor::CompressedEdgeContainer &compressed_geometries;
const RestrictionMap &node_restriction_map;
const std::unordered_set<NodeID> &barrier_nodes;
const guidance::TurnLanesIndexedArray &turn_lanes_data;
const util::NameTable &name_table;
const SuffixTable &street_name_suffix_table;
const IntersectionGenerator &intersection_generator;
const NodeBasedGraphWalker graph_walker; // for skipping traffic signal, distances etc.
// Decide on a basic turn types
TurnType::Enum findBasicTurnType(const EdgeID via_edge, const ConnectedRoad &candidate) const;
TurnType::Enum areSameClasses(const EdgeID via_edge, const ConnectedRoad &road) const;
// Find the most obvious turn to follow. The function returns an index into the intersection
// determining whether there is a road that can be seen as obvious turn in the presence of many
// other possible turns. The function will consider road categories and other inputs like the
@@ -90,9 +98,6 @@ class IntersectionHandler
const std::size_t begin,
const std::size_t end) const;
// Checks the intersection for a through street connected to `intersection[index]`
bool isThroughStreet(const std::size_t index, const Intersection &intersection) const;
// See `getNextIntersection`
struct IntersectionViewAndNode final
{
@@ -567,11 +572,19 @@ std::size_t IntersectionHandler::findObviousTurn(const EdgeID via_edge,
// try to find whether there is a turn going to the opposite direction of our obvious
// turn, this should be alright.
const auto previous_intersection = [&]() -> IntersectionView {
const auto parameters = intersection_generator.SkipDegreeTwoNodes(
node_at_intersection, intersection[0].eid);
if (node_based_graph.GetTarget(parameters.via_eid) == node_at_intersection)
const auto parameters = intersection::skipDegreeTwoNodes(
node_based_graph, {node_at_intersection, intersection[0].eid});
if (node_based_graph.GetTarget(parameters.edge) == node_at_intersection)
return {};
return intersection_generator.GetConnectedRoads(parameters.nid, parameters.via_eid);
return intersection::getConnectedRoads<false>(node_based_graph,
node_data_container,
node_coordinates,
compressed_geometries,
node_restriction_map,
barrier_nodes,
turn_lanes_data,
parameters);
}();
if (!previous_intersection.empty())
@@ -1,25 +0,0 @@
#ifndef OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_NORMALIZATION_OPERATION_HPP_
#define OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_NORMALIZATION_OPERATION_HPP_
#include "util/typedefs.hpp"
namespace osrm
{
namespace extractor
{
namespace guidance
{
struct IntersectionNormalizationOperation
{
// the source of the merge, not part of the intersection after the merge is performed.
EdgeID merged_eid;
// the edge that is covering the `merged_eid`
EdgeID into_eid;
};
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif /*OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_NORMALIZATION_OPERATION_HPP_*/
@@ -1,125 +0,0 @@
#ifndef OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_NORMALIZER_HPP_
#define OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_NORMALIZER_HPP_
#include "util/attributes.hpp"
#include "util/name_table.hpp"
#include "util/typedefs.hpp"
#include "extractor/guidance/coordinate_extractor.hpp"
#include "extractor/guidance/intersection.hpp"
#include "extractor/guidance/intersection_generator.hpp"
#include "extractor/guidance/intersection_normalization_operation.hpp"
#include "extractor/guidance/mergable_road_detector.hpp"
#include "extractor/query_node.hpp"
#include "extractor/suffix_table.hpp"
#include <utility>
#include <vector>
namespace osrm
{
namespace extractor
{
namespace guidance
{
/*
* An intersection is a central part in computing guidance decisions. However the model in OSM and
* the view we want to use in guidance are not necessarily the same thing. We have to account for
* some models that are chosen explicitly in OSM and that don't actually describe how a human would
* experience an intersection.
*
* For example, if a small pedestrian island is located at a traffic light right in the middle of a
* road, OSM tends to model the road as two separate ways. A human would consider these two ways a
* single road, though. In this normalizer, we try to account for these subtle differences between
* OSM data and human perception to improve our decision base for guidance later on.
*/
class IntersectionNormalizer
{
public:
struct NormalizationResult
{
IntersectionShape normalized_shape;
std::vector<IntersectionNormalizationOperation> performed_merges;
};
IntersectionNormalizer(const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &node_coordinates,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table,
const IntersectionGenerator &intersection_generator);
// The function takes an intersection an converts it to a `perceived` intersection which closer
// represents how a human might experience the intersection
OSRM_ATTR_WARN_UNUSED
NormalizationResult operator()(const NodeID node_at_intersection,
IntersectionShape intersection) const;
private:
const util::NodeBasedDynamicGraph &node_based_graph;
const IntersectionGenerator &intersection_generator;
const MergableRoadDetector mergable_road_detector;
/* check if two indices in an intersection can be seen as a single road in the perceived
* intersection representation. See below for an example. Utility function for
* MergeSegregatedRoads. It also checks for neighboring merges.
* This is due possible segments where multiple roads could end up being merged into one.
* We only support merging two roads, not three or more, though.
* c c
* / /
* a - b -> a - b - (c,d) but not a - b d -> a,b,(cde)
* \ \
* d e
*/
bool CanMerge(const NodeID intersection_node,
const IntersectionShape &intersection,
std::size_t first_index,
std::size_t second_index) const;
// Perform an Actual Merge
IntersectionNormalizationOperation
DetermineMergeDirection(const IntersectionShapeData &lhs,
const IntersectionShapeData &rhs) const;
IntersectionShapeData MergeRoads(const IntersectionShapeData &destination,
const IntersectionShapeData &source) const;
IntersectionShapeData MergeRoads(const IntersectionNormalizationOperation direction,
const IntersectionShapeData &lhs,
const IntersectionShapeData &rhs,
const double opposite_bearing) const;
// Merge segregated roads to omit invalid turns in favor of treating segregated roads as
// one.
// This function combines roads the following way:
//
// * *
// * is converted to *
// v ^ +
// v ^ +
//
// The treatment results in a straight turn angle of 180º rather than a turn angle of approx
// 160
OSRM_ATTR_WARN_UNUSED
NormalizationResult MergeSegregatedRoads(const NodeID intersection_node,
IntersectionShape intersection) const;
// The counterpiece to mergeSegregatedRoads. While we can adjust roads that split up at the
// intersection itself, it can also happen that intersections are connected to joining roads.
//
// * *
// * is converted to *
// v a --- a ---
// v ^ +
// v ^ +
// b
//
// for the local view of b at a.
OSRM_ATTR_WARN_UNUSED
IntersectionShape AdjustBearingsForMergeAtDestination(const NodeID node_at_intersection,
IntersectionShape intersection) const;
};
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif /* OSRM_EXTRACTOR_GUIDANCE_INTERSECTION_NORMALIZER_HPP_ */
@@ -0,0 +1,63 @@
#ifndef OSRM_EXTRACTOR_GUIDANCE_IS_THROUGH_STREET_HPP_
#define OSRM_EXTRACTOR_GUIDANCE_IS_THROUGH_STREET_HPP_
#include "extractor/guidance/constants.hpp"
#include "extractor/suffix_table.hpp"
#include "util/guidance/name_announcements.hpp"
using osrm::util::angularDeviation;
namespace osrm
{
namespace extractor
{
namespace guidance
{
template <typename IntersectionType>
inline bool isThroughStreet(const std::size_t index,
const IntersectionType &intersection,
const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table)
{
const auto &data_at_index = node_data_container.GetAnnotation(
node_based_graph.GetEdgeData(intersection[index].eid).annotation_data);
if (data_at_index.name_id == EMPTY_NAMEID)
return false;
// a through street cannot start at our own position -> index 1
for (std::size_t road_index = 1; road_index < intersection.size(); ++road_index)
{
if (road_index == index)
continue;
const auto &road = intersection[road_index];
const auto &road_data = node_data_container.GetAnnotation(
node_based_graph.GetEdgeData(road.eid).annotation_data);
// roads have a near straight angle (180 degree)
const bool is_nearly_straight = angularDeviation(road.angle, intersection[index].angle) >
(STRAIGHT_ANGLE - FUZZY_ANGLE_DIFFERENCE);
const bool have_same_name = HaveIdenticalNames(
data_at_index.name_id, road_data.name_id, name_table, street_name_suffix_table);
const bool have_same_category =
node_based_graph.GetEdgeData(intersection[index].eid).flags.road_classification ==
node_based_graph.GetEdgeData(road.eid).flags.road_classification;
if (is_nearly_straight && have_same_name && have_same_category)
return true;
}
return false;
}
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif /*OSRM_EXTRACTOR_GUIDANCE_IS_THROUGH_STREET_HPP_*/
@@ -1,7 +1,12 @@
#ifndef OSRM_EXTRACTOR_GUIDANCE_MERGEABLE_ROADS
#define OSRM_EXTRACTOR_GUIDANCE_MERGEABLE_ROADS
#include "extractor/compressed_edge_container.hpp"
#include "extractor/guidance/coordinate_extractor.hpp"
#include "extractor/guidance/have_identical_names.hpp"
#include "extractor/guidance/intersection.hpp"
#include "extractor/guidance/turn_lane_types.hpp"
#include "extractor/restriction_index.hpp"
#include "util/coordinate.hpp"
#include "util/node_based_graph.hpp"
#include "util/typedefs.hpp"
@@ -9,6 +14,7 @@
#include <cstdint>
#include <functional>
#include <limits>
#include <unordered_set>
#include <vector>
namespace osrm
@@ -39,8 +45,10 @@ class MergableRoadDetector
MergableRoadDetector(const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &node_coordinates,
const IntersectionGenerator &intersection_generator,
const CoordinateExtractor &coordinate_extractor,
const extractor::CompressedEdgeContainer &compressed_geometries,
const RestrictionMap &node_restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table);
@@ -71,11 +79,6 @@ class MergableRoadDetector
bool IsDistinctFrom(const MergableRoadData &lhs, const MergableRoadData &rhs) const;
private:
// check if two name ids can be seen as identical (in presence of refs/others)
// in our case this translates into no name announcement in either direction (lhs->rhs and
// rhs->lhs)
bool HaveIdenticalNames(const NameID lhs, const NameID rhs) const;
// When it comes to merging roads, we need to find out if two ways actually represent the
// same road. This check tries to identify roads which are the same road in opposite directions
bool EdgeDataSupportsMerge(const NodeBasedEdgeClassification &lhs_flags,
@@ -159,15 +162,19 @@ class MergableRoadDetector
const util::NodeBasedDynamicGraph &node_based_graph;
const EdgeBasedNodeDataContainer &node_data_container;
const std::vector<util::Coordinate> &node_coordinates;
const IntersectionGenerator &intersection_generator;
const CoordinateExtractor &coordinate_extractor;
const extractor::CompressedEdgeContainer &compressed_geometries;
const RestrictionMap &node_restriction_map;
const std::unordered_set<NodeID> &barrier_nodes;
const guidance::TurnLanesIndexedArray &turn_lanes_data;
// name detection
const util::NameTable &name_table;
const SuffixTable &street_name_suffix_table;
const CoordinateExtractor coordinate_extractor;
// limit for detecting circles / parallel roads
const static double constexpr distance_to_extract = 150;
const static double constexpr distance_to_extract = 120;
};
} // namespace guidance
@@ -2,8 +2,8 @@
#define OSRM_EXTRACTOR_GUIDANCE_MOTORWAY_HANDLER_HPP_
#include "extractor/guidance/intersection.hpp"
#include "extractor/guidance/intersection_generator.hpp"
#include "extractor/guidance/intersection_handler.hpp"
#include "extractor/guidance/is_through_street.hpp"
#include "extractor/query_node.hpp"
#include "util/attributes.hpp"
@@ -26,9 +26,12 @@ class MotorwayHandler : public IntersectionHandler
MotorwayHandler(const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &coordinates,
const extractor::CompressedEdgeContainer &compressed_geometries,
const RestrictionMap &node_restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table,
const IntersectionGenerator &intersection_generator);
const SuffixTable &street_name_suffix_table);
~MotorwayHandler() override final = default;
@@ -2,7 +2,9 @@
#define OSRM_EXTRACTOR_GUIDANCE_NODE_BASED_GRAPH_WALKER
#include "extractor/guidance/constants.hpp"
#include "extractor/guidance/intersection_generator.hpp"
#include "extractor/guidance/coordinate_extractor.hpp"
#include "extractor/guidance/turn_lane_data.hpp"
#include "extractor/intersection/intersection_analysis.hpp"
#include "util/coordinate.hpp"
#include "util/coordinate_calculation.hpp"
#include "util/node_based_graph.hpp"
@@ -29,7 +31,11 @@ class NodeBasedGraphWalker
public:
NodeBasedGraphWalker(const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const IntersectionGenerator &intersection_generator);
const std::vector<util::Coordinate> &node_coordinates,
const extractor::CompressedEdgeContainer &compressed_geometries,
const RestrictionMap &node_restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data);
/*
* the returned node-id, edge-id are either the last ones used, just prior accumulator
@@ -48,7 +54,11 @@ class NodeBasedGraphWalker
private:
const util::NodeBasedDynamicGraph &node_based_graph;
const EdgeBasedNodeDataContainer &node_data_container;
const IntersectionGenerator &intersection_generator;
const std::vector<util::Coordinate> &node_coordinates;
const extractor::CompressedEdgeContainer &compressed_geometries;
const RestrictionMap &node_restriction_map;
const std::unordered_set<NodeID> &barrier_nodes;
const guidance::TurnLanesIndexedArray &turn_lanes_data;
};
/*
@@ -149,7 +159,13 @@ struct SelectStraightmostRoadByNameAndOnlyChoice
struct IntersectionFinderAccumulator
{
IntersectionFinderAccumulator(const std::uint8_t hop_limit,
const IntersectionGenerator &intersection_generator);
const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &node_coordinates,
const extractor::CompressedEdgeContainer &compressed_geometries,
const RestrictionMap &node_restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data);
// true if the path has traversed enough distance
bool terminate();
@@ -159,13 +175,19 @@ struct IntersectionFinderAccumulator
std::uint8_t hops;
const std::uint8_t hop_limit;
// we need to be able to look-up the intersection
const IntersectionGenerator &intersection_generator;
// the result we are looking for
NodeID nid;
EdgeID via_edge_id;
IntersectionView intersection;
private:
const util::NodeBasedDynamicGraph &node_based_graph;
const EdgeBasedNodeDataContainer &node_data_container;
const std::vector<util::Coordinate> &node_coordinates;
const extractor::CompressedEdgeContainer &compressed_geometries;
const RestrictionMap &node_restriction_map;
const std::unordered_set<NodeID> &barrier_nodes;
const guidance::TurnLanesIndexedArray &turn_lanes_data;
};
template <class accumulator_type, class selector_type>
@@ -199,9 +221,15 @@ NodeBasedGraphWalker::TraverseRoad(NodeID current_node_id,
return {};
// look at the next intersection
const constexpr auto LOW_PRECISION = true;
const auto next_intersection = intersection_generator.GetConnectedRoads(
current_node_id, current_edge_id, LOW_PRECISION);
const auto next_intersection =
intersection::getConnectedRoads<true>(node_based_graph,
node_data_container,
node_coordinates,
compressed_geometries,
node_restriction_map,
barrier_nodes,
turn_lanes_data,
{current_node_id, current_edge_id});
// don't follow u-turns or go past our initial intersection
if (next_intersection.size() <= 1)
@@ -4,8 +4,8 @@
#include "extractor/compressed_edge_container.hpp"
#include "extractor/guidance/coordinate_extractor.hpp"
#include "extractor/guidance/intersection.hpp"
#include "extractor/guidance/intersection_generator.hpp"
#include "extractor/guidance/intersection_handler.hpp"
#include "extractor/guidance/is_through_street.hpp"
#include "extractor/guidance/roundabout_type.hpp"
#include "extractor/query_node.hpp"
@@ -42,10 +42,12 @@ class RoundaboutHandler : public IntersectionHandler
RoundaboutHandler(const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &coordinates,
const CompressedEdgeContainer &compressed_edge_container,
const extractor::CompressedEdgeContainer &compressed_geometries,
const RestrictionMap &node_restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table,
const IntersectionGenerator &intersection_generator);
const SuffixTable &street_name_suffix_table);
~RoundaboutHandler() override final = default;
@@ -64,10 +66,6 @@ class RoundaboutHandler : public IntersectionHandler
const EdgeID via_eid,
const Intersection &intersection) const;
void invalidateExitAgainstDirection(const NodeID from_nid,
const EdgeID via_eid,
Intersection &intersection) const;
// decide whether we lookk at a roundabout or a rotary
RoundaboutType getRoundaboutType(const NodeID nid) const;
@@ -84,7 +82,6 @@ class RoundaboutHandler : public IntersectionHandler
bool
qualifiesAsRoundaboutIntersection(const std::unordered_set<NodeID> &roundabout_nodes) const;
const CompressedEdgeContainer &compressed_edge_container;
const CoordinateExtractor coordinate_extractor;
};
@@ -2,8 +2,8 @@
#define OSRM_EXTRACTOR_GUIDANCE_SLIPROAD_HANDLER_HPP_
#include "extractor/guidance/intersection.hpp"
#include "extractor/guidance/intersection_generator.hpp"
#include "extractor/guidance/intersection_handler.hpp"
#include "extractor/guidance/is_through_street.hpp"
#include "extractor/query_node.hpp"
#include "util/name_table.hpp"
@@ -24,10 +24,13 @@ namespace guidance
class SliproadHandler final : public IntersectionHandler
{
public:
SliproadHandler(const IntersectionGenerator &intersection_generator,
const util::NodeBasedDynamicGraph &node_based_graph,
SliproadHandler(const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &coordinates,
const extractor::CompressedEdgeContainer &compressed_geometries,
const RestrictionMap &node_restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table);
@@ -51,9 +54,6 @@ class SliproadHandler final : public IntersectionHandler
// Next intersection from `start` onto `onto` is too far away for a Siproad scenario
bool nextIntersectionIsTooFarAway(const NodeID start, const EdgeID onto) const;
// Through street: does a road continue with from's name at the intersection
bool isThroughStreet(const EdgeID from, const IntersectionView &intersection) const;
// Does the road from `current` to `next` continue
bool roadContinues(const EdgeID current, const EdgeID next) const;
@@ -78,6 +78,8 @@ class SliproadHandler final : public IntersectionHandler
// The return value is guaranteed to not be larger than `threshold`.
static double scaledThresholdByRoadClass(const double max_threshold,
const RoadClassification &classification);
const CoordinateExtractor coordinate_extractor;
};
} // namespace guidance
@@ -10,8 +10,8 @@
#include <algorithm>
#include <iomanip>
#include <iterator>
#include <map>
#include <mutex>
#include <unordered_map>
#include <cstdint>
@@ -27,18 +27,24 @@ namespace guidance
class StatisticsHandler final : public IntersectionHandler
{
public:
StatisticsHandler(const IntersectionGenerator &intersection_generator,
const util::NodeBasedDynamicGraph &node_based_graph,
StatisticsHandler(const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &coordinates,
const extractor::CompressedEdgeContainer &compressed_geometries,
const RestrictionMap &node_restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table)
: IntersectionHandler(node_based_graph,
node_data_container,
coordinates,
compressed_geometries,
node_restriction_map,
barrier_nodes,
turn_lanes_data,
name_table,
street_name_suffix_table,
intersection_generator)
street_name_suffix_table)
{
}
@@ -55,7 +61,7 @@ class StatisticsHandler final : public IntersectionHandler
for (const auto &kv : type_hist)
if (kv.second > 0)
util::Log() << std::fixed << std::setprecision(2)
util::Log() << " " << std::fixed << std::setprecision(2)
<< internalInstructionTypeToString(kv.first) << ": " << kv.second
<< " (" << (kv.second / static_cast<float>(num_types) * 100.) << "%)";
@@ -63,7 +69,7 @@ class StatisticsHandler final : public IntersectionHandler
for (const auto &kv : modifier_hist)
if (kv.second > 0)
util::Log() << std::fixed << std::setprecision(2)
util::Log() << " " << std::fixed << std::setprecision(2)
<< instructionModifierToString(kv.first) << ": " << kv.second << " ("
<< (kv.second / static_cast<float>(num_modifiers) * 100.) << "%)";
}
@@ -84,12 +90,14 @@ class StatisticsHandler final : public IntersectionHandler
// numbers closer to the handlers and see how often handlers ran.
for (const auto &road : intersection)
{
if (road.entry_allowed)
{
const auto type = road.instruction.type;
const auto modifier = road.instruction.direction_modifier;
const auto type = road.instruction.type;
const auto modifier = road.instruction.direction_modifier;
type_hist[type] += 1;
modifier_hist[modifier] += 1;
type_hist[type] += 1;
modifier_hist[modifier] += 1;
}
}
return intersection;
@@ -97,8 +105,8 @@ class StatisticsHandler final : public IntersectionHandler
private:
mutable std::mutex lock;
mutable std::unordered_map<TurnType::Enum, std::uint64_t> type_hist;
mutable std::unordered_map<DirectionModifier::Enum, std::uint64_t> modifier_hist;
mutable std::map<TurnType::Enum, std::uint64_t> type_hist;
mutable std::map<DirectionModifier::Enum, std::uint64_t> modifier_hist;
};
} // namespace guidance
@@ -3,7 +3,6 @@
#include "extractor/guidance/constants.hpp"
#include "extractor/guidance/intersection.hpp"
#include "extractor/guidance/intersection_generator.hpp"
#include "extractor/guidance/intersection_handler.hpp"
#include "extractor/travel_mode.hpp"
#include "util/node_based_graph.hpp"
@@ -21,10 +20,13 @@ namespace guidance
class SuppressModeHandler final : public IntersectionHandler
{
public:
SuppressModeHandler(const IntersectionGenerator &intersection_generator,
const util::NodeBasedDynamicGraph &node_based_graph,
SuppressModeHandler(const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &coordinates,
const extractor::CompressedEdgeContainer &compressed_geometries,
const RestrictionMap &node_restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table);
+3 -25
View File
@@ -4,9 +4,6 @@
#include "extractor/compressed_edge_container.hpp"
#include "extractor/guidance/driveway_handler.hpp"
#include "extractor/guidance/intersection.hpp"
#include "extractor/guidance/intersection_generator.hpp"
#include "extractor/guidance/intersection_normalization_operation.hpp"
#include "extractor/guidance/intersection_normalizer.hpp"
#include "extractor/guidance/motorway_handler.hpp"
#include "extractor/guidance/roundabout_handler.hpp"
#include "extractor/guidance/sliproad_handler.hpp"
@@ -43,10 +40,11 @@ class TurnAnalysis
public:
TurnAnalysis(const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &coordinates,
const std::vector<util::Coordinate> &node_coordinates,
const CompressedEdgeContainer &compressed_edge_container,
const RestrictionMap &restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const CompressedEdgeContainer &compressed_edge_container,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table);
@@ -56,34 +54,14 @@ class TurnAnalysis
Intersection operator()(const NodeID node_prior_to_intersection,
const EdgeID entering_via_edge) const;
/*
* Returns a normalized intersection without any assigned turn types.
* This intersection can be used as input for intersection classification, turn lane assignment
* and similar.
*/
struct ShapeResult
{
// the basic shape, containing all turns
IntersectionShape intersection_shape;
// normalized shape, merged some roads into others, adjusted bearings
// see intersection_normalizer for further explanations
IntersectionNormalizer::NormalizationResult annotated_normalized_shape;
};
OSRM_ATTR_WARN_UNUSED
ShapeResult ComputeIntersectionShapes(const NodeID node_at_center_of_intersection) const;
// Select turn types based on the intersection shape
OSRM_ATTR_WARN_UNUSED
Intersection AssignTurnTypes(const NodeID from_node,
const EdgeID via_eid,
const IntersectionView &intersection) const;
const IntersectionGenerator &GetIntersectionGenerator() const;
private:
const util::NodeBasedDynamicGraph &node_based_graph;
const IntersectionGenerator intersection_generator;
const IntersectionNormalizer intersection_normalizer;
const RoundaboutHandler roundabout_handler;
const MotorwayHandler motorway_handler;
const TurnHandler turn_handler;
+19 -2
View File
@@ -2,15 +2,27 @@
#define OSRM_EXTRACTOR_GUIDANCE_TURN_DISCOVERY_HPP_
#include "extractor/guidance/intersection.hpp"
#include "extractor/guidance/intersection_generator.hpp"
#include "extractor/guidance/turn_lane_data.hpp"
#include "extractor/restriction_index.hpp"
#include "util/typedefs.hpp"
#include <unordered_set>
namespace osrm
{
namespace util
{
struct Coordinate;
}
namespace extractor
{
class CompressedEdgeContainer;
namespace guidance
{
namespace lanes
{
@@ -21,8 +33,13 @@ bool findPreviousIntersection(
const NodeID node,
const EdgeID via_edge,
const Intersection &intersection,
const IntersectionGenerator &intersection_generator,
const util::NodeBasedDynamicGraph &node_based_graph, // query edge data
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &node_coordinates,
const extractor::CompressedEdgeContainer &compressed_geometries,
const RestrictionMap &node_restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
// output parameters, will be in an arbitrary state on failure
NodeID &result_node,
EdgeID &result_via_edge,
+6 -3
View File
@@ -2,8 +2,8 @@
#define OSRM_EXTRACTOR_GUIDANCE_TURN_HANDLER_HPP_
#include "extractor/guidance/intersection.hpp"
#include "extractor/guidance/intersection_generator.hpp"
#include "extractor/guidance/intersection_handler.hpp"
#include "extractor/guidance/is_through_street.hpp"
#include "extractor/query_node.hpp"
#include "util/attributes.hpp"
@@ -30,9 +30,12 @@ class TurnHandler : public IntersectionHandler
TurnHandler(const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &coordinates,
const extractor::CompressedEdgeContainer &compressed_geometries,
const RestrictionMap &node_restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table,
const IntersectionGenerator &intersection_generator);
const SuffixTable &street_name_suffix_table);
~TurnHandler() override final = default;
@@ -77,7 +77,11 @@ struct TurnInstruction
TurnType::Enum type : 5;
DirectionModifier::Enum direction_modifier : 3;
// the lane tupel that is used for the turn
bool IsUTurn() const
{
return type != TurnType::NoTurn && direction_modifier == DirectionModifier::UTurn;
}
static TurnInstruction INVALID() { return {TurnType::Invalid, DirectionModifier::UTurn}; }
@@ -74,6 +74,11 @@ class TurnLaneHandler
TurnLaneHandler(const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &node_coordinates,
const extractor::CompressedEdgeContainer &compressed_geometries,
const RestrictionMap &node_restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
LaneDescriptionMap &lane_description_map,
const TurnAnalysis &turn_analysis,
util::guidance::LaneDataIdMap &id_map);
@@ -90,6 +95,12 @@ class TurnLaneHandler
// lanes for a turn
const util::NodeBasedDynamicGraph &node_based_graph;
const EdgeBasedNodeDataContainer &node_data_container;
const std::vector<util::Coordinate> &node_coordinates;
const extractor::CompressedEdgeContainer &compressed_geometries;
const RestrictionMap &node_restriction_map;
const std::unordered_set<NodeID> &barrier_nodes;
const guidance::TurnLanesIndexedArray &turn_lanes_data;
std::vector<std::uint32_t> turn_lane_offsets;
std::vector<TurnLaneType::Mask> turn_lane_masks;
LaneDescriptionMap &lane_description_map;
@@ -100,8 +100,10 @@ typedef util::ConcurrentIDMap<guidance::TurnLaneDescription,
guidance::TurnLaneDescription_hash>
LaneDescriptionMap;
inline std::tuple<std::vector<std::uint32_t>, std::vector<TurnLaneType::Mask>>
transformTurnLaneMapIntoArrays(const LaneDescriptionMap &turn_lane_map)
using TurnLanesIndexedArray =
std::tuple<std::vector<std::uint32_t>, std::vector<TurnLaneType::Mask>>;
inline TurnLanesIndexedArray transformTurnLaneMapIntoArrays(const LaneDescriptionMap &turn_lane_map)
{
// could use some additional capacity? To avoid a copy during processing, though small data so
// probably not that important.
@@ -111,8 +113,7 @@ transformTurnLaneMapIntoArrays(const LaneDescriptionMap &turn_lane_map)
//
// turn lane offsets points into the locations of the turn_lane_masks array. We use a standard
// adjacency array like structure to store the turn lane masks.
std::vector<std::uint32_t> turn_lane_offsets(turn_lane_map.data.size() +
2); // empty ID + sentinel
std::vector<std::uint32_t> turn_lane_offsets(turn_lane_map.data.size() + 1); // + sentinel
for (auto entry = turn_lane_map.data.begin(); entry != turn_lane_map.data.end(); ++entry)
turn_lane_offsets[entry->second + 1] = entry->first.size();
@@ -125,6 +126,7 @@ transformTurnLaneMapIntoArrays(const LaneDescriptionMap &turn_lane_map)
std::copy(entry->first.begin(),
entry->first.end(),
turn_lane_masks.begin() + turn_lane_offsets[entry->second]);
return std::make_tuple(std::move(turn_lane_offsets), std::move(turn_lane_masks));
}
@@ -0,0 +1,87 @@
#ifndef OSRM_EXTRACTOR_INTERSECTION_INTERSECTION_ANALYSIS_HPP
#define OSRM_EXTRACTOR_INTERSECTION_INTERSECTION_ANALYSIS_HPP
#include "extractor/compressed_edge_container.hpp"
#include "extractor/guidance/mergable_road_detector.hpp"
#include "extractor/guidance/turn_lane_types.hpp"
#include "extractor/intersection/intersection_edge.hpp"
#include "extractor/restriction_index.hpp"
#include "util/coordinate.hpp"
#include "util/node_based_graph.hpp"
#include <unordered_set>
#include <vector>
namespace osrm
{
namespace extractor
{
namespace intersection
{
IntersectionEdges getIncomingEdges(const util::NodeBasedDynamicGraph &graph,
const NodeID intersection);
IntersectionEdges getOutgoingEdges(const util::NodeBasedDynamicGraph &graph,
const NodeID intersection);
bool isTurnAllowed(const util::NodeBasedDynamicGraph &graph,
const EdgeBasedNodeDataContainer &node_data_container,
const RestrictionMap &restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const IntersectionEdgeGeometries &geometries,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
const IntersectionEdge &from,
const IntersectionEdge &to);
double findEdgeBearing(const IntersectionEdgeGeometries &geometries, const EdgeID &edge);
double findEdgeLength(const IntersectionEdgeGeometries &geometries, const EdgeID &edge);
std::pair<IntersectionEdgeGeometries, std::unordered_set<EdgeID>>
getIntersectionGeometries(const util::NodeBasedDynamicGraph &graph,
const extractor::CompressedEdgeContainer &compressed_geometries,
const std::vector<util::Coordinate> &node_coordinates,
const guidance::MergableRoadDetector &detector,
const NodeID intersection);
guidance::IntersectionView
convertToIntersectionView(const util::NodeBasedDynamicGraph &graph,
const EdgeBasedNodeDataContainer &node_data_container,
const RestrictionMap &restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const IntersectionEdgeGeometries &edge_geometries,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
const IntersectionEdge &incoming_edge,
const IntersectionEdges &outgoing_edges,
const std::unordered_set<EdgeID> &merged_edges);
// Check for restrictions/barriers and generate a list of valid and invalid turns present at
// the node reached from `incoming_edge`. The resulting candidates have to be analyzed
// for their actual instructions later on.
template <bool USE_CLOSE_COORDINATE>
guidance::IntersectionView
getConnectedRoads(const util::NodeBasedDynamicGraph &graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &node_coordinates,
const extractor::CompressedEdgeContainer &compressed_geometries,
const RestrictionMap &node_restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
const IntersectionEdge &incoming_edge);
// Graph Compression cannot compress every setting. For example any barrier/traffic light cannot
// be compressed. As a result, a simple road of the form `a ----- b` might end up as having an
// intermediate intersection, if there is a traffic light in between. If we want to look farther
// down a road, finding the next actual decision requires the look at multiple intersections.
// Here we follow the road until we either reach a dead end or find the next intersection with
// more than a single next road. This function skips over degree two nodes to find correct input
// for getConnectedRoads.
IntersectionEdge skipDegreeTwoNodes(const util::NodeBasedDynamicGraph &graph,
IntersectionEdge road);
}
}
}
#endif
@@ -0,0 +1,44 @@
#ifndef OSRM_EXTRACTOR_INTERSECTION_INTERSECTION_EDGE_HPP
#define OSRM_EXTRACTOR_INTERSECTION_INTERSECTION_EDGE_HPP
#include "util/typedefs.hpp"
#include <vector>
namespace osrm
{
namespace extractor
{
namespace intersection
{
// IntersectionEdge is an alias for incoming and outgoing node-based graph edges of an intersection
struct IntersectionEdge
{
NodeID node;
EdgeID edge;
bool operator<(const IntersectionEdge &other) const
{
return std::tie(node, edge) < std::tie(other.node, other.edge);
}
};
using IntersectionEdges = std::vector<IntersectionEdge>;
struct IntersectionEdgeGeometry
{
EdgeID edge;
double initial_bearing;
double perceived_bearing;
double length;
bool operator<(const IntersectionEdgeGeometry &other) const { return edge < other.edge; }
};
using IntersectionEdgeGeometries = std::vector<IntersectionEdgeGeometry>;
}
}
}
#endif
@@ -10,8 +10,6 @@
#include <boost/optional/optional.hpp>
#include <tbb/concurrent_vector.h>
#include <string>
#include <vector>
+5
View File
@@ -186,6 +186,8 @@ inline engine_config_ptr argumentsToEngineConfig(const Nan::FunctionCallbackInfo
params->Get(Nan::New("max_locations_map_matching").ToLocalChecked());
auto max_results_nearest = params->Get(Nan::New("max_results_nearest").ToLocalChecked());
auto max_alternatives = params->Get(Nan::New("max_alternatives").ToLocalChecked());
auto max_radius_map_matching =
params->Get(Nan::New("max_radius_map_matching").ToLocalChecked());
if (!max_locations_trip->IsUndefined() && !max_locations_trip->IsNumber())
{
@@ -233,6 +235,9 @@ inline engine_config_ptr argumentsToEngineConfig(const Nan::FunctionCallbackInfo
engine_config->max_results_nearest = static_cast<int>(max_results_nearest->NumberValue());
if (max_alternatives->IsNumber())
engine_config->max_alternatives = static_cast<int>(max_alternatives->NumberValue());
if (max_radius_map_matching->IsNumber())
engine_config->max_radius_map_matching =
static_cast<double>(max_radius_map_matching->NumberValue());
return engine_config;
}
+1
View File
@@ -16,6 +16,7 @@
#include <boost/iterator/iterator_facade.hpp>
#include <boost/range/iterator_range.hpp>
#include <tbb/parallel_sort.h>
#include <algorithm>
@@ -10,8 +10,9 @@
#include "util/dynamic_graph.hpp"
#include "util/typedefs.hpp"
#include <tbb/blocked_range.h>
#include <tbb/parallel_for.h>
#include <tbb/parallel_reduce.h>
#include <tbb/parallel_sort.h>
#include <cstdint>
@@ -28,17 +28,31 @@ struct MatchParametersGrammar final : public RouteParametersGrammar<Iterator, Si
MatchParametersGrammar() : BaseGrammar(root_rule)
{
#ifdef BOOST_HAS_LONG_LONG
if (std::is_same<std::size_t, unsigned long long>::value)
size_t_ = qi::ulong_long;
else
size_t_ = qi::ulong_;
#else
size_t_ = qi::ulong_;
#endif
timestamps_rule =
qi::lit("timestamps=") >
(qi::uint_ %
';')[ph::bind(&engine::api::MatchParameters::timestamps, qi::_r1) = qi::_1];
waypoints_rule =
qi::lit("waypoints=") >
(size_t_ % ';')[ph::bind(&engine::api::MatchParameters::waypoints, qi::_r1) = qi::_1];
gaps_type.add("split", engine::api::MatchParameters::GapsType::Split)(
"ignore", engine::api::MatchParameters::GapsType::Ignore);
root_rule =
BaseGrammar::query_rule(qi::_r1) > -qi::lit(".json") >
-('?' > (timestamps_rule(qi::_r1) | BaseGrammar::base_rule(qi::_r1) |
waypoints_rule(qi::_r1) |
(qi::lit("gaps=") >
gaps_type[ph::bind(&engine::api::MatchParameters::gaps, qi::_r1) = qi::_1]) |
(qi::lit("tidy=") >
@@ -49,6 +63,8 @@ struct MatchParametersGrammar final : public RouteParametersGrammar<Iterator, Si
private:
qi::rule<Iterator, Signature> root_rule;
qi::rule<Iterator, Signature> timestamps_rule;
qi::rule<Iterator, Signature> waypoints_rule;
qi::rule<Iterator, std::size_t()> size_t_;
qi::symbols<char, engine::api::MatchParameters::GapsType> gaps_type;
};
-12
View File
@@ -144,18 +144,6 @@ inline double restrictAngleToValidRange(const double angle)
return angle;
}
// finds the angle between two angles, based on the minum difference between the two
inline double angleBetween(const double lhs, const double rhs)
{
const auto difference = std::abs(lhs - rhs);
const auto is_clockwise_difference = difference <= 180;
const auto angle_between_candidate = .5 * (lhs + rhs);
if (is_clockwise_difference)
return angle_between_candidate;
else
return restrictAngleToValidRange(angle_between_candidate + 180);
}
} // namespace util
} // namespace osrm
+38 -63
View File
@@ -26,7 +26,8 @@ namespace guidance
// Name Change Logic
// Used both during Extraction as well as during Post-Processing
inline std::string longest_common_substring(const std::string &lhs, const std::string &rhs)
inline util::StringView longest_common_substring(const util::StringView &lhs,
const util::StringView &rhs)
{
if (lhs.empty() || rhs.empty())
return "";
@@ -60,15 +61,15 @@ inline std::string longest_common_substring(const std::string &lhs, const std::s
// TODO US-ASCII support only, no UTF-8 support
// While UTF-8 might work in some cases, we do not guarantee full functionality
inline auto decompose(const std::string &lhs, const std::string &rhs)
template <typename StringView> inline auto decompose(const StringView &lhs, const StringView &rhs)
{
auto const lcs = longest_common_substring(lhs, rhs);
// trim spaces, transform to lower
const auto trim = [](auto str) {
const auto trim = [](StringView view) {
// we compare suffixes based on this value, it might break UTF chars, but as long as we are
// consistent in handling, we do not create bad results
boost::to_lower(str);
std::string str = boost::to_lower_copy(view.to_string());
auto front = str.find_first_not_of(" ");
if (front == std::string::npos)
@@ -80,8 +81,7 @@ inline auto decompose(const std::string &lhs, const std::string &rhs)
if (lcs.empty())
{
std::string empty = "";
return std::make_tuple(trim(lhs), trim(rhs), empty, empty);
return std::make_tuple(trim(lhs), trim(rhs), std::string(), std::string());
}
// find the common substring in both
@@ -92,32 +92,27 @@ inline auto decompose(const std::string &lhs, const std::string &rhs)
BOOST_ASSERT(rhs_pos + lcs.size() <= rhs.size());
// prefixes
std::string lhs_prefix = (lhs_pos > 0) ? lhs.substr(0, lhs_pos) : "";
std::string rhs_prefix = (rhs_pos > 0) ? rhs.substr(0, rhs_pos) : "";
auto lhs_prefix = (lhs_pos > 0) ? lhs.substr(0, lhs_pos) : StringView();
auto rhs_prefix = (rhs_pos > 0) ? rhs.substr(0, rhs_pos) : StringView();
// suffices
std::string lhs_suffix = lhs.substr(lhs_pos + lcs.size());
std::string rhs_suffix = rhs.substr(rhs_pos + lcs.size());
auto lhs_suffix = lhs.substr(lhs_pos + lcs.size());
auto rhs_suffix = rhs.substr(rhs_pos + lcs.size());
lhs_prefix = trim(std::move(lhs_prefix));
lhs_suffix = trim(std::move(lhs_suffix));
rhs_prefix = trim(std::move(rhs_prefix));
rhs_suffix = trim(std::move(rhs_suffix));
return std::make_tuple(lhs_prefix, lhs_suffix, rhs_prefix, rhs_suffix);
return std::make_tuple(trim(lhs_prefix), trim(lhs_suffix), trim(rhs_prefix), trim(rhs_suffix));
}
// Note: there is an overload without suffix checking below.
// (that's the reason we template the suffix table here)
template <typename SuffixTable>
inline bool requiresNameAnnounced(const std::string &from_name,
const std::string &from_ref,
const std::string &from_pronunciation,
const std::string &from_exits,
const std::string &to_name,
const std::string &to_ref,
const std::string &to_pronunciation,
const std::string &to_exits,
template <typename StringView, typename SuffixTable>
inline bool requiresNameAnnounced(const StringView &from_name,
const StringView &from_ref,
const StringView &from_pronunciation,
const StringView &from_exits,
const StringView &to_name,
const StringView &to_ref,
const StringView &to_pronunciation,
const StringView &to_exits,
const SuffixTable &suffix_table)
{
// first is empty and the second is not
@@ -134,7 +129,7 @@ inline bool requiresNameAnnounced(const std::string &from_name,
boost::starts_with(from_name, to_name) || boost::starts_with(to_name, from_name);
const auto checkForPrefixOrSuffixChange =
[](const std::string &first, const std::string &second, const SuffixTable &suffix_table) {
[](const StringView &first, const StringView &second, const SuffixTable &suffix_table) {
std::string first_prefix, first_suffix, second_prefix, second_suffix;
std::tie(first_prefix, first_suffix, second_prefix, second_suffix) =
decompose(first, second);
@@ -203,17 +198,17 @@ inline bool requiresNameAnnounced(const std::string &from_name,
struct NopSuffixTable final
{
NopSuffixTable() {}
bool isSuffix(const std::string &) const { return false; }
bool isSuffix(const StringView &) const { return false; }
} static const table;
return requiresNameAnnounced(from_name,
from_ref,
from_pronunciation,
from_exits,
to_name,
to_ref,
to_pronunciation,
to_exits,
return requiresNameAnnounced(util::StringView(from_name),
util::StringView(from_ref),
util::StringView(from_pronunciation),
util::StringView(from_exits),
util::StringView(to_name),
util::StringView(to_ref),
util::StringView(to_pronunciation),
util::StringView(to_exits),
table);
}
@@ -225,37 +220,17 @@ inline bool requiresNameAnnounced(const NameID from_name_id,
if (from_name_id == to_name_id)
return false;
else
return requiresNameAnnounced(name_table.GetNameForID(from_name_id).to_string(),
name_table.GetRefForID(from_name_id).to_string(),
name_table.GetPronunciationForID(from_name_id).to_string(),
name_table.GetExitsForID(from_name_id).to_string(),
return requiresNameAnnounced(name_table.GetNameForID(from_name_id),
name_table.GetRefForID(from_name_id),
name_table.GetPronunciationForID(from_name_id),
name_table.GetExitsForID(from_name_id),
//
name_table.GetNameForID(to_name_id).to_string(),
name_table.GetRefForID(to_name_id).to_string(),
name_table.GetPronunciationForID(to_name_id).to_string(),
name_table.GetExitsForID(to_name_id).to_string(),
name_table.GetNameForID(to_name_id),
name_table.GetRefForID(to_name_id),
name_table.GetPronunciationForID(to_name_id),
name_table.GetExitsForID(to_name_id),
//
suffix_table);
// FIXME: converts StringViews to strings since the name change heuristics mutates in place
}
inline bool requiresNameAnnounced(const NameID from_name_id,
const NameID to_name_id,
const util::NameTable &name_table)
{
if (from_name_id == to_name_id)
return false;
else
return requiresNameAnnounced(name_table.GetNameForID(from_name_id).to_string(),
name_table.GetRefForID(from_name_id).to_string(),
name_table.GetPronunciationForID(from_name_id).to_string(),
name_table.GetExitsForID(from_name_id).to_string(),
//
name_table.GetNameForID(to_name_id).to_string(),
name_table.GetRefForID(to_name_id).to_string(),
name_table.GetExitsForID(to_name_id).to_string(),
name_table.GetPronunciationForID(to_name_id).to_string());
// FIXME: converts StringViews to strings since the name change heuristics mutates in place
}
} // namespace guidance
+1
View File
@@ -10,6 +10,7 @@
#include <boost/iterator/iterator_facade.hpp>
#include <boost/iterator/reverse_iterator.hpp>
#include <tbb/atomic.h>
#include <array>
+1
View File
@@ -23,6 +23,7 @@
#include <boost/format.hpp>
#include <boost/iostreams/device/mapped_file.hpp>
#include <tbb/blocked_range.h>
#include <tbb/parallel_for.h>
#include <tbb/parallel_sort.h>
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "osrm",
"version": "5.15.0-latest.1",
"version": "5.15.1",
"private": false,
"description": "The Open Source Routing Machine is a high performance routing engine written in C++14 designed to run on OpenStreetMap data.",
"dependencies": {
+9 -2
View File
@@ -37,6 +37,10 @@ function setup()
turn_bias = 1.075,
cardinal_directions = false,
-- Size of the vehicle, to be limited by physical restriction of the way
vehicle_height = 2.5, -- in metters, 2.5m is the height of van
vehicle_width = 1.9, -- in metters, ways with narrow tag are considered narrower than 2.2m
-- a list of suffixes to suppress in name change instructions. The suffixes also include common substrings of each other
suffix_list = {
'N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW', 'North', 'South', 'West', 'East', 'Nor', 'Sou', 'We', 'Ea'
@@ -100,7 +104,7 @@ function setup()
},
classes = Sequence {
'toll', 'motorway', 'ferry', 'restricted'
'toll', 'motorway', 'ferry', 'restricted', 'tunnel'
},
-- classes to support for exclude flags
@@ -253,6 +257,8 @@ function setup()
["at:rural"] = 100,
["at:trunk"] = 100,
["be:motorway"] = 120,
["by:urban"] = 60,
["by:motorway"] = 110,
["ch:rural"] = 80,
["ch:trunk"] = 100,
["ch:motorway"] = 120,
@@ -276,7 +282,6 @@ function setup()
["ru:living_street"] = 20,
["ru:urban"] = 60,
["ru:motorway"] = 110,
["ua:urban"] = 60,
["uk:nsl_single"] = (60*1609)/1000,
["uk:nsl_dual"] = (70*1609)/1000,
["uk:motorway"] = (70*1609)/1000,
@@ -357,6 +362,8 @@ function process_way(profile, way, result, relations)
-- toll=yes and oneway=reversible
WayHandlers.blocked_ways,
WayHandlers.avoid_ways,
WayHandlers.handle_height,
WayHandlers.handle_width,
-- determine access status by checking our hierarchy of
-- access tags, e.g: motorcar, motor_vehicle, vehicle
+81
View File
@@ -0,0 +1,81 @@
local Sequence = require('lib/sequence')
Measure = {}
-- measurements conversion constants
local inch_to_meters = 0.0254
local feet_to_inches = 12
--- Parse string as a height in meters.
--- according to http://wiki.openstreetmap.org/wiki/Key:maxheight
function Measure.parse_value_meters(value)
local n = tonumber(value:gsub(",", "."):match("%d+%.?%d*"))
if n then
inches = value:match("'.*")
if inches then -- Imperial unit to metric
-- try to parse feets/inch
n = n * feet_to_inches
local m = tonumber(inches:match("%d+"))
if m then
n = n + m
end
n = n * inch_to_meters
end
return n
end
end
--- according to http://wiki.openstreetmap.org/wiki/Map_Features/Units#Explicit_specifications
local tonns_parse_patterns = Sequence {
"%d+",
"%d+.%d+",
"%d+.%d+ ?t"
}
local kg_parse_patterns = Sequence {
"%d+ ?kg"
}
--- Parse weight value in kilograms
function Measure.parse_value_kilograms(value)
-- try to parse kilograms
for i, templ in ipairs(kg_parse_patterns) do
m = string.match(value, templ)
if m then
return tonumber(m)
end
end
-- try to parse tonns
for i, templ in ipairs(tonns_parse_patterns) do
m = string.match(value, templ)
if m then
return tonumber(m) * 1000
end
end
end
--- Get maxheight of specified way in meters. If there are no
--- max height, then return nil
function Measure.get_max_height(raw_value)
if raw_value then
return Measure.parse_value_meters(raw_value)
end
end
--- Get maxwidth of specified way in meters.
function Measure.get_max_width(raw_value)
if raw_value then
return Measure.parse_value_meters(raw_value)
end
end
--- Get maxweight of specified way in kilogramms
function Measure.get_max_weight(raw_value)
if raw_value then
return Measure.parse_value_kilograms(raw_value)
end
end
return Measure;
+48
View File
@@ -8,6 +8,7 @@ local get_turn_lanes = require("lib/guidance").get_turn_lanes
local set_classification = require("lib/guidance").set_classification
local get_destination = require("lib/destination").get_destination
local Tags = require('lib/tags')
local Measure = require("lib/measure")
WayHandlers = {}
@@ -282,6 +283,12 @@ end
function WayHandlers.classes(profile,way,result,data)
local forward_toll, backward_toll = Tags.get_forward_backward_by_key(way, data, "toll")
local forward_route, backward_route = Tags.get_forward_backward_by_key(way, data, "route")
local tunnel = way:get_value_by_key("tunnel")
if tunnel and tunnel ~= "no" then
result.forward_classes["tunnel"] = true
result.backward_classes["tunnel"] = true
end
if forward_toll == "yes" then
result.forward_classes["toll"] = true
@@ -429,6 +436,47 @@ function WayHandlers.parse_maxspeed(source,profile)
return n
end
-- handle maxheight tags
function WayHandlers.handle_height(profile,way,result,data)
local keys = Sequence { 'maxheight:physical', 'maxheight' }
local forward, backward = Tags.get_forward_backward_by_set(way,data,keys)
forward = Measure.get_max_height(forward)
backward = Measure.get_max_height(backward)
if forward and forward < profile.vehicle_height then
result.forward_mode = mode.inaccessible
end
if backward and backward < profile.vehicle_height then
result.backward_mode = mode.inaccessible
end
end
-- handle maxwidth tags
function WayHandlers.handle_width(profile,way,result,data)
local keys = Sequence { 'maxwidth:physical', 'maxwidth', 'width', 'est_width' }
local forward, backward = Tags.get_forward_backward_by_set(way,data,keys)
local narrow = way:get_value_by_key('narrow')
if ((forward and forward == 'narrow') or (narrow and narrow == 'yes')) and profile.vehicle_width > 2.2 then
result.forward_mode = mode.inaccessible
elseif forward then
forward = Measure.get_max_width(forward)
if forward and forward <= profile.vehicle_width then
result.forward_mode = mode.inaccessible
end
end
if ((backward and backward == 'narrow') or (narrow and narrow == 'yes')) and profile.vehicle_width > 2.2 then
result.backward_mode = mode.inaccessible
elseif backward then
backward = Measure.get_max_width(backward)
if backward and backward <= profile.vehicle_width then
result.backward_mode = mode.inaccessible
end
end
end
-- handle oneways tags
function WayHandlers.oneway(profile,way,result,data)
if not profile.oneway_handling then
+2 -2
View File
@@ -71,10 +71,10 @@ def build_pretty_printer():
pp.add_printer('TurnLaneData', '::TurnLaneData$', TurnLaneDataPrinter)
return pp
gdb.pretty_printers = [x for x in gdb.pretty_printers if x.name != 'OSRM'] # unregister OSRM pretty printer before (re)loading
## unregister OSRM pretty printer before (re)loading
gdb.pretty_printers = [x for x in gdb.pretty_printers if not isinstance(x, gdb.printing.RegexpCollectionPrettyPrinter) or x.name != 'OSRM']
gdb.printing.register_pretty_printer(gdb.current_objfile(), build_pretty_printer())
import geojson
import os
import time
+1
View File
@@ -12,6 +12,7 @@
#include <boost/assert.hpp>
#include <tbb/blocked_range.h>
#include <tbb/enumerable_thread_specific.h>
#include <tbb/parallel_for.h>
#include <tbb/parallel_invoke.h>
+2 -1
View File
@@ -11,12 +11,13 @@ bool EngineConfig::IsValid() const
// leads to an empty path
const bool all_path_are_empty = storage_config.GetPath("").empty();
const auto unlimited_or_more_than = [](const int v, const int limit) {
const auto unlimited_or_more_than = [](const auto v, const auto limit) {
return v == -1 || v > limit;
};
const bool limits_valid = unlimited_or_more_than(max_locations_distance_table, 2) &&
unlimited_or_more_than(max_locations_map_matching, 2) &&
unlimited_or_more_than(max_radius_map_matching, 0) &&
unlimited_or_more_than(max_locations_trip, 2) &&
unlimited_or_more_than(max_locations_viaroute, 2) &&
unlimited_or_more_than(max_results_nearest, 0) &&
+72 -3
View File
@@ -17,6 +17,7 @@
#include <functional>
#include <iterator>
#include <memory>
#include <set>
#include <string>
#include <vector>
@@ -139,6 +140,17 @@ Status MatchPlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms,
return Error("InvalidValue", "Invalid coordinate value.", json_result);
}
if (max_radius_map_matching > 0 && std::any_of(parameters.radiuses.begin(),
parameters.radiuses.end(),
[&](const auto &radius) {
if (!radius)
return false;
return *radius > max_radius_map_matching;
}))
{
return Error("TooBig", "Radius search size is too large for map matching.", json_result);
}
// Check for same or increasing timestamps. Impl. note: Incontrast to `sort(first,
// last, less_equal)` checking `greater` in reverse meets irreflexive requirements.
const auto time_increases_monotonically = std::is_sorted(
@@ -163,6 +175,16 @@ Status MatchPlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms,
tidied = api::tidy::keep_all(parameters);
}
// Error: first and last points should be waypoints
if (!parameters.waypoints.empty() &&
(tidied.parameters.waypoints[0] != 0 ||
tidied.parameters.waypoints.back() != (tidied.parameters.coordinates.size() - 1)))
{
return Error("InvalidValue",
"First and last coordinates must be specified as waypoints.",
json_result);
}
// assuming radius is the standard deviation of a normal distribution
// that models GPS noise (in this model), x3 should give us the correct
// search radius with > 99% confidence
@@ -218,6 +240,34 @@ Status MatchPlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms,
return Error("NoMatch", "Could not match the trace.", json_result);
}
// trace was split, we don't support the waypoints parameter across multiple match objects
if (sub_matchings.size() > 1 && !parameters.waypoints.empty())
{
return Error("NoMatch", "Could not match the trace with the given waypoints.", json_result);
}
// Error: Check if user-supplied waypoints can be found in the resulting matches
if (!parameters.waypoints.empty())
{
std::set<std::size_t> tidied_waypoints(tidied.parameters.waypoints.begin(),
tidied.parameters.waypoints.end());
for (const auto &sm : sub_matchings)
{
std::for_each(sm.indices.begin(),
sm.indices.end(),
[&tidied_waypoints](const auto index) { tidied_waypoints.erase(index); });
}
if (!tidied_waypoints.empty())
{
return Error(
"NoMatch", "Requested waypoint parameter could not be matched.", json_result);
}
}
// we haven't errored yet, only allow leg collapsing if it was originally requested
BOOST_ASSERT(parameters.waypoints.empty() || sub_matchings.size() == 1);
const auto collapse_legs = !parameters.waypoints.empty();
// each sub_route will correspond to a MatchObject
std::vector<InternalRouteResult> sub_routes(sub_matchings.size());
for (auto index : util::irange<std::size_t>(0UL, sub_matchings.size()))
{
@@ -234,12 +284,31 @@ Status MatchPlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms,
BOOST_ASSERT(current_phantom_node_pair.target_phantom.IsValid());
sub_routes[index].segment_end_coordinates.emplace_back(current_phantom_node_pair);
}
// force uturns to be on, since we split the phantom nodes anyway and only have
// bi-directional
// phantom nodes for possible uturns
// force uturns to be on
// we split the phantom nodes anyway and only have bi-directional phantom nodes for
// possible uturns
sub_routes[index] =
algorithms.ShortestPathSearch(sub_routes[index].segment_end_coordinates, {false});
BOOST_ASSERT(sub_routes[index].shortest_path_weight != INVALID_EDGE_WEIGHT);
if (collapse_legs)
{
std::vector<bool> waypoint_legs;
waypoint_legs.reserve(sub_matchings[index].indices.size());
for (unsigned i = 0, j = 0; i < sub_matchings[index].indices.size(); ++i)
{
auto current_wp = tidied.parameters.waypoints[j];
if (current_wp == sub_matchings[index].indices[i])
{
waypoint_legs.push_back(true);
++j;
}
else
{
waypoint_legs.push_back(false);
}
}
sub_routes[index] = CollapseInternalRouteResult(sub_routes[index], waypoint_legs);
}
}
api::MatchAPI match_api{facade, parameters, tidied};
+127 -92
View File
@@ -7,6 +7,8 @@
#include "extractor/scripting_environment.hpp"
#include "extractor/suffix_table.hpp"
#include "extractor/intersection/intersection_analysis.hpp"
#include "extractor/serialization.hpp"
#include "storage/io.hpp"
@@ -375,7 +377,9 @@ EdgeBasedGraphFactory::GenerateEdgeExpandedNodes(const WayRestrictionMap &way_re
m_edge_based_node_container.nodes[edge_based_node_id].segregated =
segregated_edges.count(eid) > 0;
m_edge_based_node_weights.push_back(m_edge_based_node_weights[eid]);
const auto ebn_weight = m_edge_based_node_weights[nbe_to_ebn_mapping[eid]];
BOOST_ASSERT(ebn_weight == INVALID_EDGE_WEIGHT || ebn_weight == edge_data.weight);
m_edge_based_node_weights.push_back(ebn_weight);
edge_based_node_id++;
progress.PrintStatus(progress_counter++);
@@ -419,22 +423,39 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
TurnDataExternalContainer turn_data_container;
SuffixTable street_name_suffix_table(scripting_environment);
const auto &turn_lanes_data = transformTurnLaneMapIntoArrays(lane_description_map);
guidance::MergableRoadDetector mergable_road_detector(m_node_based_graph,
m_edge_based_node_container,
m_coordinates,
m_compressed_edge_container,
node_restriction_map,
m_barrier_nodes,
turn_lanes_data,
name_table,
street_name_suffix_table);
// Loop over all turns and generate new set of edges.
// Three nested loop look super-linear, but we are dealing with a (kind of)
// linear number of turns only.
SuffixTable street_name_suffix_table(scripting_environment);
guidance::TurnAnalysis turn_analysis(m_node_based_graph,
m_edge_based_node_container,
m_coordinates,
m_compressed_edge_container,
node_restriction_map,
m_barrier_nodes,
m_compressed_edge_container,
turn_lanes_data,
name_table,
street_name_suffix_table);
util::guidance::LaneDataIdMap lane_data_map;
guidance::lanes::TurnLaneHandler turn_lane_handler(m_node_based_graph,
m_edge_based_node_container,
m_coordinates,
m_compressed_edge_container,
node_restriction_map,
m_barrier_nodes,
turn_lanes_data,
lane_description_map,
turn_analysis,
lane_data_map);
@@ -537,14 +558,14 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
// the situation of the turn
const auto node_along_road_entering,
const auto node_based_edge_from,
const auto node_at_center_of_intersection,
const auto intersection_node,
const auto node_based_edge_to,
const auto &intersection,
const auto incoming_bearing,
const auto &turn,
const auto entry_class_id) {
const auto node_restricted = isRestricted(node_along_road_entering,
node_at_center_of_intersection,
intersection_node,
m_node_based_graph.GetTarget(turn.eid),
conditional_restriction_map);
@@ -556,7 +577,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
conditional = {{edge_based_node_from,
edge_based_node_to,
{static_cast<std::uint64_t>(-1),
m_coordinates[node_at_center_of_intersection],
m_coordinates[intersection_node],
conditions}}};
}
@@ -572,15 +593,15 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
TurnData turn_data = {turn.instruction,
turn.lane_data_id,
entry_class_id,
util::guidance::TurnBearing(intersection[0].bearing),
util::guidance::TurnBearing(incoming_bearing),
util::guidance::TurnBearing(turn.bearing)};
// compute weight and duration penalties
auto is_traffic_light = m_traffic_lights.count(node_at_center_of_intersection);
auto is_traffic_light = m_traffic_lights.count(intersection_node);
ExtractionTurn extracted_turn(
turn.angle,
m_node_based_graph.GetOutDegree(node_at_center_of_intersection),
turn.instruction.direction_modifier == guidance::DirectionModifier::UTurn,
m_node_based_graph.GetOutDegree(intersection_node),
turn.instruction.IsUTurn(),
is_traffic_light,
edge_data1.flags.restricted,
edge_data2.flags.restricted,
@@ -630,8 +651,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
: m_compressed_edge_container.GetLastEdgeSourceID(node_based_edge_from);
const auto &to_node = m_compressed_edge_container.GetFirstEdgeTargetID(turn.eid);
lookup::TurnIndexBlock turn_index_block = {
from_node, node_at_center_of_intersection, to_node};
lookup::TurnIndexBlock turn_index_block = {from_node, intersection_node, to_node};
// insert data into the designated buffer
return std::make_pair(
@@ -653,17 +673,26 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
if (buffer->nodes_processed == 0)
return buffer;
for (auto node_at_center_of_intersection = intersection_node_range.begin(),
for (auto intersection_node = intersection_node_range.begin(),
end = intersection_node_range.end();
node_at_center_of_intersection < end;
++node_at_center_of_intersection)
intersection_node < end;
++intersection_node)
{
// We capture the thread-local work in these objects, then flush
// them in a controlled manner at the end of the parallel range
const auto &incoming_edges =
intersection::getIncomingEdges(m_node_based_graph, intersection_node);
const auto &outgoing_edges =
intersection::getOutgoingEdges(m_node_based_graph, intersection_node);
const auto shape_result =
turn_analysis.ComputeIntersectionShapes(node_at_center_of_intersection);
intersection::IntersectionEdgeGeometries edge_geometries;
std::unordered_set<EdgeID> merged_edge_ids;
std::tie(edge_geometries, merged_edge_ids) =
intersection::getIntersectionGeometries(m_node_based_graph,
m_compressed_edge_container,
m_coordinates,
mergable_road_detector,
intersection_node);
// all nodes in the graph are connected in both directions. We check all
// outgoing nodes to find the incoming edge. This is a larger search overhead,
@@ -683,45 +712,33 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
// From the flags alone, we cannot determine which nodes are connected to
// `b` by an outgoing edge. Therefore, we have to search all connected edges for
// edges entering `b`
for (const EdgeID outgoing_edge :
m_node_based_graph.GetAdjacentEdgeRange(node_at_center_of_intersection))
for (const auto &incoming_edge : incoming_edges)
{
const NodeID node_along_road_entering =
m_node_based_graph.GetTarget(outgoing_edge);
const auto incoming_edge = m_node_based_graph.FindEdge(
node_along_road_entering, node_at_center_of_intersection);
if (m_node_based_graph.GetEdgeData(incoming_edge).reversed)
continue;
++node_based_edge_counter;
auto intersection_with_flags_and_angles =
turn_analysis.GetIntersectionGenerator()
.TransformIntersectionShapeIntoView(
node_along_road_entering,
incoming_edge,
shape_result.annotated_normalized_shape.normalized_shape,
shape_result.intersection_shape,
shape_result.annotated_normalized_shape.performed_merges);
const auto intersection_view =
convertToIntersectionView(m_node_based_graph,
m_edge_based_node_container,
node_restriction_map,
m_barrier_nodes,
edge_geometries,
turn_lanes_data,
incoming_edge,
outgoing_edges,
merged_edge_ids);
auto intersection =
turn_analysis.AssignTurnTypes(node_along_road_entering,
incoming_edge,
intersection_with_flags_and_angles);
OSRM_ASSERT(intersection.valid(),
m_coordinates[node_at_center_of_intersection]);
auto intersection = turn_analysis.AssignTurnTypes(
incoming_edge.node, incoming_edge.edge, intersection_view);
OSRM_ASSERT(intersection.valid(), m_coordinates[intersection_node]);
intersection = turn_lane_handler.assignTurnLanes(
node_along_road_entering, incoming_edge, std::move(intersection));
incoming_edge.node, incoming_edge.edge, std::move(intersection));
// the entry class depends on the turn, so we have to classify the
// interesction for
// every edge
const auto turn_classification = classifyIntersection(
intersection, m_coordinates[node_at_center_of_intersection]);
// interesction for every edge
const auto turn_classification =
classifyIntersection(intersection, m_coordinates[intersection_node]);
const auto entry_class_id =
entry_class_hash.ConcurrentFindOrAdd(turn_classification.first);
@@ -732,19 +749,37 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
// Note - this is strictly speaking not thread safe, but we know we
// should never be touching the same element twice, so we should
// be fine.
bearing_class_by_node_based_node[node_at_center_of_intersection] =
bearing_class_id;
bearing_class_by_node_based_node[intersection_node] = bearing_class_id;
// check if we are turning off a via way
const auto turning_off_via_way = way_restriction_map.IsViaWay(
node_along_road_entering, node_at_center_of_intersection);
const auto turning_off_via_way =
way_restriction_map.IsViaWay(incoming_edge.node, intersection_node);
for (const auto &turn : intersection)
// Save reversed incoming bearing to compute turn angles
const auto reversed_incoming_bearing = util::bearing::reverse(
findEdgeBearing(edge_geometries, incoming_edge.edge));
for (const auto &outgoing_edge : outgoing_edges)
{
// only keep valid turns
if (!turn.entry_allowed)
if (!intersection::isTurnAllowed(m_node_based_graph,
m_edge_based_node_container,
node_restriction_map,
m_barrier_nodes,
edge_geometries,
turn_lanes_data,
incoming_edge,
outgoing_edge))
continue;
const auto turn =
std::find_if(intersection.begin(),
intersection.end(),
[edge = outgoing_edge.edge](const auto &road) {
return road.eid == edge;
});
OSRM_ASSERT(turn != intersection.end(),
m_coordinates[intersection_node]);
// In case a way restriction starts at a given location, add a turn onto
// every artificial node eminating here.
//
@@ -766,22 +801,22 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
// duplicated node associated with the turn. (e.g. ab via bc switches bc
// to bc_dup)
auto const target_id = way_restriction_map.RemapIfRestricted(
nbe_to_ebn_mapping[turn.eid],
node_along_road_entering,
node_at_center_of_intersection,
m_node_based_graph.GetTarget(turn.eid),
nbe_to_ebn_mapping[outgoing_edge.edge],
incoming_edge.node,
outgoing_edge.node,
m_node_based_graph.GetTarget(outgoing_edge.edge),
m_number_of_edge_based_nodes);
{ // scope to forget edge_with_data after
const auto edge_with_data_and_condition =
generate_edge(nbe_to_ebn_mapping[incoming_edge],
generate_edge(nbe_to_ebn_mapping[incoming_edge.edge],
target_id,
node_along_road_entering,
incoming_edge,
node_at_center_of_intersection,
turn.eid,
intersection,
turn,
incoming_edge.node,
incoming_edge.edge,
outgoing_edge.node,
outgoing_edge.edge,
reversed_incoming_bearing,
*turn,
entry_class_id);
buffer->continuous_data.edges_list.push_back(
@@ -808,7 +843,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
if (turning_off_via_way)
{
const auto duplicated_nodes = way_restriction_map.DuplicatedNodeIDs(
node_along_road_entering, node_at_center_of_intersection);
incoming_edge.node, intersection_node);
// next to the normal restrictions tracked in `entry_allowed`, via
// ways might introduce additional restrictions. These are handled
@@ -816,12 +851,12 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
for (auto duplicated_node_id : duplicated_nodes)
{
const auto from_id =
m_number_of_edge_based_nodes -
way_restriction_map.NumberOfDuplicatedNodes() +
duplicated_node_id;
NodeID(m_number_of_edge_based_nodes -
way_restriction_map.NumberOfDuplicatedNodes() +
duplicated_node_id);
auto const node_at_end_of_turn =
m_node_based_graph.GetTarget(turn.eid);
m_node_based_graph.GetTarget(outgoing_edge.edge);
const auto is_way_restricted = way_restriction_map.IsRestricted(
duplicated_node_id, node_at_end_of_turn);
@@ -836,14 +871,14 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
// add into delayed data
auto edge_with_data_and_condition =
generate_edge(NodeID(from_id),
nbe_to_ebn_mapping[turn.eid],
node_along_road_entering,
incoming_edge,
node_at_center_of_intersection,
turn.eid,
intersection,
turn,
generate_edge(from_id,
nbe_to_ebn_mapping[outgoing_edge.edge],
incoming_edge.node,
incoming_edge.edge,
outgoing_edge.node,
outgoing_edge.edge,
reversed_incoming_bearing,
*turn,
entry_class_id);
buffer->delayed_data.push_back(
@@ -860,24 +895,24 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
{
// add a new conditional for the edge we just created
buffer->conditionals.push_back(
{NodeID(from_id),
nbe_to_ebn_mapping[turn.eid],
{from_id,
nbe_to_ebn_mapping[outgoing_edge.edge],
{static_cast<std::uint64_t>(-1),
m_coordinates[node_at_center_of_intersection],
m_coordinates[intersection_node],
restriction.condition}});
}
}
else
{
auto edge_with_data_and_condition =
generate_edge(NodeID(from_id),
nbe_to_ebn_mapping[turn.eid],
node_along_road_entering,
incoming_edge,
node_at_center_of_intersection,
turn.eid,
intersection,
turn,
generate_edge(from_id,
nbe_to_ebn_mapping[outgoing_edge.edge],
incoming_edge.node,
incoming_edge.edge,
outgoing_edge.node,
outgoing_edge.edge,
reversed_incoming_bearing,
*turn,
entry_class_id);
buffer->delayed_data.push_back(
+3 -2
View File
@@ -365,7 +365,7 @@ Extractor::ParseOSMData(ScriptingEnvironment &scripting_environment,
TIMER_START(parsing);
{ // Parse OSM header
osmium::io::Reader reader(input_file, osmium::osm_entity_bits::nothing);
osmium::io::Reader reader(input_file, pool, osmium::osm_entity_bits::nothing);
osmium::io::Header header = reader.header();
std::string generator = header.get("generator");
@@ -545,7 +545,7 @@ Extractor::ParseOSMData(ScriptingEnvironment &scripting_environment,
{ // Relations reading pipeline
util::Log() << "Parse relations ...";
osmium::io::Reader reader(input_file, osmium::osm_entity_bits::relation, read_meta);
osmium::io::Reader reader(input_file, pool, osmium::osm_entity_bits::relation, read_meta);
tbb::parallel_pipeline(
num_threads, buffer_reader(reader) & buffer_relation_cache & buffer_storage_relation);
}
@@ -553,6 +553,7 @@ Extractor::ParseOSMData(ScriptingEnvironment &scripting_environment,
{ // Nodes and ways reading pipeline
util::Log() << "Parse ways and nodes ...";
osmium::io::Reader reader(input_file,
pool,
osmium::osm_entity_bits::node | osmium::osm_entity_bits::way |
osmium::osm_entity_bits::relation,
read_meta);
+14 -8
View File
@@ -12,18 +12,24 @@ namespace extractor
namespace guidance
{
DrivewayHandler::DrivewayHandler(const IntersectionGenerator &intersection_generator,
const util::NodeBasedDynamicGraph &node_based_graph,
DrivewayHandler::DrivewayHandler(const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &coordinates,
const std::vector<util::Coordinate> &node_coordinates,
const extractor::CompressedEdgeContainer &compressed_geometries,
const RestrictionMap &node_restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table)
: IntersectionHandler(node_based_graph,
node_data_container,
coordinates,
node_coordinates,
compressed_geometries,
node_restriction_map,
barrier_nodes,
turn_lanes_data,
name_table,
street_name_suffix_table,
intersection_generator)
street_name_suffix_table)
{
}
@@ -64,12 +70,12 @@ operator()(const NodeID nid, const EdgeID source_edge_id, Intersection intersect
});
(void)nid;
OSRM_ASSERT(road != intersection.end(), coordinates[nid]);
OSRM_ASSERT(road != intersection.end(), node_coordinates[nid]);
if (road->instruction == TurnInstruction::INVALID())
return intersection;
OSRM_ASSERT(road->instruction.type == TurnType::Turn, coordinates[nid]);
OSRM_ASSERT(road->instruction.type == TurnType::Turn, node_coordinates[nid]);
road->instruction.type =
isSameName(source_edge_id, road->eid) ? TurnType::NoTurn : TurnType::NewName;
@@ -0,0 +1,33 @@
#ifndef OSRM_EXTRACTOR_GUIDANCE_HAVE_IDENTICAL_NAMES_HPP_
#define OSRM_EXTRACTOR_GUIDANCE_HAVE_IDENTICAL_NAMES_HPP_
#include "util/guidance/name_announcements.hpp"
namespace osrm
{
namespace extractor
{
namespace guidance
{
// check if two name ids can be seen as identical (in presence of refs/others)
// in our case this translates into no name announcement in either direction (lhs->rhs and
// rhs->lhs)
bool HaveIdenticalNames(const NameID lhs,
const NameID rhs,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table)
{
const auto non_empty = (lhs != EMPTY_NAMEID) && (rhs != EMPTY_NAMEID);
// symmetrical check for announcements
return non_empty &&
!util::guidance::requiresNameAnnounced(lhs, rhs, name_table, street_name_suffix_table) &&
!util::guidance::requiresNameAnnounced(rhs, lhs, name_table, street_name_suffix_table);
}
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif /*OSRM_EXTRACTOR_GUIDANCE_HAVE_IDENTICAL_NAMES_HPP_*/
@@ -1,488 +0,0 @@
#include "extractor/guidance/intersection_generator.hpp"
#include "extractor/geojson_debug_policies.hpp"
#include "util/geojson_debug_logger.hpp"
#include "util/assert.hpp"
#include "util/bearing.hpp"
#include "util/coordinate_calculation.hpp"
#include "util/log.hpp"
#include <algorithm>
#include <cmath>
#include <functional> // mem_fn
#include <limits>
#include <numeric>
#include <utility>
#include <boost/range/algorithm/count_if.hpp>
namespace osrm
{
namespace extractor
{
namespace guidance
{
namespace
{
const constexpr bool USE_LOW_PRECISION_MODE = true;
// the inverse of use low precision mode
const constexpr bool USE_HIGH_PRECISION_MODE = !USE_LOW_PRECISION_MODE;
}
IntersectionGenerator::IntersectionGenerator(
const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const RestrictionMap &restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const std::vector<util::Coordinate> &coordinates,
const CompressedEdgeContainer &compressed_edge_container)
: node_based_graph(node_based_graph), node_data_container(node_data_container),
restriction_map(restriction_map), barrier_nodes(barrier_nodes), coordinates(coordinates),
coordinate_extractor(node_based_graph, compressed_edge_container, coordinates)
{
}
IntersectionView IntersectionGenerator::operator()(const NodeID from_node,
const EdgeID via_eid) const
{
return GetConnectedRoads(from_node, via_eid, USE_HIGH_PRECISION_MODE);
}
IntersectionShape
IntersectionGenerator::ComputeIntersectionShape(const NodeID node_at_center_of_intersection,
const boost::optional<NodeID> sorting_base,
const bool use_low_precision_angles) const
{
const auto intersection_degree = node_based_graph.GetOutDegree(node_at_center_of_intersection);
const util::Coordinate turn_coordinate = coordinates[node_at_center_of_intersection];
// compute bearings in a relatively small circle to prevent wrong roads order with true bearings
struct RoadWithInitialBearing
{
double bearing;
IntersectionShapeData road;
};
std::vector<RoadWithInitialBearing> initial_roads_ordering;
// reserve enough items (+ the possibly missing u-turn edge)
initial_roads_ordering.reserve(intersection_degree);
// number of lanes at the intersection changes how far we look down the road
const auto edge_range = node_based_graph.GetAdjacentEdgeRange(node_at_center_of_intersection);
const auto max_lanes_intersection =
std::accumulate(edge_range.begin(),
edge_range.end(),
std::uint8_t{0},
[this](const auto current_max, const auto current_eid) {
return std::max(current_max,
node_based_graph.GetEdgeData(current_eid)
.flags.road_classification.GetNumberOfLanes());
});
for (const EdgeID edge_connected_to_intersection :
node_based_graph.GetAdjacentEdgeRange(node_at_center_of_intersection))
{
BOOST_ASSERT(edge_connected_to_intersection != SPECIAL_EDGEID);
const NodeID to_node = node_based_graph.GetTarget(edge_connected_to_intersection);
double bearing = 0.;
auto coordinates = coordinate_extractor.GetCoordinatesAlongRoad(
node_at_center_of_intersection, edge_connected_to_intersection, !INVERT, to_node);
const auto close_coordinate =
coordinate_extractor.ExtractCoordinateAtLength(2. /*m*/, coordinates);
const auto initial_bearing =
util::coordinate_calculation::bearing(turn_coordinate, close_coordinate);
const auto segment_length = util::coordinate_calculation::getLength(
coordinates.begin(),
coordinates.end(),
util::coordinate_calculation::haversineDistance);
const auto extract_coordinate = [&](const NodeID from_node,
const EdgeID via_eid,
const bool traversed_in_reverse,
const NodeID to_node) {
return (use_low_precision_angles || intersection_degree <= 2)
? coordinate_extractor.GetCoordinateCloseToTurn(
from_node, via_eid, traversed_in_reverse, to_node)
: coordinate_extractor.ExtractRepresentativeCoordinate(
from_node,
via_eid,
traversed_in_reverse,
to_node,
max_lanes_intersection,
std::move(coordinates));
};
// we have to look down the road a bit to get the correct turn
const auto coordinate_along_edge_leaving = extract_coordinate(
node_at_center_of_intersection, edge_connected_to_intersection, !INVERT, to_node);
bearing =
util::coordinate_calculation::bearing(turn_coordinate, coordinate_along_edge_leaving);
// OSM data sometimes contains duplicated nodes with identical coordinates, or
// because of coordinate precision rounding, end up at the same coordinate.
// It's impossible to calculate a bearing between these, so we log a warning
// that the data should be checked.
// The bearing calculation should return 0 in these cases, which may not be correct,
// but is at least not random.
if (turn_coordinate == coordinate_along_edge_leaving)
{
util::Log(logDEBUG) << "Zero length segment at " << coordinate_along_edge_leaving
<< " could cause invalid intersection exit bearing.";
BOOST_ASSERT(std::abs(bearing) <= 0.1);
}
initial_roads_ordering.push_back(
{initial_bearing, {edge_connected_to_intersection, bearing, segment_length}});
}
if (!initial_roads_ordering.empty())
{
const auto base_initial_bearing = [&]() {
if (sorting_base)
{
const auto itr = std::find_if(initial_roads_ordering.begin(),
initial_roads_ordering.end(),
[&](const auto &data) {
return node_based_graph.GetTarget(
data.road.eid) == *sorting_base;
});
if (itr != initial_roads_ordering.end())
return util::bearing::reverse(itr->bearing);
}
return util::bearing::reverse(initial_roads_ordering.begin()->bearing);
}();
// sort roads with respect to the initial bearings, a tie-breaker for equal initial bearings
// is to order roads via final bearings to have roads in clockwise order
//
// rhs <---. lhs <----.
// / /
// lhs / rhs /
//
// lhs road is before rhs one rhs road is before lhs one
// bearing::angleBetween < 180 bearing::angleBetween > 180
const auto initial_bearing_order = makeCompareShapeDataAngleToBearing(base_initial_bearing);
std::sort(initial_roads_ordering.begin(),
initial_roads_ordering.end(),
[&initial_bearing_order](const auto &lhs, const auto &rhs) {
return initial_bearing_order(lhs, rhs) ||
(lhs.bearing == rhs.bearing &&
util::bearing::angleBetween(lhs.road.bearing, rhs.road.bearing) <
180);
});
// copy intersection data in the initial order
IntersectionShape intersection;
intersection.reserve(initial_roads_ordering.size());
std::transform(initial_roads_ordering.begin(),
initial_roads_ordering.end(),
std::back_inserter(intersection),
[](const auto &entry) { return entry.road; });
if (intersection.size() > 2)
{ // Check bearings ordering with respect to true bearings
const auto base_bearing = intersection.front().bearing;
const auto bearings_order =
makeCompareShapeDataAngleToBearing(util::bearing::reverse(base_bearing));
for (auto curr = intersection.begin(), next = std::next(curr);
next != intersection.end();
++curr, ++next)
{
if (bearings_order(*next, *curr))
{ // If the true bearing is out of the initial order (next before current) then
// adjust the next bearing to keep the order. The adjustment angle is at most
// 0.5° or a half-angle between the current bearing and the base bearing.
// to prevent overlapping over base bearing + 360°.
const auto angle_adjustment = std::min(
.5, util::restrictAngleToValidRange(base_bearing - curr->bearing) / 2.);
next->bearing =
util::restrictAngleToValidRange(curr->bearing + angle_adjustment);
}
}
}
return intersection;
}
return IntersectionShape{};
}
// a
// |
// |
// v
// For an intersection from_node --via_eid--> turn_node ----> c
// ^
// |
// |
// b
// This functions returns _all_ turns as if the graph was undirected.
// That means we not only get (from_node, turn_node, c) in the above example
// but also (from_node, turn_node, a), (from_node, turn_node, b). These turns are
// marked as invalid and only needed for intersection classification.
IntersectionView IntersectionGenerator::GetConnectedRoads(const NodeID from_node,
const EdgeID via_eid,
const bool use_low_precision_angles) const
{
// make sure the via-eid is valid
BOOST_ASSERT([this](const NodeID from_node, const EdgeID via_eid) {
const auto range = node_based_graph.GetAdjacentEdgeRange(from_node);
return range.front() <= via_eid && via_eid <= range.back();
}(from_node, via_eid));
auto intersection = ComputeIntersectionShape(
node_based_graph.GetTarget(via_eid), boost::none, use_low_precision_angles);
return TransformIntersectionShapeIntoView(from_node, via_eid, std::move(intersection));
}
IntersectionGenerationParameters
IntersectionGenerator::SkipDegreeTwoNodes(const NodeID starting_node, const EdgeID via_edge) const
{
NodeID query_node = starting_node;
EdgeID query_edge = via_edge;
const auto get_next_edge = [this](const NodeID from, const EdgeID via) {
const NodeID new_node = node_based_graph.GetTarget(via);
BOOST_ASSERT(node_based_graph.GetOutDegree(new_node) == 2);
const EdgeID begin_edges_new_node = node_based_graph.BeginEdges(new_node);
return (node_based_graph.GetTarget(begin_edges_new_node) == from) ? begin_edges_new_node + 1
: begin_edges_new_node;
};
std::unordered_set<NodeID> visited_nodes;
// skip trivial nodes without generating the intersection in between, stop at the very first
// intersection of degree > 2
while (0 == visited_nodes.count(query_node) &&
2 == node_based_graph.GetOutDegree(node_based_graph.GetTarget(query_edge)))
{
visited_nodes.insert(query_node);
const auto next_node = node_based_graph.GetTarget(query_edge);
const auto next_edge = get_next_edge(query_node, query_edge);
query_node = next_node;
query_edge = next_edge;
// check if there is a relevant change in the graph
if (!CanBeCompressed(node_based_graph.GetEdgeData(query_edge),
node_based_graph.GetEdgeData(next_edge),
node_data_container) ||
(node_based_graph.GetTarget(next_edge) == starting_node))
break;
}
return {query_node, query_edge};
}
IntersectionView IntersectionGenerator::TransformIntersectionShapeIntoView(
const NodeID previous_node,
const EdgeID entering_via_edge,
const IntersectionShape &intersection_shape) const
{
// requires a copy of the intersection
return TransformIntersectionShapeIntoView(previous_node,
entering_via_edge,
intersection_shape, // creates a copy
intersection_shape, // reference to local
{}); // empty vector of performed merges
}
IntersectionView IntersectionGenerator::TransformIntersectionShapeIntoView(
const NodeID previous_node,
const EdgeID entering_via_edge,
const IntersectionShape &normalized_intersection,
const IntersectionShape &intersection,
const std::vector<IntersectionNormalizationOperation> &performed_merges) const
{
const auto node_at_intersection = node_based_graph.GetTarget(entering_via_edge);
// request all turn restrictions
auto const restrictions = restriction_map.Restrictions(previous_node, node_at_intersection);
// check turn restrictions to find a node that is the only allowed target when coming from a
// node to an intersection
// d
// |
// a - b - c and `only_straight_on ab | bc would return `c` for `a,b`
const auto find_only_valid_turn = [&]() -> boost::optional<NodeID> {
const auto itr = std::find_if(restrictions.first, restrictions.second, [](auto pair) {
return pair.second->is_only;
});
if (itr != restrictions.second)
return itr->second->AsNodeRestriction().to;
else
return boost::none;
};
const auto only_valid_turn = find_only_valid_turn();
// barriers change our behaviour regarding u-turns
const bool is_barrier_node = barrier_nodes.find(node_at_intersection) != barrier_nodes.end();
const auto connect_to_previous_node = [this, previous_node](const IntersectionShapeData road) {
return node_based_graph.GetTarget(road.eid) == previous_node;
};
// check which of the edges is the u-turn edge
const auto uturn_edge_itr =
std::find_if(intersection.begin(), intersection.end(), connect_to_previous_node);
// there needs to be a connection, otherwise stuff went seriously wrong. Note that this is not
// necessarily the same id as `entering_via_edge`.
// In cases where parallel edges are present, we only remember the minimal edge. Both share
// exactly the same coordinates, so the u-turn is still the best choice here.
BOOST_ASSERT(uturn_edge_itr != intersection.end());
const auto is_restricted = [&](const NodeID destination) {
// check if we have a dedicated destination
if (only_valid_turn)
return *only_valid_turn != destination;
// check if explicitly forbidden
return restrictions.second !=
std::find_if(restrictions.first, restrictions.second, [&](const auto &restriction) {
return restriction.second->AsNodeRestriction().to == destination;
});
};
const auto is_allowed_turn = [&](const IntersectionShapeData &road) {
const auto &road_data = node_based_graph.GetEdgeData(road.eid);
const NodeID road_destination_node = node_based_graph.GetTarget(road.eid);
// reverse edges are never valid turns because the resulting turn would look like this:
// from_node --via_edge--> node_at_intersection <--onto_edge-- to_node
// however we need this for capture intersection shape for incoming one-ways
return !road_data.reversed &&
// we are not turning over a barrier
(!is_barrier_node || road_destination_node == previous_node) &&
// don't allow restricted turns
!is_restricted(road_destination_node);
};
// due to merging of roads, the u-turn might actually not be part of the intersection anymore
// uturn is a pair of {edge id, bearing}
const auto uturn = [&]() {
const auto merge_entry = std::find_if(
performed_merges.begin(), performed_merges.end(), [&uturn_edge_itr](const auto entry) {
return entry.merged_eid == uturn_edge_itr->eid;
});
if (merge_entry != performed_merges.end())
{
const auto merged_into_id = merge_entry->into_eid;
const auto merged_u_turn = std::find_if(
normalized_intersection.begin(),
normalized_intersection.end(),
[&](const IntersectionShapeData &road) { return road.eid == merged_into_id; });
BOOST_ASSERT(merged_u_turn != normalized_intersection.end());
return std::make_pair(merged_u_turn->eid,
util::bearing::reverse(merged_u_turn->bearing));
}
else
{
const auto uturn_edge_at_normalized_intersection_itr =
std::find_if(normalized_intersection.begin(),
normalized_intersection.end(),
connect_to_previous_node);
BOOST_ASSERT(uturn_edge_at_normalized_intersection_itr !=
normalized_intersection.end());
return std::make_pair(
uturn_edge_at_normalized_intersection_itr->eid,
util::bearing::reverse(uturn_edge_at_normalized_intersection_itr->bearing));
}
}();
IntersectionView intersection_view;
intersection_view.reserve(normalized_intersection.size());
std::transform(normalized_intersection.begin(),
normalized_intersection.end(),
std::back_inserter(intersection_view),
[&](const IntersectionShapeData &road) {
return IntersectionViewData(
road,
is_allowed_turn(road),
util::bearing::angleBetween(uturn.second, road.bearing));
});
const auto uturn_edge_at_intersection_view_itr =
std::find_if(intersection_view.begin(), intersection_view.end(), connect_to_previous_node);
// number of found valid exit roads
const auto valid_count =
std::count_if(intersection_view.begin(),
intersection_view.end(),
[](const IntersectionViewData &road) { return road.entry_allowed; });
// in general, we don't wan't to allow u-turns. If we don't look at a barrier, we have to check
// for dead end streets. These are the only ones that we allow uturns for, next to barriers
// (which are also kind of a dead end, but we don't have to check these again :))
if (uturn_edge_at_intersection_view_itr != intersection_view.end() &&
((uturn_edge_at_intersection_view_itr->entry_allowed && !is_barrier_node &&
valid_count != 1) ||
valid_count == 0))
{
const auto allow_uturn_at_dead_end = [&]() {
const auto &uturn_data = node_based_graph.GetEdgeData(uturn_edge_itr->eid);
// we can't turn back onto oneway streets
if (uturn_data.reversed)
return false;
// don't allow explicitly restricted turns
if (is_restricted(previous_node))
return false;
// we define dead ends as roads that can only be entered via the possible u-turn
const auto is_bidirectional = [&](const EdgeID entering_via_edge) {
const auto to_node = node_based_graph.GetTarget(entering_via_edge);
const auto reverse_edge = node_based_graph.FindEdge(to_node, node_at_intersection);
BOOST_ASSERT(reverse_edge != SPECIAL_EDGEID);
return !node_based_graph.GetEdgeData(reverse_edge).reversed;
};
const auto bidirectional_edges = [&]() {
std::uint32_t count = 0;
for (const auto eid : node_based_graph.GetAdjacentEdgeRange(node_at_intersection))
if (is_bidirectional(eid))
++count;
return count;
}();
// Checking for dead-end streets is kind of difficult. There is obvious dead ends
// (single road connected)
return bidirectional_edges <= 1;
}();
uturn_edge_at_intersection_view_itr->entry_allowed = allow_uturn_at_dead_end;
}
std::sort(std::begin(intersection_view),
std::end(intersection_view),
std::mem_fn(&IntersectionViewData::CompareByAngle));
// Move entering_via_edge to intersection front and place all roads prior entering_via_edge
// at the end of the intersection view with 360° angle
auto entering_via_it = std::find_if(intersection_view.begin(),
intersection_view.end(),
[&uturn](auto &road) { return road.eid == uturn.first; });
OSRM_ASSERT(entering_via_it != intersection_view.end() && entering_via_it->angle >= 0. &&
entering_via_it->angle < std::numeric_limits<double>::epsilon(),
coordinates[node_at_intersection]);
if (entering_via_it != intersection_view.begin() && entering_via_it != intersection_view.end())
{
std::for_each(
intersection_view.begin(), entering_via_it, [](auto &road) { road.angle = 360.; });
std::rotate(intersection_view.begin(), entering_via_it, intersection_view.end());
}
return intersection_view;
}
const CoordinateExtractor &IntersectionGenerator::GetCoordinateExtractor() const
{
return coordinate_extractor;
}
} // namespace guidance
} // namespace extractor
} // namespace osrm
+52 -58
View File
@@ -1,5 +1,6 @@
#include "extractor/guidance/intersection_handler.hpp"
#include "extractor/guidance/constants.hpp"
#include "extractor/intersection/intersection_analysis.hpp"
#include "util/coordinate_calculation.hpp"
#include "util/guidance/name_announcements.hpp"
@@ -45,17 +46,27 @@ inline bool requiresAnnouncement(const util::NodeBasedDynamicGraph &node_based_g
}
} // namespace detail
IntersectionHandler::IntersectionHandler(const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &coordinates,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table,
const IntersectionGenerator &intersection_generator)
IntersectionHandler::IntersectionHandler(
const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &node_coordinates,
const extractor::CompressedEdgeContainer &compressed_geometries,
const RestrictionMap &node_restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table)
: node_based_graph(node_based_graph), node_data_container(node_data_container),
coordinates(coordinates), name_table(name_table),
street_name_suffix_table(street_name_suffix_table),
intersection_generator(intersection_generator),
graph_walker(node_based_graph, node_data_container, intersection_generator)
node_coordinates(node_coordinates), compressed_geometries(compressed_geometries),
node_restriction_map(node_restriction_map), barrier_nodes(barrier_nodes),
turn_lanes_data(turn_lanes_data), name_table(name_table),
street_name_suffix_table(street_name_suffix_table), graph_walker(node_based_graph,
node_data_container,
node_coordinates,
compressed_geometries,
node_restriction_map,
barrier_nodes,
turn_lanes_data)
{
}
@@ -91,6 +102,19 @@ TurnType::Enum IntersectionHandler::findBasicTurnType(const EdgeID via_edge,
return TurnType::Turn;
}
TurnType::Enum IntersectionHandler::areSameClasses(const EdgeID via_edge,
const ConnectedRoad &road) const
{
const auto &in_classes =
node_data_container.GetAnnotation(node_based_graph.GetEdgeData(via_edge).annotation_data)
.classes;
const auto &out_classes =
node_data_container.GetAnnotation(node_based_graph.GetEdgeData(road.eid).annotation_data)
.classes;
return in_classes == out_classes;
}
TurnInstruction IntersectionHandler::getInstructionForObvious(const std::size_t num_roads,
const EdgeID via_edge,
const bool through_street,
@@ -162,8 +186,8 @@ TurnInstruction IntersectionHandler::getInstructionForObvious(const std::size_t
// or actually follow the full road. When 2399 lands, we can exchange here for a
// precalculated distance value.
const auto distance = util::coordinate_calculation::haversineDistance(
coordinates[node_based_graph.GetTarget(via_edge)],
coordinates[node_based_graph.GetTarget(road.eid)]);
node_coordinates[node_based_graph.GetTarget(via_edge)],
node_coordinates[node_based_graph.GetTarget(road.eid)]);
return {TurnType::Turn,
(angularDeviation(road.angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE &&
@@ -184,7 +208,8 @@ TurnInstruction IntersectionHandler::getInstructionForObvious(const std::size_t
if (needs_notification)
return {TurnType::Notification, getTurnDirection(road.angle)};
else
return {num_roads == 2 ? TurnType::NoTurn : TurnType::Suppressed,
return {num_roads == 2 && areSameClasses(via_edge, road) ? TurnType::NoTurn
: TurnType::Suppressed,
getTurnDirection(road.angle)};
}
}
@@ -193,7 +218,7 @@ TurnInstruction IntersectionHandler::getInstructionForObvious(const std::size_t
{
return {TurnType::Notification, getTurnDirection(road.angle)};
}
if (num_roads > 2)
if (num_roads > 2 || !areSameClasses(via_edge, road))
{
return {TurnType::Suppressed, getTurnDirection(road.angle)};
}
@@ -407,44 +432,6 @@ void IntersectionHandler::assignTrivialTurns(const EdgeID via_eid,
}
}
bool IntersectionHandler::isThroughStreet(const std::size_t index,
const Intersection &intersection) const
{
const auto &data_at_index = node_data_container.GetAnnotation(
node_based_graph.GetEdgeData(intersection[index].eid).annotation_data);
if (data_at_index.name_id == EMPTY_NAMEID)
return false;
// a through street cannot start at our own position -> index 1
for (std::size_t road_index = 1; road_index < intersection.size(); ++road_index)
{
if (road_index == index)
continue;
const auto &road = intersection[road_index];
const auto &road_data = node_data_container.GetAnnotation(
node_based_graph.GetEdgeData(road.eid).annotation_data);
// roads have a near straight angle (180 degree)
const bool is_nearly_straight = angularDeviation(road.angle, intersection[index].angle) >
(STRAIGHT_ANGLE - FUZZY_ANGLE_DIFFERENCE);
const bool have_same_name =
road_data.name_id != EMPTY_NAMEID &&
!util::guidance::requiresNameAnnounced(
data_at_index.name_id, road_data.name_id, name_table, street_name_suffix_table);
const bool have_same_category =
node_based_graph.GetEdgeData(intersection[index].eid).flags.road_classification ==
node_based_graph.GetEdgeData(road.eid).flags.road_classification;
if (is_nearly_straight && have_same_name && have_same_category)
return true;
}
return false;
}
boost::optional<IntersectionHandler::IntersectionViewAndNode>
IntersectionHandler::getNextIntersection(const NodeID at, const EdgeID via) const
{
@@ -462,17 +449,24 @@ IntersectionHandler::getNextIntersection(const NodeID at, const EdgeID via) cons
// Starting at node `a` via edge `e0` the intersection generator returns the intersection at `c`
// writing `tl` (traffic signal) node and the edge `e1` which has the intersection as target.
const auto intersection_parameters = intersection_generator.SkipDegreeTwoNodes(at, via);
const auto intersection_parameters =
intersection::skipDegreeTwoNodes(node_based_graph, {at, via});
// This should never happen, guard against nevertheless
if (intersection_parameters.nid == SPECIAL_NODEID ||
intersection_parameters.via_eid == SPECIAL_EDGEID)
if (intersection_parameters.node == SPECIAL_NODEID ||
intersection_parameters.edge == SPECIAL_EDGEID)
{
return boost::none;
}
auto intersection =
intersection_generator(intersection_parameters.nid, intersection_parameters.via_eid);
auto intersection_node = node_based_graph.GetTarget(intersection_parameters.via_eid);
auto intersection = intersection::getConnectedRoads<false>(node_based_graph,
node_data_container,
node_coordinates,
compressed_geometries,
node_restriction_map,
barrier_nodes,
turn_lanes_data,
intersection_parameters);
auto intersection_node = node_based_graph.GetTarget(intersection_parameters.edge);
if (intersection.size() <= 2 || intersection.isTrafficSignalOrBarrier())
{
@@ -1,430 +0,0 @@
#include "extractor/guidance/intersection_normalizer.hpp"
#include "util/bearing.hpp"
#include "util/coordinate_calculation.hpp"
#include <tuple>
#include <utility>
using osrm::util::angularDeviation;
namespace osrm
{
namespace extractor
{
namespace guidance
{
IntersectionNormalizer::IntersectionNormalizer(
const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &coordinates,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table,
const IntersectionGenerator &intersection_generator)
: node_based_graph(node_based_graph), intersection_generator(intersection_generator),
mergable_road_detector(node_based_graph,
node_data_container,
coordinates,
intersection_generator,
intersection_generator.GetCoordinateExtractor(),
name_table,
street_name_suffix_table)
{
}
IntersectionNormalizer::NormalizationResult IntersectionNormalizer::
operator()(const NodeID node_at_intersection, IntersectionShape intersection) const
{
const auto intersection_copy = intersection;
auto merged_shape_and_merges =
MergeSegregatedRoads(node_at_intersection, std::move(intersection));
merged_shape_and_merges.normalized_shape = AdjustBearingsForMergeAtDestination(
node_at_intersection, std::move(merged_shape_and_merges.normalized_shape));
return merged_shape_and_merges;
}
bool IntersectionNormalizer::CanMerge(const NodeID intersection_node,
const IntersectionShape &intersection,
std::size_t fist_index_in_ccw,
std::size_t second_index_in_ccw) const
{
BOOST_ASSERT(((fist_index_in_ccw + 1) % intersection.size()) == second_index_in_ccw);
// don't merge on degree two, since it's most likely a bollard/traffic light or a round way
if (intersection.size() <= 2)
return false;
const auto can_merge = mergable_road_detector.CanMergeRoad(
intersection_node, intersection[fist_index_in_ccw], intersection[second_index_in_ccw]);
/*
* Merging should never depend on order/never merge more than two roads. To ensure that we don't
* merge anything that is impacted by neighboring roads (e.g. three roads of the same name as in
* parking lots/border checkpoints), we check if the neigboring roads would be merged as well.
* In that case, we cannot merge, since we would end up merging multiple items together
*/
const auto is_distinct = [&]() {
const auto next_index_in_ccw = (second_index_in_ccw + 1) % intersection.size();
const auto distinct_to_next_in_ccw = mergable_road_detector.IsDistinctFrom(
intersection[second_index_in_ccw], intersection[next_index_in_ccw]);
const auto prev_index_in_ccw =
(fist_index_in_ccw + intersection.size() - 1) % intersection.size();
const auto distinct_to_prev_in_ccw = mergable_road_detector.IsDistinctFrom(
intersection[prev_index_in_ccw], intersection[fist_index_in_ccw]);
return distinct_to_next_in_ccw && distinct_to_prev_in_ccw;
};
// use lazy evaluation to check only if mergable
return can_merge && is_distinct();
}
IntersectionNormalizationOperation
IntersectionNormalizer::DetermineMergeDirection(const IntersectionShapeData &lhs,
const IntersectionShapeData &rhs) const
{
if (node_based_graph.GetEdgeData(lhs.eid).reversed)
return {lhs.eid, rhs.eid};
else
return {rhs.eid, lhs.eid};
}
IntersectionShapeData IntersectionNormalizer::MergeRoads(const IntersectionShapeData &into,
const IntersectionShapeData &from) const
{
// we only merge small angles. If the difference between both is large, we are looking at a
// bearing leading north. Such a bearing cannot be handled via the basic average. In this
// case we actually need to shift the bearing by half the difference.
const auto aroundZero = [](const double first, const double second) {
return (std::max(first, second) - std::min(first, second)) >= 180;
};
// find the angle between two other angles
const auto combineAngles = [aroundZero](const double first, const double second) {
if (!aroundZero(first, second))
return .5 * (first + second);
else
{
const auto offset = angularDeviation(first, second);
auto new_angle = std::max(first, second) + .5 * offset;
if (new_angle >= 360)
return new_angle - 360;
return new_angle;
}
};
auto result = into;
BOOST_ASSERT(!node_based_graph.GetEdgeData(into.eid).reversed);
result.bearing = combineAngles(into.bearing, from.bearing);
BOOST_ASSERT(0 <= result.bearing && result.bearing < 360.0);
return result;
}
IntersectionShapeData
IntersectionNormalizer::MergeRoads(const IntersectionNormalizationOperation direction,
const IntersectionShapeData &lhs,
const IntersectionShapeData &rhs,
const double opposite_bearing) const
{
// In some intersections, turning roads can introduce artificial turns if we merge here.
// Consider a scenario like:
// 
// a . g - f
// | .
// | .
// |.
// d-b--------e
// |
// c
// 
// Merging `bgf` and `be` would introduce an angle, even though d-b-e is perfectly straight
// We don't change the angle, if such an opposite road exists
if (direction.merged_eid == lhs.eid)
{
// change the angle only if the opposite direction is not nearly straight
if (angularDeviation(opposite_bearing, rhs.bearing) >
(STRAIGHT_ANGLE - MAXIMAL_ALLOWED_NO_TURN_DEVIATION))
return rhs;
else
return MergeRoads(rhs, lhs);
}
else
{
if (angularDeviation(opposite_bearing, lhs.bearing) >
(STRAIGHT_ANGLE - MAXIMAL_ALLOWED_NO_TURN_DEVIATION))
return lhs;
else
return MergeRoads(lhs, rhs);
}
}
/*
* Segregated Roads often merge onto a single intersection.
* While technically representing different roads, they are
* often looked at as a single road.
* Due to the merging, turn Angles seem off, wenn we compute them from the
* initial positions.
*
* b<b<b<b(1)<b<b<b
* aaaaa-b
* b>b>b>b(2)>b>b>b
*
* Would be seen as a slight turn going fro a to (2). A Sharp turn going from
* (1) to (2).
*
* In cases like these, we megre this segregated roads into a single road to
* end up with a case like:
*
* aaaaa-bbbbbb
*
* for the turn representation.
* Anything containing the first u-turn in a merge affects all other angles
* and is handled separately from all others.
*/
IntersectionNormalizer::NormalizationResult
IntersectionNormalizer::MergeSegregatedRoads(const NodeID intersection_node,
IntersectionShape intersection) const
{
const auto getRight = [&](std::size_t index) {
return (index + intersection.size() - 1) % intersection.size();
};
// This map stores for all edges that participated in a merging operation in which edge id they
// end up in the end. We only store what we have merged into other edges.
std::vector<IntersectionNormalizationOperation> merging_map;
const auto merge = [this, &merging_map](const IntersectionShapeData &first,
const IntersectionShapeData &second,
const double opposite_bearing) {
const auto direction = DetermineMergeDirection(first, second);
BOOST_ASSERT(
std::find_if(merging_map.begin(), merging_map.end(), [direction](const auto pair) {
return pair.merged_eid == direction.merged_eid;
}) == merging_map.end());
merging_map.push_back(direction);
return MergeRoads(direction, first, second, opposite_bearing);
};
if (intersection.size() <= 1)
return {intersection, merging_map};
const auto intersection_copy = intersection;
const auto opposite_bearing = [this, intersection_copy](const IntersectionShapeData &lhs,
const IntersectionShapeData &rhs) {
if (node_based_graph.GetEdgeData(lhs.eid).reversed)
{
return intersection_copy.FindClosestBearing(util::bearing::reverse(rhs.bearing))
->bearing;
}
else
{
BOOST_ASSERT(node_based_graph.GetEdgeData(rhs.eid).reversed);
return intersection_copy.FindClosestBearing(util::bearing::reverse(lhs.bearing))
->bearing;
}
};
// check for merges including the basic u-turn
// these result in an adjustment of all other angles. This is due to how these angles are
// perceived. Considering the following example:
//
// c b
// Y
// a
//
// coming from a to b (given a road that splits at the fork into two one-ways), the turn is not
// considered as a turn but rather as going straight.
// Now if we look at the situation merging:
//
// a b
// \ /
// e - + - d
// |
// c
//
// With a,b representing the same road, the intersection itself represents a classif for way
// intersection so we handle it like
//
// (a),b
// |
// e - + - d
// |
// c
//
// To be able to consider this adjusted representation down the line, we merge some roads.
// If the merge occurs at the u-turn edge, we need to adjust all angles, though, since they are
// with respect to the now changed perceived location of a. If we move (a) to the left, we add
// the difference to all angles. Otherwise we subtract it.
// these result in an adjustment of all other angles
if (CanMerge(intersection_node, intersection, intersection.size() - 1, 0))
{
// moving `a` to the left
const auto opposite = opposite_bearing(intersection.front(), intersection.back());
intersection[0] = merge(intersection.front(), intersection.back(), opposite);
// FIXME if we have a left-sided country, we need to switch this off and enable it
// below
intersection.pop_back();
}
else if (CanMerge(intersection_node, intersection, 0, 1))
{
const auto opposite = opposite_bearing(intersection.front(), intersection[1]);
intersection[0] = merge(intersection.front(), intersection[1], opposite);
intersection.erase(intersection.begin() + 1);
}
// a merge including the first u-turn requires an adjustment of the turn angles
// therefore these are handled prior to this step
for (std::size_t index = 2; index < intersection.size(); ++index)
{
if (CanMerge(intersection_node, intersection, getRight(index), index))
{
const auto opposite =
opposite_bearing(intersection[getRight(index)], intersection[index]);
intersection[getRight(index)] =
merge(intersection[getRight(index)], intersection[index], opposite);
intersection.erase(intersection.begin() + index);
--index;
}
}
return {intersection, merging_map};
}
// OSM can have some very steep angles for joining roads. Considering the following intersection:
// x
// |
// v __________c
// /
// a ---d
// \ __________b
//
// with c->d as a oneway
// and d->b as a oneway, the turn von x->d is actually a turn from x->a. So when looking at the
// intersection coming from x, we want to interpret the situation as
// x
// |
// a __ d __ v__________c
// |
// |_______________b
//
// Where we see the turn to `d` as a right turn, rather than going straight.
// We do this by adjusting the local turn angle at `x` to turn onto `d` to be reflective of this
// situation, where `v` would be the node at the intersection.
IntersectionShape
IntersectionNormalizer::AdjustBearingsForMergeAtDestination(const NodeID node_at_intersection,
IntersectionShape intersection) const
{
// nothing to do for dead ends
if (intersection.size() <= 1)
return intersection;
// we don't adjust any road that is longer than 30 meters (between centers of intersections),
// since the road is probably too long otherwise to impact perception.
const double constexpr PRUNING_DISTANCE = 30;
// never adjust u-turns
for (std::size_t index = 0; index < intersection.size(); ++index)
{
auto &road = intersection[index];
// only consider roads that are close
if (road.segment_length > PRUNING_DISTANCE)
continue;
// to find out about the above situation, we need to look at the next intersection (at d in
// the example). If the initial road can be merged to the left/right, we are about to adjust
// the angle.
const auto next_intersection_along_road = intersection_generator.ComputeIntersectionShape(
node_based_graph.GetTarget(road.eid), node_at_intersection);
if (next_intersection_along_road.size() <= 1)
continue;
const auto node_at_next_intersection = node_based_graph.GetTarget(road.eid);
const auto adjustAngle = [](double angle, double offset) {
angle += offset;
if (angle > 360)
return angle - 360.;
else if (angle < 0)
return angle + 360.;
return angle;
};
const auto range = node_based_graph.GetAdjacentEdgeRange(node_at_next_intersection);
if (range.size() <= 1)
continue;
// the order does not matter
const auto get_offset = [](const IntersectionShapeData &lhs,
const IntersectionShapeData &rhs) {
return 0.5 * angularDeviation(lhs.bearing, rhs.bearing);
};
// When offsetting angles in our turns, we don't want to get past the next turn. This
// function simply limits an offset to be at most half the distance to the next turn in the
// offfset direction
const auto get_corrected_offset = [](
const double offset,
const IntersectionShapeData &road,
const IntersectionShapeData &next_road_in_offset_direction) {
const auto offset_limit =
angularDeviation(road.bearing, next_road_in_offset_direction.bearing);
// limit the offset with an additional buffer
return (offset + MAXIMAL_ALLOWED_NO_TURN_DEVIATION > offset_limit) ? 0.5 * offset_limit
: offset;
};
// only if straighmost angles get smaller, we consider it an improvement
auto const improves_straightmost = [&](auto const index, auto const offset) {
const auto itr = next_intersection_along_road.FindClosestBearing(
util::bearing::reverse(next_intersection_along_road[index].bearing));
const auto angle = util::bearing::angleBetween(
util::bearing::reverse(itr->bearing), next_intersection_along_road[index].bearing);
return util::angularDeviation(angle, STRAIGHT_ANGLE) >
util::angularDeviation(angle + offset, STRAIGHT_ANGLE);
};
// check if the u-turn edge at the next intersection could be merged to the left/right. If
// this is the case and the road is not far away (see previous distance check), if
// influences the perceived angle.
if (CanMerge(node_at_next_intersection, next_intersection_along_road, 0, 1))
{
const auto offset =
get_offset(next_intersection_along_road[0], next_intersection_along_road[1]);
if (improves_straightmost(0, -offset) && improves_straightmost(1, offset))
{
const auto corrected_offset = get_corrected_offset(
offset,
road,
intersection[(intersection.size() + index - 1) % intersection.size()]);
// at the target intersection, we merge to the right, so we need to shift the
// current
// angle to the left
road.bearing = adjustAngle(road.bearing, corrected_offset);
}
}
else if (CanMerge(node_at_next_intersection,
next_intersection_along_road,
next_intersection_along_road.size() - 1,
0))
{
const auto offset =
get_offset(next_intersection_along_road[0],
next_intersection_along_road[next_intersection_along_road.size() - 1]);
if (improves_straightmost(0, offset) &&
improves_straightmost(next_intersection_along_road.size() - 1, -offset))
{
const auto corrected_offset = get_corrected_offset(
offset, road, intersection[(index + 1) % intersection.size()]);
// at the target intersection, we merge to the left, so we need to shift the current
// angle to the right
road.bearing = adjustAngle(road.bearing, -corrected_offset);
}
}
}
return intersection;
}
} // namespace guidance
} // namespace extractor
} // namespace osrm
@@ -1,8 +1,7 @@
#include "extractor/guidance/mergable_road_detector.hpp"
#include "extractor/guidance/constants.hpp"
#include "extractor/guidance/coordinate_extractor.hpp"
#include "extractor/guidance/intersection_generator.hpp"
#include "extractor/guidance/node_based_graph_walker.hpp"
#include "extractor/intersection/intersection_analysis.hpp"
#include "extractor/query_node.hpp"
#include "extractor/suffix_table.hpp"
@@ -52,17 +51,22 @@ inline auto makeCheckRoadForName(const NameID name_id,
}
}
MergableRoadDetector::MergableRoadDetector(const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &node_coordinates,
const IntersectionGenerator &intersection_generator,
const CoordinateExtractor &coordinate_extractor,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table)
MergableRoadDetector::MergableRoadDetector(
const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &node_coordinates,
const extractor::CompressedEdgeContainer &compressed_geometries,
const RestrictionMap &node_restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table)
: node_based_graph(node_based_graph), node_data_container(node_data_container),
node_coordinates(node_coordinates), intersection_generator(intersection_generator),
coordinate_extractor(coordinate_extractor), name_table(name_table),
street_name_suffix_table(street_name_suffix_table)
node_coordinates(node_coordinates), compressed_geometries(compressed_geometries),
node_restriction_map(node_restriction_map), barrier_nodes(barrier_nodes),
turn_lanes_data(turn_lanes_data), name_table(name_table),
street_name_suffix_table(street_name_suffix_table),
coordinate_extractor(node_based_graph, compressed_geometries, node_coordinates)
{
}
@@ -118,16 +122,6 @@ bool MergableRoadDetector::CanMergeRoad(const NodeID intersection_node,
!IsCircularShape(intersection_node, lhs, rhs);
}
bool MergableRoadDetector::HaveIdenticalNames(const NameID lhs, const NameID rhs) const
{
const auto non_empty = (lhs != EMPTY_NAMEID) && (rhs != EMPTY_NAMEID);
// symmetrical check for announcements
return non_empty &&
!util::guidance::requiresNameAnnounced(lhs, rhs, name_table, street_name_suffix_table) &&
!util::guidance::requiresNameAnnounced(rhs, lhs, name_table, street_name_suffix_table);
}
bool MergableRoadDetector::IsDistinctFrom(const MergableRoadData &lhs,
const MergableRoadData &rhs) const
{
@@ -139,7 +133,9 @@ bool MergableRoadDetector::IsDistinctFrom(const MergableRoadData &lhs,
node_data_container.GetAnnotation(node_based_graph.GetEdgeData(lhs.eid).annotation_data)
.name_id,
node_data_container.GetAnnotation(node_based_graph.GetEdgeData(rhs.eid).annotation_data)
.name_id);
.name_id,
name_table,
street_name_suffix_table);
}
bool MergableRoadDetector::EdgeDataSupportsMerge(
@@ -161,7 +157,8 @@ bool MergableRoadDetector::EdgeDataSupportsMerge(
return false;
// we require valid names
if (!HaveIdenticalNames(lhs_annotation.name_id, rhs_annotation.name_id))
if (!HaveIdenticalNames(
lhs_annotation.name_id, rhs_annotation.name_id, name_table, street_name_suffix_table))
return false;
return lhs_flags.road_classification == rhs_flags.road_classification;
@@ -170,8 +167,9 @@ bool MergableRoadDetector::EdgeDataSupportsMerge(
bool MergableRoadDetector::IsTrafficLoop(const NodeID intersection_node,
const MergableRoadData &road) const
{
const auto connection = intersection_generator.SkipDegreeTwoNodes(intersection_node, road.eid);
return intersection_node == node_based_graph.GetTarget(connection.via_eid);
const auto connection =
intersection::skipDegreeTwoNodes(node_based_graph, {intersection_node, road.eid});
return intersection_node == node_based_graph.GetTarget(connection.edge);
}
bool MergableRoadDetector::IsNarrowTriangle(const NodeID intersection_node,
@@ -180,8 +178,22 @@ bool MergableRoadDetector::IsNarrowTriangle(const NodeID intersection_node,
{
// selection data to the right and left
const auto constexpr SMALL_RANDOM_HOPLIMIT = 5;
IntersectionFinderAccumulator left_accumulator(SMALL_RANDOM_HOPLIMIT, intersection_generator),
right_accumulator(SMALL_RANDOM_HOPLIMIT, intersection_generator);
IntersectionFinderAccumulator left_accumulator(SMALL_RANDOM_HOPLIMIT,
node_based_graph,
node_data_container,
node_coordinates,
compressed_geometries,
node_restriction_map,
barrier_nodes,
turn_lanes_data),
right_accumulator(SMALL_RANDOM_HOPLIMIT,
node_based_graph,
node_data_container,
node_coordinates,
compressed_geometries,
node_restriction_map,
barrier_nodes,
turn_lanes_data);
/* Standard following the straightmost road
* Since both items have the same id, we can `select` based on any setup
@@ -193,8 +205,13 @@ bool MergableRoadDetector::IsNarrowTriangle(const NodeID intersection_node,
/*requires entry=*/false,
false);
NodeBasedGraphWalker graph_walker(
node_based_graph, node_data_container, intersection_generator);
NodeBasedGraphWalker graph_walker(node_based_graph,
node_data_container,
node_coordinates,
compressed_geometries,
node_restriction_map,
barrier_nodes,
turn_lanes_data);
graph_walker.TraverseRoad(intersection_node, lhs.eid, left_accumulator, selector);
/* if the intersection does not have a right turn, we continue onto the next one once
* (skipping over a single small side street)
@@ -266,7 +283,13 @@ bool MergableRoadDetector::IsNarrowTriangle(const NodeID intersection_node,
// check if both intersections are connected
IntersectionFinderAccumulator connect_accumulator(SMALL_RANDOM_HOPLIMIT,
intersection_generator);
node_based_graph,
node_data_container,
node_coordinates,
compressed_geometries,
node_restriction_map,
barrier_nodes,
turn_lanes_data);
graph_walker.TraverseRoad(node_based_graph.GetTarget(left_accumulator.via_edge_id),
connector_turn->eid,
connect_accumulator,
@@ -281,8 +304,13 @@ bool MergableRoadDetector::IsCircularShape(const NodeID intersection_node,
const MergableRoadData &lhs,
const MergableRoadData &rhs) const
{
NodeBasedGraphWalker graph_walker(
node_based_graph, node_data_container, intersection_generator);
NodeBasedGraphWalker graph_walker(node_based_graph,
node_data_container,
node_coordinates,
compressed_geometries,
node_restriction_map,
barrier_nodes,
turn_lanes_data);
const auto getCoordinatesAlongWay = [&](const EdgeID edge_id, const double max_length) {
LengthLimitedCoordinateAccumulator accumulator(coordinate_extractor, max_length);
SelectStraightmostRoadByNameAndOnlyChoice selector(
@@ -348,8 +376,13 @@ bool MergableRoadDetector::HaveSameDirection(const NodeID intersection_node,
return false;
// Find a coordinate following a road that is far away
NodeBasedGraphWalker graph_walker(
node_based_graph, node_data_container, intersection_generator);
NodeBasedGraphWalker graph_walker(node_based_graph,
node_data_container,
node_coordinates,
compressed_geometries,
node_restriction_map,
barrier_nodes,
turn_lanes_data);
const auto getCoordinatesAlongWay = [&](const EdgeID edge_id, const double max_length) {
LengthLimitedCoordinateAccumulator accumulator(coordinate_extractor, max_length);
SelectStraightmostRoadByNameAndOnlyChoice selector(
@@ -419,10 +452,16 @@ bool MergableRoadDetector::HaveSameDirection(const NodeID intersection_node,
return false;
// compare reference distance:
const auto distance_between_roads = util::coordinate_calculation::findClosestDistance(
const auto distance_mid_left_to_right = util::coordinate_calculation::findClosestDistance(
coordinates_to_the_left[coordinates_to_the_left.size() / 2],
coordinates_to_the_right.begin(),
coordinates_to_the_right.end());
const auto distance_mid_right_to_left = util::coordinate_calculation::findClosestDistance(
coordinates_to_the_right[coordinates_to_the_right.size() / 2],
coordinates_to_the_left.begin(),
coordinates_to_the_left.end());
const auto distance_between_roads =
std::min(distance_mid_left_to_right, distance_mid_right_to_left);
const auto lane_count_lhs = std::max<int>(
1, node_based_graph.GetEdgeData(lhs.eid).flags.road_classification.GetNumberOfLanes());
@@ -443,12 +482,12 @@ bool MergableRoadDetector::IsTrafficIsland(const NodeID intersection_node,
* location with the same name repeatet at least three times
*/
const auto left_connection =
intersection_generator.SkipDegreeTwoNodes(intersection_node, lhs.eid);
intersection::skipDegreeTwoNodes(node_based_graph, {intersection_node, lhs.eid});
const auto right_connection =
intersection_generator.SkipDegreeTwoNodes(intersection_node, rhs.eid);
intersection::skipDegreeTwoNodes(node_based_graph, {intersection_node, rhs.eid});
const auto left_candidate = node_based_graph.GetTarget(left_connection.via_eid);
const auto right_candidate = node_based_graph.GetTarget(right_connection.via_eid);
const auto left_candidate = node_based_graph.GetTarget(left_connection.edge);
const auto right_candidate = node_based_graph.GetTarget(right_connection.edge);
const auto candidate_is_valid =
left_candidate == right_candidate && left_candidate != intersection_node;
@@ -516,9 +555,16 @@ bool MergableRoadDetector::IsLinkRoad(const NodeID intersection_node,
const MergableRoadData &road) const
{
const auto next_intersection_parameters =
intersection_generator.SkipDegreeTwoNodes(intersection_node, road.eid);
const auto next_intersection_along_road = intersection_generator.GetConnectedRoads(
next_intersection_parameters.nid, next_intersection_parameters.via_eid);
intersection::skipDegreeTwoNodes(node_based_graph, {intersection_node, road.eid});
const auto next_intersection_along_road =
intersection::getConnectedRoads<false>(node_based_graph,
node_data_container,
node_coordinates,
compressed_geometries,
node_restriction_map,
barrier_nodes,
turn_lanes_data,
next_intersection_parameters);
const auto extract_name_id = [this](const MergableRoadData &road) {
return node_data_container
.GetAnnotation(node_based_graph.GetEdgeData(road.eid).annotation_data)
@@ -543,7 +589,7 @@ bool MergableRoadDetector::IsLinkRoad(const NodeID intersection_node,
// we cannot be looking at the same road we came from
if (node_based_graph.GetTarget(opposite_of_next_road_along_path->eid) ==
next_intersection_parameters.nid)
next_intersection_parameters.node)
return false;
/* check if the opposite of the next road decision was sane. It could have been just as well our
+53 -21
View File
@@ -2,6 +2,7 @@
#include "extractor/guidance/constants.hpp"
#include "extractor/guidance/road_classification.hpp"
#include "util/assert.hpp"
#include "util/bearing.hpp"
#include "util/guidance/name_announcements.hpp"
@@ -42,15 +43,21 @@ inline bool isRampClass(EdgeID eid, const util::NodeBasedDynamicGraph &node_base
MotorwayHandler::MotorwayHandler(const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &coordinates,
const extractor::CompressedEdgeContainer &compressed_geometries,
const RestrictionMap &node_restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table,
const IntersectionGenerator &intersection_generator)
const SuffixTable &street_name_suffix_table)
: IntersectionHandler(node_based_graph,
node_data_container,
coordinates,
compressed_geometries,
node_restriction_map,
barrier_nodes,
turn_lanes_data,
name_table,
street_name_suffix_table,
intersection_generator)
street_name_suffix_table)
{
}
@@ -232,7 +239,12 @@ Intersection MotorwayHandler::fromMotorway(const EdgeID via_eid, Intersection in
intersection[1].instruction =
getInstructionForObvious(intersection.size(),
via_eid,
isThroughStreet(1, intersection),
isThroughStreet(1,
intersection,
node_based_graph,
node_data_container,
name_table,
street_name_suffix_table),
intersection[1]);
}
else
@@ -246,8 +258,16 @@ Intersection MotorwayHandler::fromMotorway(const EdgeID via_eid, Intersection in
if (road.angle == continue_angle)
{
road.instruction = getInstructionForObvious(
intersection.size(), via_eid, isThroughStreet(1, intersection), road);
road.instruction =
getInstructionForObvious(intersection.size(),
via_eid,
isThroughStreet(1,
intersection,
node_based_graph,
node_data_container,
name_table,
street_name_suffix_table),
road);
}
else if (road.angle < continue_angle)
{
@@ -271,17 +291,11 @@ Intersection MotorwayHandler::fromMotorway(const EdgeID via_eid, Intersection in
// handle motorway forks
else if (exiting_motorways > 1)
{
if (exiting_motorways == 2 && intersection.size() == 2)
{
intersection[1].instruction =
getInstructionForObvious(intersection.size(),
via_eid,
isThroughStreet(1, intersection),
intersection[1]);
intersection[0].entry_allowed = false; // UTURN on the freeway
}
else if (exiting_motorways == 2)
if (exiting_motorways == 2)
{
OSRM_ASSERT(intersection.size() != 2,
node_coordinates[node_based_graph.GetTarget(via_eid)]);
// standard fork
std::size_t first_valid = std::numeric_limits<std::size_t>::max(),
second_valid = std::numeric_limits<std::size_t>::max();
@@ -352,8 +366,16 @@ Intersection MotorwayHandler::fromRamp(const EdgeID via_eid, Intersection inters
BOOST_ASSERT(!intersection[0].entry_allowed);
BOOST_ASSERT(isMotorwayClass(intersection[1].eid, node_based_graph));
intersection[1].instruction = getInstructionForObvious(
intersection.size(), via_eid, isThroughStreet(1, intersection), intersection[1]);
intersection[1].instruction =
getInstructionForObvious(intersection.size(),
via_eid,
isThroughStreet(1,
intersection,
node_based_graph,
node_data_container,
name_table,
street_name_suffix_table),
intersection[1]);
}
else if (intersection.size() == 3)
{
@@ -403,7 +425,12 @@ Intersection MotorwayHandler::fromRamp(const EdgeID via_eid, Intersection inters
intersection[1].instruction =
getInstructionForObvious(intersection.size(),
via_eid,
isThroughStreet(1, intersection),
isThroughStreet(1,
intersection,
node_based_graph,
node_data_container,
name_table,
street_name_suffix_table),
intersection[1]);
}
}
@@ -428,7 +455,12 @@ Intersection MotorwayHandler::fromRamp(const EdgeID via_eid, Intersection inters
intersection[2].instruction =
getInstructionForObvious(intersection.size(),
via_eid,
isThroughStreet(2, intersection),
isThroughStreet(2,
intersection,
node_based_graph,
node_data_container,
name_table,
street_name_suffix_table),
intersection[2]);
}
}
@@ -1,4 +1,5 @@
#include "extractor/guidance/node_based_graph_walker.hpp"
#include "extractor/intersection/intersection_analysis.hpp"
#include "util/bearing.hpp"
#include "util/coordinate_calculation.hpp"
@@ -14,11 +15,18 @@ namespace guidance
{
// ---------------------------------------------------------------------------------
NodeBasedGraphWalker::NodeBasedGraphWalker(const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const IntersectionGenerator &intersection_generator)
NodeBasedGraphWalker::NodeBasedGraphWalker(
const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &node_coordinates,
const extractor::CompressedEdgeContainer &compressed_geometries,
const RestrictionMap &node_restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data)
: node_based_graph(node_based_graph), node_data_container(node_data_container),
intersection_generator(intersection_generator)
node_coordinates(node_coordinates), compressed_geometries(compressed_geometries),
node_restriction_map(node_restriction_map), barrier_nodes(barrier_nodes),
turn_lanes_data(turn_lanes_data)
{
}
@@ -235,8 +243,18 @@ operator()(const NodeID /*nid*/,
// ---------------------------------------------------------------------------------
IntersectionFinderAccumulator::IntersectionFinderAccumulator(
const std::uint8_t hop_limit, const IntersectionGenerator &intersection_generator)
: hops(0), hop_limit(hop_limit), intersection_generator(intersection_generator)
const std::uint8_t hop_limit,
const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &node_coordinates,
const extractor::CompressedEdgeContainer &compressed_geometries,
const RestrictionMap &node_restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data)
: hops(0), hop_limit(hop_limit), node_based_graph(node_based_graph),
node_data_container(node_data_container), node_coordinates(node_coordinates),
compressed_geometries(compressed_geometries), node_restriction_map(node_restriction_map),
barrier_nodes(barrier_nodes), turn_lanes_data(turn_lanes_data)
{
}
@@ -261,7 +279,14 @@ void IntersectionFinderAccumulator::update(const NodeID from_node,
nid = from_node;
via_edge_id = via_edge;
intersection = intersection_generator.GetConnectedRoads(from_node, via_edge, true);
intersection = intersection::getConnectedRoads<true>(node_based_graph,
node_data_container,
node_coordinates,
compressed_geometries,
node_restriction_map,
barrier_nodes,
turn_lanes_data,
{from_node, via_edge});
}
} // namespace guidance
+30 -89
View File
@@ -23,21 +23,26 @@ namespace extractor
namespace guidance
{
RoundaboutHandler::RoundaboutHandler(const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &coordinates,
const CompressedEdgeContainer &compressed_edge_container,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table,
const IntersectionGenerator &intersection_generator)
RoundaboutHandler::RoundaboutHandler(
const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &coordinates,
const extractor::CompressedEdgeContainer &compressed_geometries,
const RestrictionMap &node_restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table)
: IntersectionHandler(node_based_graph,
node_data_container,
coordinates,
compressed_geometries,
node_restriction_map,
barrier_nodes,
turn_lanes_data,
name_table,
street_name_suffix_table,
intersection_generator),
compressed_edge_container(compressed_edge_container),
coordinate_extractor(node_based_graph, compressed_edge_container, coordinates)
street_name_suffix_table),
coordinate_extractor(node_based_graph, compressed_geometries, coordinates)
{
}
@@ -56,7 +61,6 @@ bool RoundaboutHandler::canProcess(const NodeID from_nid,
Intersection RoundaboutHandler::
operator()(const NodeID from_nid, const EdgeID via_eid, Intersection intersection) const
{
invalidateExitAgainstDirection(from_nid, via_eid, intersection);
const auto flags = getRoundaboutFlags(from_nid, via_eid, intersection);
const auto roundabout_type = getRoundaboutType(node_based_graph.GetTarget(via_eid));
// find the radius of the roundabout
@@ -107,77 +111,6 @@ detail::RoundaboutFlags RoundaboutHandler::getRoundaboutFlags(
return {on_roundabout, can_enter_roundabout, can_exit_roundabout_separately};
}
void RoundaboutHandler::invalidateExitAgainstDirection(const NodeID from_nid,
const EdgeID via_eid,
Intersection &intersection) const
{
const auto &in_edge_class = node_based_graph.GetEdgeData(via_eid).flags;
if (in_edge_class.roundabout || in_edge_class.circular)
return;
// Find range in which exits that must be invalidated (shaded areas):
// exit..end exit..end begin..exit for ↺ roundabouts
// *************************************
// * <--. ^ <--. / <--. *
// * | / | /░ | *
// * |/ |v░░ -->| *
// * |^ |\ ░ ░░░|\ *
// * |░\ |░\░ ░░░| \ *
// * --'░░░\ --'░░░v --' v *
// *************************************
//
// begin..exit begin..exit exit..end for ↻ roundabouts
// *************************************
// * --.░░░^ --.░░░/ --. ^ *
// * |░/░ |░/ ░░░| / *
// * |/░░ |v ░░░|/ *
// * |^░░ |\ -->| *
// * | \░ | \ | *
// * <--' \ <--' v <--' *
// *************************************
bool roundabout_entry_first = false;
auto invalidate_from = intersection.end(), invalidate_to = intersection.end();
for (auto road = intersection.begin(); road != intersection.end(); ++road)
{
const auto &edge = node_based_graph.GetEdgeData(road->eid);
if (edge.flags.roundabout || edge.flags.circular)
{
if (edge.reversed)
{
if (roundabout_entry_first)
{ // invalidate turns in range exit..end
invalidate_from = road + 1;
invalidate_to = intersection.end();
}
else
{ // invalidate turns in range begin..exit
invalidate_from = intersection.begin() + 1;
invalidate_to = road;
}
}
else
{
roundabout_entry_first = true;
}
}
}
OSRM_ASSERT(invalidate_from <= invalidate_to, coordinates[from_nid]);
// Exiting roundabouts at an entry point is technically a data-modelling issue.
// This workaround handles cases in which an exit precedes and entry. The resulting
// u-turn against the roundabout direction is invalidated.
for (; invalidate_from != invalidate_to; ++invalidate_from)
{
const auto &edge = node_based_graph.GetEdgeData(invalidate_from->eid);
if (!edge.flags.roundabout && !edge.flags.circular &&
node_based_graph.GetTarget(invalidate_from->eid) != from_nid)
{
invalidate_from->entry_allowed = false;
}
}
}
// If we want to see a roundabout as a turn, the exits have to be distinct enough to be seen a
// dedicated turns. We are limiting it to four-way intersections with well distinct bearings.
// All entry/roads and exit roads have to be simple. Not segregated roads.
@@ -215,7 +148,7 @@ bool RoundaboutHandler::qualifiesAsRoundaboutIntersection(
continue;
// there is a single non-roundabout edge
const auto src_coordinate = coordinates[node];
const auto src_coordinate = node_coordinates[node];
const auto edge_range = node_based_graph.GetAdjacentEdgeRange(node);
const auto number_of_lanes_at_intersection = std::accumulate(
@@ -334,11 +267,11 @@ RoundaboutType RoundaboutHandler::getRoundaboutType(const NodeID nid) const
const auto getEdgeLength = [&](const NodeID source_node, EdgeID eid) {
double length = 0.;
auto last_coord = coordinates[source_node];
const auto &edge_bucket = compressed_edge_container.GetBucketReference(eid);
auto last_coord = node_coordinates[source_node];
const auto &edge_bucket = compressed_geometries.GetBucketReference(eid);
for (const auto &compressed_edge : edge_bucket)
{
const auto next_coord = coordinates[compressed_edge.node_id];
const auto next_coord = node_coordinates[compressed_edge.node_id];
length += util::coordinate_calculation::haversineDistance(last_coord, next_coord);
last_coord = next_coord;
}
@@ -545,8 +478,16 @@ Intersection RoundaboutHandler::handleRoundabouts(const RoundaboutType roundabou
if (util::angularDeviation(turn.angle, STRAIGHT_ANGLE) < FUZZY_ANGLE_DIFFERENCE &&
crossing_roundabout)
{
turn.instruction = getInstructionForObvious(
intersection.size(), via_eid, isThroughStreet(idx, intersection), turn);
turn.instruction =
getInstructionForObvious(intersection.size(),
via_eid,
isThroughStreet(idx,
intersection,
node_based_graph,
node_data_container,
name_table,
street_name_suffix_table),
turn);
}
else
{
+53 -51
View File
@@ -22,18 +22,25 @@ namespace extractor
namespace guidance
{
SliproadHandler::SliproadHandler(const IntersectionGenerator &intersection_generator,
const util::NodeBasedDynamicGraph &node_based_graph,
SliproadHandler::SliproadHandler(const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &coordinates,
const std::vector<util::Coordinate> &node_coordinates,
const extractor::CompressedEdgeContainer &compressed_geometries,
const RestrictionMap &node_restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table)
: IntersectionHandler(node_based_graph,
node_data_container,
coordinates,
node_coordinates,
compressed_geometries,
node_restriction_map,
barrier_nodes,
turn_lanes_data,
name_table,
street_name_suffix_table,
intersection_generator)
street_name_suffix_table),
coordinate_extractor(node_based_graph, compressed_geometries, node_coordinates)
{
}
@@ -243,7 +250,14 @@ operator()(const NodeID /*nid*/, const EdgeID source_edge_id, Intersection inter
// Starting out at the intersection and going onto the Sliproad we skip artificial
// degree two intersections and limit the max hop count in doing so.
IntersectionFinderAccumulator intersection_finder{10, intersection_generator};
IntersectionFinderAccumulator intersection_finder{10,
node_based_graph,
node_data_container,
node_coordinates,
compressed_geometries,
node_restriction_map,
barrier_nodes,
turn_lanes_data};
const SkipTrafficSignalBarrierRoadSelector road_selector{};
(void)graph_walker.TraverseRoad(intersection_node_id, // start node
sliproad_edge, // onto edge
@@ -330,9 +344,24 @@ operator()(const NodeID /*nid*/, const EdgeID source_edge_id, Intersection inter
}
// If the sliproad candidate is a through street, we cannot handle it as a sliproad.
if (isThroughStreet(sliproad_edge, target_intersection))
auto sliproad_in_target_intersection =
std::find_if(begin(target_intersection),
end(target_intersection),
[&](const auto &road) { return road.eid == sliproad_edge; });
if (sliproad_in_target_intersection != target_intersection.end())
{
continue;
auto index_of_sliproad_in_target_intersection =
sliproad_in_target_intersection - target_intersection.begin();
if (isThroughStreet<IntersectionView>(index_of_sliproad_in_target_intersection,
target_intersection,
node_based_graph,
node_data_container,
name_table,
street_name_suffix_table))
{
continue;
}
}
// The turn off of the Sliproad has to be obvious and a narrow turn and must not be a
@@ -372,8 +401,6 @@ operator()(const NodeID /*nid*/, const EdgeID source_edge_id, Intersection inter
//
// Sliproad Not a Sliproad
{
const auto &coordinate_extractor = intersection_generator.GetCoordinateExtractor();
const NodeID start = intersection_node_id; // b
const EdgeID edge = sliproad_edge; // bd
@@ -424,8 +451,8 @@ operator()(const NodeID /*nid*/, const EdgeID source_edge_id, Intersection inter
// Only check for curvature and ~90 degree when it makes sense to do so.
const constexpr auto MIN_LENGTH = 3.;
const auto length = haversineDistance(coordinates[intersection_node_id],
coordinates[main_road_intersection->node]);
const auto length = haversineDistance(node_coordinates[intersection_node_id],
node_coordinates[main_road_intersection->node]);
const double minimal_crossroad_angle_of_intersection = 40.;
@@ -475,10 +502,7 @@ operator()(const NodeID /*nid*/, const EdgeID source_edge_id, Intersection inter
// Name mismatch: check roads at `c` and `d` for same name
const auto name_mismatch = [&](const NameID road_name_id) {
const auto unnamed = name_table.GetNameForID(road_name_id).empty();
return unnamed ||
util::guidance::requiresNameAnnounced(road_name_id, //
return util::guidance::requiresNameAnnounced(road_name_id, //
candidate_data.name_id, //
name_table, //
street_name_suffix_table); //
@@ -549,8 +573,15 @@ operator()(const NodeID /*nid*/, const EdgeID source_edge_id, Intersection inter
}
else
{
const auto skip_traffic_light_intersection = intersection_generator(
node_based_graph.GetTarget(sliproad_edge), candidate_road.eid);
const auto skip_traffic_light_intersection = intersection::getConnectedRoads<false>(
node_based_graph,
node_data_container,
node_coordinates,
compressed_geometries,
node_restriction_map,
barrier_nodes,
turn_lanes_data,
{node_based_graph.GetTarget(sliproad_edge), candidate_road.eid});
if (skip_traffic_light_intersection.isTrafficSignalOrBarrier() &&
node_based_graph.GetTarget(skip_traffic_light_intersection[1].eid) ==
main_road_intersection->node)
@@ -582,7 +613,6 @@ operator()(const NodeID /*nid*/, const EdgeID source_edge_id, Intersection inter
}
else if (!name_table.GetNameForID(main_annotation.name_id).empty())
{
OSRM_ASSERT(false, coordinates[intersection_node_id]);
intersection[*obvious].instruction.type = TurnType::NewName;
intersection[*obvious].instruction.direction_modifier =
getTurnDirection(intersection[*obvious].angle);
@@ -660,8 +690,6 @@ bool SliproadHandler::nextIntersectionIsTooFarAway(const NodeID start, const Edg
BOOST_ASSERT(start != SPECIAL_NODEID);
BOOST_ASSERT(onto != SPECIAL_EDGEID);
const auto &coordinate_extractor = intersection_generator.GetCoordinateExtractor();
// Base max distance threshold on the current road class we're on
const auto &data = node_based_graph.GetEdgeData(onto).flags;
const auto threshold = scaledThresholdByRoadClass(MAX_SLIPROAD_THRESHOLD, // <- scales down
@@ -676,32 +704,6 @@ bool SliproadHandler::nextIntersectionIsTooFarAway(const NodeID start, const Edg
return accumulator.too_far_away;
}
bool SliproadHandler::isThroughStreet(const EdgeID from, const IntersectionView &intersection) const
{
BOOST_ASSERT(from != SPECIAL_EDGEID);
BOOST_ASSERT(!intersection.empty());
const auto from_annotation_id = node_based_graph.GetEdgeData(from).annotation_data;
const auto &edge_name_id = node_data_container.GetAnnotation(from_annotation_id).name_id;
auto first = begin(intersection) + 1; // Skip UTurn road
auto last = end(intersection);
auto same_name = [&](const auto &road) {
const auto annotation_id = node_based_graph.GetEdgeData(road.eid).annotation_data;
const auto &road_name_id = node_data_container.GetAnnotation(annotation_id).name_id;
return edge_name_id != EMPTY_NAMEID && //
road_name_id != EMPTY_NAMEID && //
!util::guidance::requiresNameAnnounced(edge_name_id,
road_name_id,
name_table,
street_name_suffix_table); //
};
return std::find_if(first, last, same_name) != last;
}
bool SliproadHandler::roadContinues(const EdgeID current, const EdgeID next) const
{
const auto &current_data =
@@ -732,9 +734,9 @@ bool SliproadHandler::isValidSliproadArea(const double max_area,
{
using namespace util::coordinate_calculation;
const auto first = coordinates[a];
const auto second = coordinates[b];
const auto third = coordinates[c];
const auto first = node_coordinates[a];
const auto second = node_coordinates[b];
const auto third = node_coordinates[c];
const auto length = haversineDistance(first, second);
const auto heigth = haversineDistance(second, third);
@@ -11,18 +11,25 @@ namespace extractor
namespace guidance
{
SuppressModeHandler::SuppressModeHandler(const IntersectionGenerator &intersection_generator,
const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &coordinates,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table)
SuppressModeHandler::SuppressModeHandler(
const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &coordinates,
const extractor::CompressedEdgeContainer &compressed_geometries,
const RestrictionMap &node_restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table)
: IntersectionHandler(node_based_graph,
node_data_container,
coordinates,
compressed_geometries,
node_restriction_map,
barrier_nodes,
turn_lanes_data,
name_table,
street_name_suffix_table,
intersection_generator)
street_name_suffix_table)
{
}
+48 -76
View File
@@ -23,89 +23,79 @@ using EdgeData = util::NodeBasedDynamicGraph::EdgeData;
TurnAnalysis::TurnAnalysis(const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &coordinates,
const std::vector<util::Coordinate> &node_coordinates,
const CompressedEdgeContainer &compressed_edge_container,
const RestrictionMap &restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const CompressedEdgeContainer &compressed_edge_container,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table)
: node_based_graph(node_based_graph), intersection_generator(node_based_graph,
node_data_container,
restriction_map,
barrier_nodes,
coordinates,
compressed_edge_container),
intersection_normalizer(node_based_graph,
node_data_container,
coordinates,
name_table,
street_name_suffix_table,
intersection_generator),
roundabout_handler(node_based_graph,
node_data_container,
coordinates,
compressed_edge_container,
name_table,
street_name_suffix_table,
intersection_generator),
: node_based_graph(node_based_graph), roundabout_handler(node_based_graph,
node_data_container,
node_coordinates,
compressed_edge_container,
restriction_map,
barrier_nodes,
turn_lanes_data,
name_table,
street_name_suffix_table),
motorway_handler(node_based_graph,
node_data_container,
coordinates,
node_coordinates,
compressed_edge_container,
restriction_map,
barrier_nodes,
turn_lanes_data,
name_table,
street_name_suffix_table,
intersection_generator),
street_name_suffix_table),
turn_handler(node_based_graph,
node_data_container,
coordinates,
node_coordinates,
compressed_edge_container,
restriction_map,
barrier_nodes,
turn_lanes_data,
name_table,
street_name_suffix_table,
intersection_generator),
sliproad_handler(intersection_generator,
node_based_graph,
street_name_suffix_table),
sliproad_handler(node_based_graph,
node_data_container,
coordinates,
node_coordinates,
compressed_edge_container,
restriction_map,
barrier_nodes,
turn_lanes_data,
name_table,
street_name_suffix_table),
suppress_mode_handler(intersection_generator,
node_based_graph,
suppress_mode_handler(node_based_graph,
node_data_container,
coordinates,
node_coordinates,
compressed_edge_container,
restriction_map,
barrier_nodes,
turn_lanes_data,
name_table,
street_name_suffix_table),
driveway_handler(intersection_generator,
node_based_graph,
driveway_handler(node_based_graph,
node_data_container,
coordinates,
node_coordinates,
compressed_edge_container,
restriction_map,
barrier_nodes,
turn_lanes_data,
name_table,
street_name_suffix_table),
statistics_handler(intersection_generator,
node_based_graph,
statistics_handler(node_based_graph,
node_data_container,
coordinates,
node_coordinates,
compressed_edge_container,
restriction_map,
barrier_nodes,
turn_lanes_data,
name_table,
street_name_suffix_table)
{
}
Intersection TurnAnalysis::operator()(const NodeID node_prior_to_intersection,
const EdgeID entering_via_edge) const
{
TurnAnalysis::ShapeResult shape_result =
ComputeIntersectionShapes(node_based_graph.GetTarget(entering_via_edge));
// assign valid flags to normalized_shape
const auto intersection_view = intersection_generator.TransformIntersectionShapeIntoView(
node_prior_to_intersection,
entering_via_edge,
shape_result.annotated_normalized_shape.normalized_shape,
shape_result.intersection_shape,
shape_result.annotated_normalized_shape.performed_merges);
// assign the turn types to the intersection
return AssignTurnTypes(node_prior_to_intersection, entering_via_edge, intersection_view);
}
Intersection TurnAnalysis::AssignTurnTypes(const NodeID node_prior_to_intersection,
const EdgeID entering_via_edge,
const IntersectionView &intersection_view) const
@@ -191,19 +181,6 @@ Intersection TurnAnalysis::AssignTurnTypes(const NodeID node_prior_to_intersecti
return intersection;
}
TurnAnalysis::ShapeResult
TurnAnalysis::ComputeIntersectionShapes(const NodeID node_at_center_of_intersection) const
{
ShapeResult intersection_shape;
intersection_shape.intersection_shape =
intersection_generator.ComputeIntersectionShape(node_at_center_of_intersection);
intersection_shape.annotated_normalized_shape = intersection_normalizer(
node_at_center_of_intersection, intersection_shape.intersection_shape);
return intersection_shape;
}
// Sets basic turn types as fallback for otherwise unhandled turns
Intersection TurnAnalysis::setTurnTypes(const NodeID node_prior_to_intersection,
const EdgeID,
@@ -225,11 +202,6 @@ Intersection TurnAnalysis::setTurnTypes(const NodeID node_prior_to_intersection,
return intersection;
}
const IntersectionGenerator &TurnAnalysis::GetIntersectionGenerator() const
{
return intersection_generator;
}
} // namespace guidance
} // namespace extractor
} // namespace osrm
+36 -11
View File
@@ -1,5 +1,7 @@
#include "extractor/guidance/turn_discovery.hpp"
#include "extractor/guidance/constants.hpp"
#include "extractor/guidance/coordinate_extractor.hpp"
#include "extractor/intersection/intersection_analysis.hpp"
#include "util/bearing.hpp"
#include "util/coordinate_calculation.hpp"
@@ -14,16 +16,16 @@ namespace guidance
namespace lanes
{
namespace
{
const constexpr bool USE_LOW_PRECISION_MODE = true;
}
bool findPreviousIntersection(const NodeID node_v,
const EdgeID via_edge,
const Intersection &intersection,
const IntersectionGenerator &intersection_generator,
const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &node_coordinates,
const extractor::CompressedEdgeContainer &compressed_geometries,
const RestrictionMap &node_restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
// output parameters
NodeID &result_node,
EdgeID &result_via_edge,
@@ -43,7 +45,9 @@ bool findPreviousIntersection(const NodeID node_v,
*/
const constexpr double COMBINE_DISTANCE_CUTOFF = 30;
const auto coordinate_extractor = intersection_generator.GetCoordinateExtractor();
const CoordinateExtractor coordinate_extractor(
node_based_graph, compressed_geometries, node_coordinates);
const auto coordinates_along_via_edge =
coordinate_extractor.GetForwardCoordinatesAlongRoad(node_v, via_edge);
const auto via_edge_length =
@@ -71,7 +75,14 @@ bool findPreviousIntersection(const NodeID node_v,
return false;
const auto node_v_reverse_intersection =
intersection_generator.GetConnectedRoads(node_w, u_turn_at_node_w, USE_LOW_PRECISION_MODE);
intersection::getConnectedRoads<true>(node_based_graph,
node_data_container,
node_coordinates,
compressed_geometries,
node_restriction_map,
barrier_nodes,
turn_lanes_data,
{node_w, u_turn_at_node_w});
// Continue along the straightmost turn. If there is no straight turn, we cannot find a valid
// previous intersection.
const auto straightmost_at_v_in_reverse =
@@ -83,8 +94,15 @@ bool findPreviousIntersection(const NodeID node_v,
return false;
const auto node_u = node_based_graph.GetTarget(straightmost_at_v_in_reverse->eid);
const auto node_u_reverse_intersection = intersection_generator.GetConnectedRoads(
node_v, straightmost_at_v_in_reverse->eid, USE_LOW_PRECISION_MODE);
const auto node_u_reverse_intersection =
intersection::getConnectedRoads<true>(node_based_graph,
node_data_container,
node_coordinates,
compressed_geometries,
node_restriction_map,
barrier_nodes,
turn_lanes_data,
{node_v, straightmost_at_v_in_reverse->eid});
// now check that the u-turn at the given intersection connects to via-edge
// The u-turn at the now found intersection should, hopefully, represent the previous edge.
@@ -102,7 +120,14 @@ bool findPreviousIntersection(const NodeID node_v,
return false;
}
result_intersection = intersection_generator(node_u, result_via_edge);
result_intersection = intersection::getConnectedRoads<false>(node_based_graph,
node_data_container,
node_coordinates,
compressed_geometries,
node_restriction_map,
barrier_nodes,
turn_lanes_data,
{node_u, result_via_edge});
const auto check_via_edge =
result_intersection.end() !=
std::find_if(result_intersection.begin(),
+36 -9
View File
@@ -113,15 +113,21 @@ std::size_t TurnHandler::Fork::getLeftIndex() const
TurnHandler::TurnHandler(const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &coordinates,
const extractor::CompressedEdgeContainer &compressed_geometries,
const RestrictionMap &node_restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table,
const IntersectionGenerator &intersection_generator)
const SuffixTable &street_name_suffix_table)
: IntersectionHandler(node_based_graph,
node_data_container,
coordinates,
compressed_geometries,
node_restriction_map,
barrier_nodes,
turn_lanes_data,
name_table,
street_name_suffix_table,
intersection_generator)
street_name_suffix_table)
{
}
@@ -281,8 +287,16 @@ Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection
const auto direction_at_two = getTurnDirection(intersection[2].angle);
if (obvious_index == 1)
{
intersection[1].instruction = getInstructionForObvious(
3, via_edge, isThroughStreet(1, intersection), intersection[1]);
intersection[1].instruction =
getInstructionForObvious(3,
via_edge,
isThroughStreet(1,
intersection,
node_based_graph,
node_data_container,
name_table,
street_name_suffix_table),
intersection[1]);
const auto second_direction = (direction_at_one == direction_at_two &&
direction_at_two == DirectionModifier::Straight)
? DirectionModifier::SlightLeft
@@ -294,8 +308,16 @@ Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection
else
{
BOOST_ASSERT(obvious_index == 2);
intersection[2].instruction = getInstructionForObvious(
3, via_edge, isThroughStreet(2, intersection), intersection[2]);
intersection[2].instruction =
getInstructionForObvious(3,
via_edge,
isThroughStreet(2,
intersection,
node_based_graph,
node_data_container,
name_table,
street_name_suffix_table),
intersection[2]);
const auto first_direction = (direction_at_one == direction_at_two &&
direction_at_one == DirectionModifier::Straight)
? DirectionModifier::SlightRight
@@ -330,7 +352,12 @@ Intersection TurnHandler::handleComplexTurn(const EdgeID via_edge, Intersection
intersection[obvious_index].instruction =
getInstructionForObvious(intersection.size(),
via_edge,
isThroughStreet(obvious_index, intersection),
isThroughStreet(obvious_index,
intersection,
node_based_graph,
node_data_container,
name_table,
street_name_suffix_table),
intersection[obvious_index]);
// assign left/right turns
+27 -5
View File
@@ -3,6 +3,7 @@
#include "extractor/guidance/turn_discovery.hpp"
#include "extractor/guidance/turn_lane_augmentation.hpp"
#include "extractor/guidance/turn_lane_matcher.hpp"
#include "extractor/intersection/intersection_analysis.hpp"
#include "util/bearing.hpp"
#include "util/log.hpp"
#include "util/typedefs.hpp"
@@ -35,14 +36,21 @@ std::size_t getNumberOfTurns(const Intersection &intersection)
TurnLaneHandler::TurnLaneHandler(const util::NodeBasedDynamicGraph &node_based_graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &node_coordinates,
const extractor::CompressedEdgeContainer &compressed_geometries,
const RestrictionMap &node_restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
LaneDescriptionMap &lane_description_map,
const TurnAnalysis &turn_analysis,
util::guidance::LaneDataIdMap &id_map)
: node_based_graph(node_based_graph), node_data_container(node_data_container),
lane_description_map(lane_description_map), turn_analysis(turn_analysis), id_map(id_map)
node_coordinates(node_coordinates), compressed_geometries(compressed_geometries),
node_restriction_map(node_restriction_map), barrier_nodes(barrier_nodes),
turn_lanes_data(turn_lanes_data), lane_description_map(lane_description_map),
turn_analysis(turn_analysis), id_map(id_map)
{
std::tie(turn_lane_offsets, turn_lane_masks) =
transformTurnLaneMapIntoArrays(lane_description_map);
std::tie(turn_lane_offsets, turn_lane_masks) = turn_lanes_data;
count_handled = count_called = 0;
}
@@ -205,8 +213,13 @@ TurnLaneScenario TurnLaneHandler::deduceScenario(const NodeID at,
if (findPreviousIntersection(at,
via_edge,
intersection,
turn_analysis.GetIntersectionGenerator(),
node_based_graph,
node_data_container,
node_coordinates,
compressed_geometries,
node_restriction_map,
barrier_nodes,
turn_lanes_data,
previous_node,
previous_via_edge,
previous_intersection_view))
@@ -560,7 +573,16 @@ std::pair<LaneDataVector, LaneDataVector> TurnLaneHandler::partitionLaneData(
// find out about the next intersection. To check for valid matches, we also need the turn
// types. We can skip merging/angle adjustments, though
const auto next_intersection = turn_analysis.AssignTurnTypes(
at, straightmost->eid, turn_analysis.GetIntersectionGenerator()(at, straightmost->eid));
at,
straightmost->eid,
intersection::getConnectedRoads<false>(node_based_graph,
node_data_container,
node_coordinates,
compressed_geometries,
node_restriction_map,
barrier_nodes,
turn_lanes_data,
{at, straightmost->eid}));
// check where we can match turn lanes
std::size_t straightmost_tag_index = turn_lane_data.size();
@@ -228,7 +228,6 @@ Intersection triviallyMatchLanesToTurns(Intersection intersection,
u_turn = 1;
road_index = 2;
}
intersection[u_turn].entry_allowed = true;
intersection[u_turn].instruction.type = TurnType::Continue;
intersection[u_turn].instruction.direction_modifier = DirectionModifier::UTurn;
@@ -268,7 +267,6 @@ Intersection triviallyMatchLanesToTurns(Intersection intersection,
}
u_turn = intersection.size() - 1;
}
intersection[u_turn].entry_allowed = true;
intersection[u_turn].instruction.type = TurnType::Continue;
intersection[u_turn].instruction.direction_modifier = DirectionModifier::UTurn;
@@ -0,0 +1,845 @@
#include "extractor/intersection/intersection_analysis.hpp"
#include "util/assert.hpp"
#include "util/bearing.hpp"
#include "util/coordinate_calculation.hpp"
#include "extractor/guidance/coordinate_extractor.hpp"
#include <boost/optional/optional_io.hpp>
namespace osrm
{
namespace extractor
{
namespace intersection
{
IntersectionEdges getIncomingEdges(const util::NodeBasedDynamicGraph &graph,
const NodeID intersection_node)
{
IntersectionEdges result;
for (const auto outgoing_edge : graph.GetAdjacentEdgeRange(intersection_node))
{
const auto from_node = graph.GetTarget(outgoing_edge);
const auto incoming_edge = graph.FindEdge(from_node, intersection_node);
if (!graph.GetEdgeData(incoming_edge).reversed)
{
result.push_back({from_node, incoming_edge});
}
}
// Enforce ordering of incoming edges
std::sort(result.begin(), result.end());
return result;
}
IntersectionEdges getOutgoingEdges(const util::NodeBasedDynamicGraph &graph,
const NodeID intersection_node)
{
IntersectionEdges result;
for (const auto outgoing_edge : graph.GetAdjacentEdgeRange(intersection_node))
{
result.push_back({intersection_node, outgoing_edge});
}
BOOST_ASSERT(std::is_sorted(result.begin(), result.end()));
return result;
}
std::vector<util::Coordinate>
getEdgeCoordinates(const extractor::CompressedEdgeContainer &compressed_geometries,
const std::vector<util::Coordinate> &node_coordinates,
const NodeID from_node,
const EdgeID edge,
const NodeID to_node)
{
if (!compressed_geometries.HasEntryForID(edge))
return {node_coordinates[from_node], node_coordinates[to_node]};
BOOST_ASSERT(from_node < node_coordinates.size());
BOOST_ASSERT(to_node < node_coordinates.size());
// extracts the geometry in coordinates from the compressed edge container
std::vector<util::Coordinate> result;
const auto &geometry = compressed_geometries.GetBucketReference(edge);
result.reserve(geometry.size() + 1);
result.push_back(node_coordinates[from_node]);
std::transform(geometry.begin(),
geometry.end(),
std::back_inserter(result),
[&node_coordinates](const auto &compressed_edge) {
return node_coordinates[compressed_edge.node_id];
});
// filter duplicated coordinates
result.erase(std::unique(result.begin(), result.end()), result.end());
return result;
}
namespace
{
double findAngleBisector(double alpha, double beta)
{
alpha *= M_PI / 180.;
beta *= M_PI / 180.;
const auto average =
180. * std::atan2(std::sin(alpha) + std::sin(beta), std::cos(alpha) + std::cos(beta)) /
M_PI;
return std::fmod(average + 360., 360.);
}
double findClosestOppositeBearing(const IntersectionEdgeGeometries &edge_geometries,
const double bearing)
{
BOOST_ASSERT(!edge_geometries.empty());
const auto min = std::min_element(
edge_geometries.begin(),
edge_geometries.end(),
[bearing = util::bearing::reverse(bearing)](const auto &lhs, const auto &rhs) {
return util::angularDeviation(lhs.perceived_bearing, bearing) <
util::angularDeviation(rhs.perceived_bearing, bearing);
});
return util::bearing::reverse(min->perceived_bearing);
}
std::pair<bool, double> findMergedBearing(const util::NodeBasedDynamicGraph &graph,
const IntersectionEdgeGeometries &edge_geometries,
std::size_t lhs_index,
std::size_t rhs_index,
bool neighbor_intersection)
{
// Function returns a pair with a flag and a value of bearing for merged roads
// If the flag is false the bearing must not be used as a merged value at neighbor intersections
using guidance::STRAIGHT_ANGLE;
using guidance::MAXIMAL_ALLOWED_NO_TURN_DEVIATION;
using util::bearing::angleBetween;
using util::angularDeviation;
const auto &lhs = edge_geometries[lhs_index];
const auto &rhs = edge_geometries[rhs_index];
BOOST_ASSERT(graph.GetEdgeData(lhs.edge).reversed != graph.GetEdgeData(rhs.edge).reversed);
const auto &entry = graph.GetEdgeData(lhs.edge).reversed ? rhs : lhs;
const auto opposite_bearing =
findClosestOppositeBearing(edge_geometries, entry.perceived_bearing);
const auto merged_bearing = findAngleBisector(rhs.perceived_bearing, lhs.perceived_bearing);
if (angularDeviation(angleBetween(opposite_bearing, entry.perceived_bearing), STRAIGHT_ANGLE) <
MAXIMAL_ALLOWED_NO_TURN_DEVIATION)
{
// In some intersections, turning roads can introduce artificial turns if we merge here.
// Consider a scenario like:
// 
// a . g - f
// | .
// | .
// |.
// d-b--------e
// |
// c
// 
// Merging `bgf` and `be` would introduce an angle, even though d-b-e is perfectly straight
// We don't change the angle, if such an opposite road exists
return {false, entry.perceived_bearing};
}
if (neighbor_intersection)
{
// Check that the merged bearing makes both turns closer to straight line
const auto turn_angle_lhs = angleBetween(opposite_bearing, lhs.perceived_bearing);
const auto turn_angle_rhs = angleBetween(opposite_bearing, rhs.perceived_bearing);
const auto turn_angle_new = angleBetween(opposite_bearing, merged_bearing);
if (util::angularDeviation(turn_angle_lhs, STRAIGHT_ANGLE) <
util::angularDeviation(turn_angle_new, STRAIGHT_ANGLE) ||
util::angularDeviation(turn_angle_rhs, STRAIGHT_ANGLE) <
util::angularDeviation(turn_angle_new, STRAIGHT_ANGLE))
return {false, opposite_bearing};
}
return {true, merged_bearing};
}
bool isRoadsPairMergeable(const guidance::MergableRoadDetector &detector,
const IntersectionEdgeGeometries &edge_geometries,
const NodeID intersection_node,
const std::size_t index)
{
const auto size = edge_geometries.size();
BOOST_ASSERT(index < size);
const auto &llhs = edge_geometries[(index + size - 1) % size];
const auto &lhs = edge_geometries[index];
const auto &rhs = edge_geometries[(index + 1) % size];
const auto &rrhs = edge_geometries[(index + 2) % size];
// TODO: check IsDistinctFrom - it is an angle and name-only check
// also check CanMergeRoad for all merging scenarios
return detector.IsDistinctFrom({llhs.edge, llhs.perceived_bearing, llhs.length},
{lhs.edge, lhs.perceived_bearing, lhs.length}) &&
detector.CanMergeRoad(intersection_node,
{lhs.edge, lhs.perceived_bearing, lhs.length},
{rhs.edge, rhs.perceived_bearing, rhs.length}) &&
detector.IsDistinctFrom({rhs.edge, rhs.perceived_bearing, rhs.length},
{rrhs.edge, rrhs.perceived_bearing, rrhs.length});
}
auto getIntersectionLanes(const util::NodeBasedDynamicGraph &graph, const NodeID intersection_node)
{
std::uint8_t max_lanes_intersection = 0;
for (auto outgoing_edge : graph.GetAdjacentEdgeRange(intersection_node))
{
max_lanes_intersection =
std::max(max_lanes_intersection,
graph.GetEdgeData(outgoing_edge).flags.road_classification.GetNumberOfLanes());
}
return max_lanes_intersection;
}
template <bool USE_CLOSE_COORDINATE>
IntersectionEdgeGeometries
getIntersectionOutgoingGeometries(const util::NodeBasedDynamicGraph &graph,
const extractor::CompressedEdgeContainer &compressed_geometries,
const std::vector<util::Coordinate> &node_coordinates,
const NodeID intersection_node)
{
IntersectionEdgeGeometries edge_geometries;
// TODO: keep CoordinateExtractor to reproduce bearings, simplify later
const guidance::CoordinateExtractor coordinate_extractor(
graph, compressed_geometries, node_coordinates);
const auto max_lanes_intersection = getIntersectionLanes(graph, intersection_node);
// Collect outgoing edges
for (const auto outgoing_edge : graph.GetAdjacentEdgeRange(intersection_node))
{
const auto remote_node = graph.GetTarget(outgoing_edge);
const auto &geometry = getEdgeCoordinates(
compressed_geometries, node_coordinates, intersection_node, outgoing_edge, remote_node);
// OSRM_ASSERT(geometry.size() >= 2, node_coordinates[intersection_node]);
const auto close_coordinate =
coordinate_extractor.ExtractCoordinateAtLength(2. /*m*/, geometry);
const auto initial_bearing =
util::coordinate_calculation::bearing(geometry[0], close_coordinate);
const auto representative_coordinate =
USE_CLOSE_COORDINATE || graph.GetOutDegree(intersection_node) <= 2
? coordinate_extractor.GetCoordinateCloseToTurn(
intersection_node, outgoing_edge, false, remote_node)
: coordinate_extractor.ExtractRepresentativeCoordinate(intersection_node,
outgoing_edge,
false,
remote_node,
max_lanes_intersection,
geometry);
const auto perceived_bearing =
util::coordinate_calculation::bearing(geometry[0], representative_coordinate);
const auto edge_length = util::coordinate_calculation::getLength(
geometry.begin(), geometry.end(), util::coordinate_calculation::haversineDistance);
edge_geometries.push_back({outgoing_edge, initial_bearing, perceived_bearing, edge_length});
}
// Sort edges in the clockwise bearings order
std::sort(edge_geometries.begin(), edge_geometries.end(), [](const auto &lhs, const auto &rhs) {
return lhs.perceived_bearing < rhs.perceived_bearing;
});
return edge_geometries;
}
}
std::pair<IntersectionEdgeGeometries, std::unordered_set<EdgeID>>
getIntersectionGeometries(const util::NodeBasedDynamicGraph &graph,
const extractor::CompressedEdgeContainer &compressed_geometries,
const std::vector<util::Coordinate> &node_coordinates,
const guidance::MergableRoadDetector &detector,
const NodeID intersection_node)
{
IntersectionEdgeGeometries edge_geometries = getIntersectionOutgoingGeometries<false>(
graph, compressed_geometries, node_coordinates, intersection_node);
const auto edges_number = edge_geometries.size();
std::vector<bool> merged_edges(edges_number, false);
// TODO: intersection views do not contain merged and not allowed edges
// but contain other restricted edges that are used in TurnAnalysis,
// to be deleted after TurnAnalysis refactoring
std::unordered_set<EdgeID> merged_edge_ids;
if (edges_number >= 3)
{ // Adjust bearings of mergeable roads
for (std::size_t index = 0; index < edges_number; ++index)
{
if (isRoadsPairMergeable(detector, edge_geometries, intersection_node, index))
{ // Merge bearings of roads left & right
const auto next = (index + 1) % edges_number;
auto &lhs = edge_geometries[index];
auto &rhs = edge_geometries[next];
merged_edges[index] = true;
merged_edges[next] = true;
const auto merge = findMergedBearing(graph, edge_geometries, index, next, false);
lhs.perceived_bearing = lhs.initial_bearing = merge.second;
rhs.perceived_bearing = rhs.initial_bearing = merge.second;
// Only one of the edges must be reversed, mark it as merged to remove from
// intersection view
BOOST_ASSERT(graph.GetEdgeData(lhs.edge).reversed ^
graph.GetEdgeData(rhs.edge).reversed);
merged_edge_ids.insert(graph.GetEdgeData(lhs.edge).reversed ? lhs.edge : rhs.edge);
}
}
}
if (edges_number >= 2)
{ // Adjust bearings of roads that will be merged at the neighbor intersections
const double constexpr PRUNING_DISTANCE = 30.;
for (std::size_t index = 0; index < edges_number; ++index)
{
auto &edge_geometry = edge_geometries[index];
// Don't adjust bearings of roads that were merged at the current intersection
// or have neighbor intersection farer than the pruning distance
if (merged_edges[index] || edge_geometry.length > PRUNING_DISTANCE)
continue;
const auto neighbor_intersection_node = graph.GetTarget(edge_geometry.edge);
const auto neighbor_geometries = getIntersectionOutgoingGeometries<false>(
graph, compressed_geometries, node_coordinates, neighbor_intersection_node);
const auto neighbor_edges = neighbor_geometries.size();
if (neighbor_edges <= 1)
continue;
const auto neighbor_curr = std::distance(
neighbor_geometries.begin(),
std::find_if(neighbor_geometries.begin(),
neighbor_geometries.end(),
[&graph, &intersection_node](const auto &road) {
return graph.GetTarget(road.edge) == intersection_node;
}));
BOOST_ASSERT(static_cast<std::size_t>(neighbor_curr) != neighbor_geometries.size());
const auto neighbor_prev = (neighbor_curr + neighbor_edges - 1) % neighbor_edges;
const auto neighbor_next = (neighbor_curr + 1) % neighbor_edges;
if (isRoadsPairMergeable(
detector, neighbor_geometries, neighbor_intersection_node, neighbor_prev))
{ // Neighbor intersection has mergable neighbor_prev and neighbor_curr roads
BOOST_ASSERT(!isRoadsPairMergeable(
detector, neighbor_geometries, neighbor_intersection_node, neighbor_curr));
// TODO: merge with an angle bisector, but not a reversed closed turn, to be
// checked as a difference with the previous implementation
const auto merge = findMergedBearing(
graph, neighbor_geometries, neighbor_prev, neighbor_curr, true);
if (merge.first)
{
const auto offset = util::angularDeviation(
merge.second, neighbor_geometries[neighbor_curr].perceived_bearing);
// Adjust bearing of AB at the node A if at the node B roads BA (neighbor_curr)
// and BC (neighbor_prev) will be merged and will have merged bearing Bb.
// The adjustment value is ∠bBA with negative sign (counter-clockwise) to Aa
// A ~~~ a
// \ 
// b --- B ---
// /
// C
edge_geometry.perceived_bearing = edge_geometry.initial_bearing =
std::fmod(edge_geometry.perceived_bearing + 360. - offset, 360.);
}
}
else if (isRoadsPairMergeable(
detector, neighbor_geometries, neighbor_intersection_node, neighbor_curr))
{ // Neighbor intersection has mergable neighbor_curr and neighbor_next roads
BOOST_ASSERT(!isRoadsPairMergeable(
detector, neighbor_geometries, neighbor_intersection_node, neighbor_prev));
// TODO: merge with an angle bisector, but not a reversed closed turn, to be
// checked as a difference with the previous implementation
const auto merge = findMergedBearing(
graph, neighbor_geometries, neighbor_curr, neighbor_next, true);
if (merge.first)
{
const auto offset = util::angularDeviation(
merge.second, neighbor_geometries[neighbor_curr].perceived_bearing);
// Adjust bearing of AB at the node A if at the node B roads BA (neighbor_curr)
// and BC (neighbor_next) will be merged and will have merged bearing Bb.
// The adjustment value is ∠bBA with positive sign (clockwise) to Aa
// a ~~~ A
// /
// --- B --- b
// \ 
// C
edge_geometry.perceived_bearing = edge_geometry.initial_bearing =
std::fmod(edge_geometry.perceived_bearing + offset, 360.);
}
}
}
}
// Add incoming edges with reversed bearings
edge_geometries.resize(2 * edges_number);
for (std::size_t index = 0; index < edges_number; ++index)
{
const auto &geometry = edge_geometries[index];
const auto remote_node = graph.GetTarget(geometry.edge);
const auto incoming_edge = graph.FindEdge(remote_node, intersection_node);
edge_geometries[edges_number + index] = {incoming_edge,
util::bearing::reverse(geometry.initial_bearing),
util::bearing::reverse(geometry.perceived_bearing),
geometry.length};
}
// Enforce ordering of edges by IDs
std::sort(edge_geometries.begin(), edge_geometries.end());
return std::make_pair(edge_geometries, merged_edge_ids);
}
inline auto findEdge(const IntersectionEdgeGeometries &geometries, const EdgeID &edge)
{
const auto it = std::lower_bound(
geometries.begin(), geometries.end(), edge, [](const auto &geometry, const auto edge) {
return geometry.edge < edge;
});
BOOST_ASSERT(it != geometries.end() && it->edge == edge);
return it;
}
double findEdgeBearing(const IntersectionEdgeGeometries &geometries, const EdgeID &edge)
{
return findEdge(geometries, edge)->perceived_bearing;
}
double findEdgeLength(const IntersectionEdgeGeometries &geometries, const EdgeID &edge)
{
return findEdge(geometries, edge)->length;
}
template <typename RestrictionsRange>
bool isTurnRestricted(const RestrictionsRange &restrictions, const NodeID to)
{
// Check turn restrictions to find a node that is the only allowed target when coming from a
// node to an intersection
// d
// |
// a - b - c and `only_straight_on ab | bc would return `c` for `a,b`
const auto is_only = std::find_if(restrictions.first,
restrictions.second,
[](const auto &pair) { return pair.second->is_only; });
if (is_only != restrictions.second)
return is_only->second->AsNodeRestriction().to != to;
// Check if explicitly forbidden
const auto no_turn =
std::find_if(restrictions.first, restrictions.second, [&to](const auto &restriction) {
return restriction.second->AsNodeRestriction().to == to;
});
return no_turn != restrictions.second;
}
bool isTurnAllowed(const util::NodeBasedDynamicGraph &graph,
const EdgeBasedNodeDataContainer &node_data_container,
const RestrictionMap &restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const IntersectionEdgeGeometries &geometries,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
const IntersectionEdge &from,
const IntersectionEdge &to)
{
BOOST_ASSERT(graph.GetTarget(from.edge) == to.node);
// TODO: to use TurnAnalysis all outgoing edges are required, to be removed later
if (graph.GetEdgeData(from.edge).reversed || graph.GetEdgeData(to.edge).reversed)
return false;
const auto intersection_node = to.node;
const auto destination_node = graph.GetTarget(to.edge);
auto const &restrictions = restriction_map.Restrictions(from.node, intersection_node);
// Check if turn is explicitly restricted by a turn restriction
if (isTurnRestricted(restrictions, destination_node))
return false;
// Precompute reversed bearing of the `from` edge
const auto from_edge_reversed_bearing =
util::bearing::reverse(findEdgeBearing(geometries, from.edge));
// Collect some information about the intersection
// 1) number of allowed exits and adjacent bidirectional edges
std::uint32_t allowed_exits = 0, bidirectional_edges = 0;
// 2) edge IDs of roundabouts edges
EdgeID roundabout_from = SPECIAL_EDGEID, roundabout_to = SPECIAL_EDGEID;
double roundabout_from_angle = 0., roundabout_to_angle = 0.;
for (const auto eid : graph.GetAdjacentEdgeRange(intersection_node))
{
const auto &edge_data = graph.GetEdgeData(eid);
const auto &edge_class = edge_data.flags;
const auto to_node = graph.GetTarget(eid);
const auto reverse_edge = graph.FindEdge(to_node, intersection_node);
BOOST_ASSERT(reverse_edge != SPECIAL_EDGEID);
const auto is_exit_edge = !edge_data.reversed && !isTurnRestricted(restrictions, to_node);
const auto is_bidirectional = !graph.GetEdgeData(reverse_edge).reversed;
allowed_exits += is_exit_edge;
bidirectional_edges += is_bidirectional;
if (edge_class.roundabout || edge_class.circular)
{
if (edge_data.reversed)
{
// "Linked Roundabouts" is an example of tie between two linked roundabouts
// A tie breaker for that maximizes ∠(roundabout_from_bearing, ¬from_edge_bearing)
const auto angle = util::bearing::angleBetween(
findEdgeBearing(geometries, reverse_edge), from_edge_reversed_bearing);
if (angle > roundabout_from_angle)
{
roundabout_from = reverse_edge;
roundabout_from_angle = angle;
}
}
else
{
// a tie breaker that maximizes ∠(¬from_edge_bearing, roundabout_to_bearing)
const auto angle = util::bearing::angleBetween(from_edge_reversed_bearing,
findEdgeBearing(geometries, eid));
if (angle > roundabout_to_angle)
{
roundabout_to = eid;
roundabout_to_angle = angle;
}
}
}
}
// 3) if the intersection has a barrier
const bool is_barrier_node = barrier_nodes.find(intersection_node) != barrier_nodes.end();
// Check a U-turn
if (from.node == destination_node)
{
// Allow U-turns before barrier nodes
if (is_barrier_node)
return true;
// Allow U-turns at dead-ends
if (graph.GetAdjacentEdgeRange(intersection_node).size() == 1)
return true;
// Allow U-turns at dead-ends if there is at most one bidirectional road at the intersection
// The condition allows U-turns d→a→d and c→b→c ("Bike - Around the Block" test)
// a→b
// ↕ ↕
// d↔c
if (allowed_exits == 1 || bidirectional_edges <= 1)
return true;
// Allow U-turn if the incoming edge has a U-turn lane
// TODO: revisit the use-case, related PR #2753
const auto &incoming_edge_annotation_id = graph.GetEdgeData(from.edge).annotation_data;
const auto lane_description_id = static_cast<std::size_t>(
node_data_container.GetAnnotation(incoming_edge_annotation_id).lane_description_id);
if (lane_description_id != INVALID_LANE_DESCRIPTIONID)
{
const auto &turn_lane_offsets = std::get<0>(turn_lanes_data);
const auto &turn_lanes = std::get<1>(turn_lanes_data);
BOOST_ASSERT(lane_description_id + 1 < turn_lane_offsets.size());
if (std::any_of(turn_lanes.begin() + turn_lane_offsets[lane_description_id],
turn_lanes.begin() + turn_lane_offsets[lane_description_id + 1],
[](const auto &lane) { return lane & guidance::TurnLaneType::uturn; }))
return true;
}
// Don't allow U-turns on usual intersections
return false;
}
// Don't allow turns via barriers for not U-turn maneuvers
if (is_barrier_node)
return false;
// Check for roundabouts exits in the opposite direction of roundabout flow
if (roundabout_from != SPECIAL_EDGEID && roundabout_to != SPECIAL_EDGEID)
{
// Get bearings of edges
const auto roundabout_from_bearing = findEdgeBearing(geometries, roundabout_from);
const auto roundabout_to_bearing = findEdgeBearing(geometries, roundabout_to);
const auto to_edge_bearing = findEdgeBearing(geometries, to.edge);
// Get angles from the roundabout edge to three other edges
const auto roundabout_angle =
util::bearing::angleBetween(roundabout_from_bearing, roundabout_to_bearing);
const auto roundabout_from_angle =
util::bearing::angleBetween(roundabout_from_bearing, from_edge_reversed_bearing);
const auto roundabout_to_angle =
util::bearing::angleBetween(roundabout_from_bearing, to_edge_bearing);
// Restrict turning over a roundabout if `roundabout_to_angle` is in
// a sector between `roundabout_from_bearing` to `from_bearing` (shaded area)
//
// roundabout_angle = 270° roundabout_angle = 90°
// roundabout_from_angle = 150° roundabout_from_angle = 150°
// roundabout_to_angle = 90° roundabout_to_angle = 270°
//
// 150° 150°
// v░░░░░░ ░░░░░░░░░v
// v░░░░░░░ ░░░░░░░░v
// 270° <-ooo- v -ttt-> 90° 270° <-ttt- v -ooo-> 90°
// ^░░░░░░░ ░░░░░░░^
// r░░░░░░░ ░░░░░░░r
// r░░░░░░░ ░░░░░░░r
if ((roundabout_from_angle < roundabout_angle &&
roundabout_to_angle < roundabout_from_angle) ||
(roundabout_from_angle > roundabout_angle &&
roundabout_to_angle > roundabout_from_angle))
return false;
}
return true;
}
// The function adapts intersection geometry data to TurnAnalysis
guidance::IntersectionView
convertToIntersectionView(const util::NodeBasedDynamicGraph &graph,
const EdgeBasedNodeDataContainer &node_data_container,
const RestrictionMap &restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const IntersectionEdgeGeometries &edge_geometries,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
const IntersectionEdge &incoming_edge,
const IntersectionEdges &outgoing_edges,
const std::unordered_set<EdgeID> &merged_edges)
{
using util::bearing::angleBetween;
const auto edge_it = findEdge(edge_geometries, incoming_edge.edge);
const auto incoming_bearing = edge_it->perceived_bearing;
const auto initial_incoming_bearing = edge_it->initial_bearing;
using IntersectionViewDataWithAngle = std::pair<guidance::IntersectionViewData, double>;
std::vector<IntersectionViewDataWithAngle> pre_intersection_view;
guidance::IntersectionViewData uturn{{SPECIAL_EDGEID, 0., 0.}, false, 0.};
std::size_t allowed_uturns_number = 0;
for (const auto &outgoing_edge : outgoing_edges)
{
const auto is_uturn = [](const auto angle) {
return std::fabs(angle) < std::numeric_limits<double>::epsilon();
};
const auto edge_it = findEdge(edge_geometries, outgoing_edge.edge);
const auto segment_length = edge_it->length;
const auto is_merged = merged_edges.count(outgoing_edge.edge) != 0;
const auto is_turn_allowed = intersection::isTurnAllowed(graph,
node_data_container,
restriction_map,
barrier_nodes,
edge_geometries,
turn_lanes_data,
incoming_edge,
outgoing_edge);
// Compute angles
const auto outgoing_bearing = edge_it->perceived_bearing;
const auto initial_outgoing_bearing = edge_it->initial_bearing;
auto turn_angle = std::fmod(
std::round(angleBetween(incoming_bearing, outgoing_bearing) * 1e8) / 1e8, 360.);
auto initial_angle = angleBetween(initial_incoming_bearing, initial_outgoing_bearing);
// If angle of the allowed turn is in a neighborhood of 0° (±15°) but the initial OSM angle
// is in the opposite semi-plane then assume explicitly a U-turn to avoid incorrect
// adjustments due to numerical noise in selection of representative_coordinate
if (is_turn_allowed &&
((turn_angle < 15 && initial_angle > 180) || (turn_angle > 345 && initial_angle < 180)))
{
turn_angle = 0;
initial_angle = 0;
}
const auto is_uturn_angle = is_uturn(turn_angle);
guidance::IntersectionViewData road{
{outgoing_edge.edge, outgoing_bearing, segment_length}, is_turn_allowed, turn_angle};
if (graph.GetTarget(outgoing_edge.edge) == incoming_edge.node)
{ // Save the true U-turn road to add later if no allowed U-turns will be added
uturn = road;
}
else if (is_turn_allowed || (!is_merged && !is_uturn_angle))
{ // Add roads that have allowed entry or not U-turns and not merged
allowed_uturns_number += is_uturn_angle;
// Adjust computed initial turn angle for non-U-turn road edge cases:
// 1) use 0° or 360° if the road has 0° initial angle
// 2) use turn angle if the smallest arc between turn and initial angles passes 0°
const auto use_turn_angle = (turn_angle > 270 && initial_angle < 90) ||
(turn_angle < 90 && initial_angle > 270);
const auto adjusted_angle = is_uturn(initial_angle)
? (turn_angle > 180. ? 360. : 0.)
: use_turn_angle ? turn_angle : initial_angle;
pre_intersection_view.push_back({road, adjusted_angle});
}
}
BOOST_ASSERT(uturn.eid != SPECIAL_EDGEID);
if (uturn.entry_allowed || allowed_uturns_number == 0)
{ // Add the true U-turn if it is allowed or no other U-turns found
pre_intersection_view.insert(pre_intersection_view.begin(), {uturn, 0});
}
// Order roads in counter-clockwise order starting from the U-turn edge in the OSM order
std::stable_sort(pre_intersection_view.begin(),
pre_intersection_view.end(),
[](const auto &lhs, const auto &rhs) {
return std::tie(lhs.second, lhs.first.angle) <
std::tie(rhs.second, rhs.first.angle);
});
// Adjust perceived bearings to keep the initial OSM order with respect to the first edge
for (auto curr = pre_intersection_view.begin(), next = std::next(curr);
next != pre_intersection_view.end();
++curr, ++next)
{
// Check that the perceived angles order is the same as the initial OSM one
if (next->first.angle < curr->first.angle)
{ // If the true bearing is out of the initial order (next before current) then
// adjust the next road angle to keep the order. The adjustment angle is at most
// 0.5° or a half-angle between the current angle and 360° to prevent overlapping
const auto angle_adjustment =
std::min(.5, util::restrictAngleToValidRange(360. - curr->first.angle) / 2.);
next->first.angle =
util::restrictAngleToValidRange(curr->first.angle + angle_adjustment);
}
}
// Copy intersection view data
guidance::IntersectionView intersection_view;
intersection_view.reserve(pre_intersection_view.size());
std::transform(pre_intersection_view.begin(),
pre_intersection_view.end(),
std::back_inserter(intersection_view),
[](const auto &road) { return road.first; });
return intersection_view;
}
// a
// |
// |
// v
// For an intersection from_node --via_eid--> turn_node ----> c
// ^
// |
// |
// b
// This functions returns _all_ turns as if the graph was undirected.
// That means we not only get (from_node, turn_node, c) in the above example
// but also (from_node, turn_node, a), (from_node, turn_node, b). These turns are
// marked as invalid and only needed for intersection classification.
template <bool USE_CLOSE_COORDINATE>
guidance::IntersectionView
getConnectedRoads(const util::NodeBasedDynamicGraph &graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &node_coordinates,
const extractor::CompressedEdgeContainer &compressed_geometries,
const RestrictionMap &node_restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
const IntersectionEdge &incoming_edge)
{
const auto intersection_node = graph.GetTarget(incoming_edge.edge);
const auto &outgoing_edges = intersection::getOutgoingEdges(graph, intersection_node);
auto edge_geometries = getIntersectionOutgoingGeometries<USE_CLOSE_COORDINATE>(
graph, compressed_geometries, node_coordinates, intersection_node);
// Add incoming edges with reversed bearings
const auto edges_number = edge_geometries.size();
edge_geometries.resize(2 * edges_number);
for (std::size_t index = 0; index < edges_number; ++index)
{
const auto &geometry = edge_geometries[index];
const auto remote_node = graph.GetTarget(geometry.edge);
const auto incoming_edge = graph.FindEdge(remote_node, intersection_node);
edge_geometries[edges_number + index] = {incoming_edge,
util::bearing::reverse(geometry.initial_bearing),
util::bearing::reverse(geometry.perceived_bearing),
geometry.length};
}
// Enforce ordering of edges by IDs
std::sort(edge_geometries.begin(), edge_geometries.end());
return convertToIntersectionView(graph,
node_data_container,
node_restriction_map,
barrier_nodes,
edge_geometries,
turn_lanes_data,
incoming_edge,
outgoing_edges,
std::unordered_set<EdgeID>());
}
template guidance::IntersectionView
getConnectedRoads<false>(const util::NodeBasedDynamicGraph &graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &node_coordinates,
const extractor::CompressedEdgeContainer &compressed_geometries,
const RestrictionMap &node_restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
const IntersectionEdge &incoming_edge);
template guidance::IntersectionView
getConnectedRoads<true>(const util::NodeBasedDynamicGraph &graph,
const EdgeBasedNodeDataContainer &node_data_container,
const std::vector<util::Coordinate> &node_coordinates,
const extractor::CompressedEdgeContainer &compressed_geometries,
const RestrictionMap &node_restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const guidance::TurnLanesIndexedArray &turn_lanes_data,
const IntersectionEdge &incoming_edge);
IntersectionEdge skipDegreeTwoNodes(const util::NodeBasedDynamicGraph &graph, IntersectionEdge road)
{
std::unordered_set<NodeID> visited_nodes;
(void)visited_nodes;
// Skip trivial nodes without generating the intersection in between, stop at the very first
// intersection of degree > 2
const auto starting_node = road.node;
auto next_node = graph.GetTarget(road.edge);
while (graph.GetOutDegree(next_node) == 2 && next_node != starting_node)
{
BOOST_ASSERT(visited_nodes.insert(next_node).second);
const auto next_edge = graph.BeginEdges(next_node);
road.edge = graph.GetTarget(next_edge) == road.node ? next_edge + 1 : next_edge;
road.node = next_node;
next_node = graph.GetTarget(road.edge);
}
return road;
}
}
}
}
+2 -2
View File
@@ -107,12 +107,12 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const
const std::string value(fi_begin->value());
// documented OSM restriction tags start either with only_* or no_*;
// check and return on these values, and ignore unrecognized values
// check and return on these values, and ignore no_*_on_red or unrecognized values
if (value.find("only_") == 0)
{
is_only_restriction = true;
}
else if (value.find("no_") == 0)
else if (value.find("no_") == 0 && !boost::algorithm::ends_with(value, "_on_red"))
{
is_only_restriction = false;
}
@@ -19,8 +19,6 @@
#include <osmium/osm.hpp>
#include <tbb/parallel_for.h>
#include <memory>
#include <sstream>
+2 -1
View File
@@ -76,8 +76,9 @@ NAN_MODULE_INIT(Engine::Init)
* @param {Number} [options.max_locations_viaroute] Max. locations supported in viaroute query (default: unlimited).
* @param {Number} [options.max_locations_distance_table] Max. locations supported in distance table query (default: unlimited).
* @param {Number} [options.max_locations_map_matching] Max. locations supported in map-matching query (default: unlimited).
* @param {Number} [options.max_radius_map_matching] Max. radius size supported in map matching query (default: 5).
* @param {Number} [options.max_results_nearest] Max. results supported in nearest query (default: unlimited).
* @param {Number} [options.max_alternatives] Max.number of alternatives supported in alternative routes query (default: 3).
* @param {Number} [options.max_alternatives] Max. number of alternatives supported in alternative routes query (default: 3).
*
* @class OSRM
*
-2
View File
@@ -2,8 +2,6 @@
#include "util/permutation.hpp"
#include <tbb/parallel_sort.h>
namespace osrm
{
namespace partition
+2 -1
View File
@@ -27,7 +27,8 @@ struct URLParser final : qi::grammar<Iterator, Into>
using boost::spirit::repository::qi::iter_pos;
alpha_numeral = qi::char_("a-zA-Z0-9");
percent_encoding = qi::char_('%') > qi::uint_parser<char, 16, 2, 2>()[qi::_val = qi::_1];
percent_encoding =
qi::char_('%') > qi::uint_parser<unsigned char, 16, 2, 2>()[qi::_val = qi::_1];
polyline_chars = qi::char_("a-zA-Z0-9_.--[]{}@?|\\~`^") | percent_encoding;
all_chars = polyline_chars | qi::char_("=,;:&().");
+4 -1
View File
@@ -133,7 +133,10 @@ inline unsigned generateServerProgramOptions(const int argc,
"Max. results supported in nearest query") //
("max-alternatives",
value<int>(&config.max_alternatives)->default_value(3),
"Max. number of alternatives supported in the MLD route query");
"Max. number of alternatives supported in the MLD route query") //
("max-matching-radius",
value<double>(&config.max_radius_map_matching)->default_value(5),
"Max. radius size supported in map matching query");
// hidden options, will be allowed on command line, but will not be shown to the user
boost::program_options::options_description hidden_options("Hidden options");
+3 -2
View File
@@ -33,10 +33,11 @@
#include <boost/interprocess/mapped_region.hpp>
#include <tbb/blocked_range.h>
#include <tbb/concurrent_unordered_map.h>
#include <tbb/concurrent_vector.h>
#include <tbb/enumerable_thread_specific.h>
#include <tbb/parallel_for_each.h>
#include <tbb/parallel_for.h>
#include <tbb/parallel_invoke.h>
#include <tbb/parallel_sort.h>
#include <algorithm>
#include <atomic>
+2 -1
View File
@@ -147,6 +147,8 @@
{"key": "maxspeed", "value": "AT:rural"},
{"key": "maxspeed", "value": "AT:trunk"},
{"key": "maxspeed", "value": "BE:motorway"},
{"key": "maxspeed", "value": "BY:urban"},
{"key": "maxspeed", "value": "BY:motorway"},
{"key": "maxspeed", "value": "CH:rural"},
{"key": "maxspeed", "value": "CH:trunk"},
{"key": "maxspeed", "value": "CH:motorway"},
@@ -170,7 +172,6 @@
{"key": "maxspeed", "value": "RU:living_street"},
{"key": "maxspeed", "value": "RU:urban"},
{"key": "maxspeed", "value": "RU:motorway"},
{"key": "maxspeed", "value": "UA:urban"},
{"key": "maxspeed", "value": "UK:nsl_single"},
{"key": "maxspeed", "value": "UK:nsl_dual"},
{"key": "maxspeed", "value": "UK:motorway"},

Some files were not shown because too many files have changed in this diff Show More