Compare commits

...

41 Commits

Author SHA1 Message Date
Patrick Niklaus 8e0b8e43d0 Bump verison to 5.8.1 2017-06-30 09:27:49 +00:00
Patrick Niklaus 43e67241a0 Update changelog 2017-06-30 09:27:08 +00:00
Moritz Kobitzsch 217d2afbb4 don't collapse u-turns into combined turns 2017-06-30 09:26:10 +00:00
Michael Krasnyk 6e9bb4ad75 Test case with incorrect u-turn collapsing
http://map.project-osrm.org/?z=18&center=52.169237%2C10.032722&loc=52.168628%2C10.030759&loc=52.168707%2C10.030952&loc=52.168457%2C10.030432&hl=en&alt=0
2017-06-30 09:26:01 +00:00
Daniel J. Hofmann 165dba8fcc Adapts obvious turn classification: no longer take entry_allowed into account, resolves #3987 2017-06-29 15:26:18 +00:00
Daniel J. Hofmann 2bd511f194 Adds a failing test case for obvious turn at the end of the road, see #3987 2017-06-29 15:26:09 +00:00
Patrick Niklaus 2d5ef1f4cf Update changelog 2017-06-29 15:25:27 +00:00
Michael Krasnyk 76457a423f Propagate lane data across traffic lights 2017-06-29 15:11:50 +00:00
Michael Krasnyk 9ec4348bd1 Test case for #4189 2017-06-29 15:11:41 +00:00
Michael Krasnyk 309d00c5e3 Correct condition for is_going_straight_and_turns_continue 2017-06-29 15:11:31 +00:00
Michael Krasnyk effc85fa07 Review adjustments for driveway handler, #4151 2017-06-29 15:08:36 +00:00
Michael Krasnyk e0eedbc5f6 Added driveway handler 2017-06-29 15:08:11 +00:00
Michael Krasnyk 655d03592f Use thread-safe lock-free assignment in PackedVector::set_value
PR uses TBB internal atomic's for atomic CAS on non-atomic data

Corresponding PR https://github.com/Project-OSRM/osrm-backend/pull/4199

Other options:

* use sequential update

* use an internal packed vector lock -> makes packed vector non-movable

* use boost.interprocess atomics implementation -> outdated and only 32 bit version

* use glib atomic's -> requires new dependency

*  wait for https://isocpp.org/blog/2014/05/n4013 as_atomic

*  use c11 _Atomic and atomic_compare_exchange_weak -> not possible to mix c++11 and c11

* use builtin functions gcc __sync_bool_compare_and_swap and msvc _InterlockedCompareExchange64 -> possible, but requires proper testing

boolean CompareAndSwapPointer(volatile * void * ptr,
                              void * new_value,
                              void * old_value) {
if defined(_MSC_VER)
   if (InterlockedCompareExchange(ptr, new_value, old_value) == old_value) return false;
   else return true;
elif (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) > 40100
   return __sync_bool_compare_and_swap(ptr, old_value, new_value);
else
  error No implementation
endif
}

* use Boost.Atomic -> requires new dependency

        WordT local_lower_word = lower_word, new_lower_word;
        do
        {
            new_lower_word = set_lower_value<WordT, T>(local_lower_word,
                                                       lower_mask[internal_index.element],
                                                       lower_offset[internal_index.element],
                                                       value);
        } while (!boost::atomics::detail::operations<sizeof(WordT), false>::compare_exchange_weak(
            lower_word,
            local_lower_word,
            new_lower_word,
            boost::memory_order_release,
            boost::memory_order_relaxed));
2017-06-27 13:05:31 +02:00
Patrick Niklaus 6fadc4d8b8 Enable travis 2017-06-19 16:03:36 +00:00
Patrick Niklaus 00230967af Change OSRM version to 5.8.0 2017-06-19 16:03:15 +00:00
Patrick Niklaus f4bf76c98e Refactor tile tests 2017-06-19 16:00:34 +00:00
Daniel Patterson 1142a9e49b Add layer to debug tiles to expose all OSM nodes in that area. 2017-06-19 16:00:25 +00:00
Patrick Niklaus b168eca6d5 Bump version 2017-06-15 16:10:37 +00:00
Daniel Patterson e3276324b9 Include 'rate' property (reciprocal of weight) on debug tile edges. (#4162)
Include 'rate' property (reciprocal of weight) on debug tile edges and add turn weight data to debug tiles.
2017-06-15 17:50:57 +02:00
Moritz Kobitzsch f80e5db346 add support for visualising turn penalties in MLD Debug tiles (#4157)
- template function for tile functionality with edge finder operator
 - refactors unit tests into single function (reduce code duplication)
 - adds unit tests for core-ch
2017-06-15 13:59:44 +02:00
Daniel Patterson 35550d8c0a Parallelize generation of the edge-expanded-edges. 2017-06-15 09:05:45 +00:00
Daniel J. Hofmann b68d79407e Takes fn by forwarding ref. in for_each_pair, resolves #4148 2017-06-14 15:17:26 +00:00
Patrick Niklaus 27ed69b08f Parallize scripting on osmium::Buffer granularity
Fixes #3447 and reduces parsing time by about 15%.
2017-06-13 21:47:54 +00:00
Daniel Patterson cd8fb82215 Add flag to allow skipping calling node function for nodes with no tags. 2017-06-13 12:23:00 +00:00
Daniel Patterson 5c8e2b6f78 Don't copy the node/way/segment/turn function objects for every call. 12-13% speedup for lua processing right there. 2017-06-13 12:23:00 +00:00
Stepan Kuzmin 9d30817294 add tests for the gaps and tidy map matching flags #4145 2017-06-12 11:32:06 +00:00
Stepan Kuzmin 5ee3c4de9f Expose Map Matching gaps and tidy parameters in Node.js bindings #4021 2017-06-12 11:32:06 +00:00
Michael Krasnyk afbcb3d38c Don't filter out MLD tests 2017-06-10 11:05:57 +00:00
Daniel J. Hofmann de1d5f199f Uses correct coefficients for local optimality in CH alternatives
The alpha constant is for the local optimality T-Test threshold.

Before we used epsilon for the T-Test threshold, but the epsilon
constant is meant to be used for the stretch test(s) only.

This changeset fixes the local optimality T-Test and uses the
epsilon constant for the two stretch tests:
- We test the stretch for the total route against epsilon and
- We test the detour against the epsilon now, too

We can discuss if the second stretch test should actually use
epsilon, too, or a adapted value of it - but definitly not alpha.
2017-06-09 13:20:41 +02:00
Michael Krasnyk 9158f69ea0 Failing test for alternative path 2017-06-09 13:20:41 +02:00
Daniel J. Hofmann 2cfd9c8d01 Adds npm5 lockfile 2017-06-08 21:58:09 +00:00
Daniel Patterson 5026741652 Make initialization fail with a specific exception if the dataset isn't compatible with the algorithm being used, rather than crashing when a query occurs. 2017-06-08 18:31:51 +00:00
Daniel Patterson 3d77714c36 Make most command-line tools return useful error codes on well-known exceptions. 2017-06-08 15:03:24 +00:00
Michael Krasnyk 03e83ec6a0 Fix invalid roundabout instructions for different driving modes, #4129 2017-06-08 15:01:27 +00:00
Michael Krasnyk 9315dc1c73 Test for a roundabout with footway exits, #4129 2017-06-08 15:01:27 +00:00
Patrick Niklaus b8bb12b2e2 Merge pull request #4134 from gojuno/polyline_input
Polyline6 support for the http REST API
2017-06-08 12:05:49 +00:00
Lev Dragunov bd1532847c Changelog entry 2017-06-07 21:46:19 +03:00
Lev Dragunov 3602b58517 Review fixes 2017-06-07 21:43:49 +03:00
Lev Dragunov d7035291ea clang-format 2017-06-07 15:39:08 +03:00
Lev Dragunov 6b0bcb5171 Docs and tests 2017-06-07 15:24:00 +03:00
Lev Dragunov 6b8f3c7fef Polyline6 support in the REST input 2017-06-06 19:51:00 +03:00
88 changed files with 8971 additions and 819 deletions
+1
View File
@@ -17,6 +17,7 @@ notifications:
branches:
only:
- master
- "5.8"
# enable building tags
- /^v\d+\.\d+(\.\d+)?(-\S*)?$/
+19
View File
@@ -1,6 +1,16 @@
# 5.8.1
- Changes from 5.8.0:
- Bugfixes:
- Fixes #4152: Superflous turn left instruction, when crossing a service-road.
- Fixes #4189: Fixes missing turn lane information after a traffic light.
- Fixes #4199: Data race-condition when updating segment speeds.
- Fixes #3987: Fixes obvious turn detection at T-intersections with slip-roads.
- Fixes #4161: Don't collapse U-Turns with other steps.
# 5.8.0
- Changes from 5.7
- API:
- polyline6 support in request string
- new parameter `approaches` for `route`, `table`, `trip` and `nearest` requests. This parameter keep waypoints on the curb side.
'approaches' accepts both 'curb' and 'unrestricted' values.
Note : the curb side depend on the `ProfileProperties::left_hand_driving`, it's a global property set once by the profile. If you are working with a planet dataset, the api will be wrong in some countries, and right in others.
@@ -10,13 +20,22 @@
- `osrm-partition` now ensures it is called before `osrm-contract` and removes inconsitent .hsgr files automatically.
- Features
- Added conditional restriction support with `parse-conditional-restrictions=true|false` to osrm-extract. This option saves conditional turn restrictions to the .restrictions file for parsing by contract later. Added `parse-conditionals-from-now=utc time stamp` and `--time-zone-file=/path/to/file` to osrm-contract
- Command-line tools (osrm-extract, osrm-contract, osrm-routed, etc) now return error codes and legible error messages for common problem scenarios, rather than ugly C++ crashes
- Speed up pre-processing by only running the Lua `node_function` for nodes that have tags. Cuts OSM file parsing time in half.
- osrm-extract now performs generation of edge-expanded-edges using all available CPUs, which should make osrm-extract significantly faster on multi-CPU machines
- Files
- .osrm.nodes file was renamed to .nbg_nodes and .ebg_nodes was added
- Guidance
- #4075 Changed counting of exits on service roundabouts
- Debug Tiles
- added support for visualising turn penalties to the MLD plugin
- added support for showing the rate (reciprocal of weight) on each edge when used
- added support for turn weights in addition to turn durations in debug tiles
- Bugfixes
- Fixed a copy/paste issue assigning wrong directions in similar turns (left over right)
- #4074: fixed a bug that would announce entering highway ramps as u-turns
- #4122: osrm-routed/libosrm should throw exception when a dataset incompatible with the requested algorithm is loaded
- Avoid collapsing u-turns into combined turn instructions
# 5.7.1
- Bugfixes
+2 -1
View File
@@ -55,7 +55,7 @@ endif()
project(OSRM C CXX)
set(OSRM_VERSION_MAJOR 5)
set(OSRM_VERSION_MINOR 8)
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}")
@@ -126,6 +126,7 @@ file(GLOB UpdaterGlob src/updater/*.cpp)
file(GLOB StorageGlob src/storage/*.cpp)
file(GLOB ServerGlob src/server/*.cpp src/server/**/*.cpp)
file(GLOB EngineGlob src/engine/*.cpp src/engine/**/*.cpp)
file(GLOB ErrorcodesGlob src/osrm/errorcodes.cpp)
add_library(UTIL OBJECT ${UtilGlob})
add_library(EXTRACTOR OBJECT ${ExtractorGlob})
+1 -1
View File
@@ -3,5 +3,5 @@ module.exports = {
verify: '--strict --tags ~@stress --tags ~@todo -f progress --require features/support --require features/step_definitions',
todo: '--strict --tags @todo --require features/support --require features/step_definitions',
all: '--strict --require features/support --require features/step_definitions',
mld: '--strict --tags ~@stress --tags ~@todo --tags ~@alternative --tags ~@matrix --tags ~@trip --tags --require features/support --require features/step_definitions -f progress'
mld: '--strict --tags ~@stress --tags ~@todo --tags ~@alternative --tags ~@matrix --tags ~@trip --require features/support --require features/step_definitions -f progress'
}
+5 -2
View File
@@ -15,7 +15,7 @@ GET /{service}/{version}/{profile}/{coordinates}[.{format}]?option=value&option=
| `service` | One of the following values: [`route`](#route-service), [`nearest`](#nearest-service), [`table`](#table-service), [`match`](#match-service), [`trip`](#trip-service), [`tile`](#tile-service) |
| `version` | Version of the protocol implemented by the service. `v1` for all OSRM 5.x installations |
| `profile` | Mode of transportation, is determined statically by the Lua profile that is used to prepare the data using `osrm-extract`. Typically `car`, `bike` or `foot` if using one of the supplied profiles. |
| `coordinates`| String of format `{longitude},{latitude};{longitude},{latitude}[;{longitude},{latitude} ...]` or `polyline({polyline})`. |
| `coordinates`| String of format `{longitude},{latitude};{longitude},{latitude}[;{longitude},{latitude} ...]` or `polyline({polyline}) or polyline6({polyline6})`. |
| `format`| Only `json` is supported at the moment. This parameter is optional and defaults to `json`. |
Passing any `option=value` is optional. `polyline` follows Google's polyline format with precision 5 by default and can be generated using [this package](https://www.npmjs.com/package/polyline).
@@ -419,8 +419,10 @@ Vector tiles contain two layers:
| `speed` | `integer` | the speed on that road segment, in km/h |
| `is_small` | `boolean` | whether this segment belongs to a small (< 1000 node) [strongly connected component](https://en.wikipedia.org/wiki/Strongly_connected_component) |
| `datasource` | `string` | the source for the speed value (normally `lua profile` unless you're using the [traffic update feature](https://github.com/Project-OSRM/osrm-backend/wiki/Traffic), in which case it contains the stem of the filename that supplied the speed value for this segment |
| `duration` | `float` | how long this segment takes to traverse, in seconds |
| `duration` | `float` | how long this segment takes to traverse, in seconds. This value is to calculate the total route ETA. |
| `weight ` | `integer` | how long this segment takes to traverse, in units (may differ from `duration` when artificial biasing is applied in the Lua profiles). ACTUAL ROUTING USES THIS VALUE. |
| `name` | `string` | the name of the road this segment belongs to |
| `rate` | `float` | the value of `length/weight` - analagous to `speed`, but using the `weight` value rather than `duration`, rounded to the nearest integer |
`turns` layer:
@@ -429,6 +431,7 @@ Vector tiles contain two layers:
| `bearing_in` | `integer` | the absolute bearing that approaches the intersection. -180 to +180, 0 = North, 90 = East |
| `turn_angle` | `integer` | the angle of the turn, relative to the `bearing_in`. -180 to +180, 0 = straight ahead, 90 = 90-degrees to the right |
| `cost` | `float` | the time we think it takes to make that turn, in seconds. May be negative, depending on how the data model is constructed (some turns get a "bonus"). |
| `weight` | `float` | the weight we think it takes to make that turn. May be negative, depending on how the data model is constructed (some turns get a "bonus"). ACTUAL ROUTING USES THIS VALUE |
## Result objects
+2
View File
@@ -194,6 +194,8 @@ if they can not be matched successfully.
- `options.overview` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** Add overview geometry either `full`, `simplified` according to highest zoom level it could be display on, or not at all (`false`). (optional, default `simplified`)
- `options.timestamps` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)&lt;[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)>?** Timestamp of the input location (integers, UNIX-like timestamp).
- `options.radiuses` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)?** Standard deviation of GPS precision used for map matching. If applicable use GPS accuracy. Can be `null` for default value `5` meters or `double >= 0`.
- `options.gaps` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** Allows the input track splitting based on huge timestamp gaps between points. Either `split` or `ignore`. (optional, default `split`)
- `options.tidy` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** Allows the input track modification to obtain better matching quality for noisy tracks. (optional, default `false`)
- `callback` **[Function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function)**
**Examples**
+4 -4
View File
@@ -35,10 +35,10 @@ Feature: Turn Lane Guidance
| restriction | bc | cd | c | no_right_turn |
When I route I should get
| waypoints | route | turns | lanes |
| a,e | in,cross,cross | depart,turn left,arrive | ,left:true straight:false right:false, |
| a,g | in,straight,straight | depart,new name straight,arrive | ,left:false straight:true right:false, |
| a,f | in,cross,cross | depart,turn right,arrive | ,left:false straight:false right:true, |
| waypoints | route | turns | lanes |
| a,e | in,cross,cross | depart,turn left,arrive | ,left:true straight:false, |
| a,g | in,straight,straight | depart,new name straight,arrive | ,left:false straight:true, |
| a,f | in,cross,cross | depart,continue right,arrive | ,, |
@sliproads
Scenario: Separate Turn Lanes
+29 -10
View File
@@ -359,7 +359,7 @@ Feature: Collapse
| a,g | first,second,second | depart,turn left,arrive | a,b,g |
| d,g | first,second,second | depart,turn right,arrive | d,e,g |
| g,f | second,first,first | depart,turn right,arrive | g,e,f |
| g,c | second,first,first | depart,end of road left,arrive | g,b,c |
| g,c | second,first,first | depart,turn left,arrive | g,e,c |
Scenario: Do not collapse turning roads
Given the node map
@@ -399,7 +399,7 @@ Feature: Collapse
| waypoints | route | turns |
| a,d | , | depart,arrive |
# This scenario could be considered to require a `turn left`. The danger to create random/unwanted instructions
# This scenario could be considered to require a `turn left`. The danger to create random/unwanted instructions
# from a setting like this are just to big, though. Therefore I opted to use `depart,arrive` only
Scenario: Crossing Bridge into Segregated Turn
Given the node map
@@ -690,9 +690,9 @@ Feature: Collapse
| restriction | bc | dc | c | no_right_turn |
When I route I should get
| waypoints | route | turns | locations |
| a,g | road,cross,cross | depart,turn left,arrive | a,b,g |
| a,e | road,road | depart,arrive | a,e |
| waypoints | route | turns | locations |
| a,g | road,road,cross,cross | depart,continue slight left,turn left,arrive | a,b,c,g |
| a,e | road,road | depart,arrive | a,e |
Scenario: On-Off on Highway
Given the node map
@@ -996,8 +996,8 @@ Feature: Collapse
a . . b .'
` d.
f e
"""
#Check collapse.detail for a similar case (shorter) that does not classify these turns as a sliproad anymore
"""
#Check collapse.detail for a similar case (shorter) that does not classify these turns as a sliproad anymore
And the ways
| nodes | name | oneway | highway |
@@ -1016,9 +1016,9 @@ Feature: Collapse
When I route I should get
| waypoints | route | turns | locations |
| a,g | road,cross,cross | depart,fork left,arrive | a,b,g |
| a,e | road,road,road | depart,fork slight right,arrive | a,b,e |
| a,f | road,road,cross,cross | depart,fork slight right,turn right,arrive | a,b,d,f |
| a,g | road,road,cross,cross | depart,fork slight left,turn left,arrive | a,b,c,g |
| a,e | road,road,road | depart,fork slight right,arrive | a,b,e |
| a,f | road,road,cross,cross | depart,fork slight right,turn right,arrive | a,b,d,f |
# http://www.openstreetmap.org/way/92415447 #3933
@@ -1055,3 +1055,22 @@ Feature: Collapse
When I route I should get
| waypoints | route | turns | locations |
| a,i | President Avenue,Princes Highway,Princes Highway | depart,turn left,arrive | a,b,i |
Scenario: Don't combine uturns
Given the node map
"""
2 d
a - - b - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - c
1
"""
And the ways
| nodes | highway |
| ab | tertiary |
| bc | tertiary |
| bd | service |
When I route I should get
| waypoints | bearings | route | turns | locations |
| 1,2 | 90 270 | ab,bd,bd,ab,ab | depart,turn left,continue uturn,turn right,arrive | _,b,d,b,_ |
+48
View File
@@ -0,0 +1,48 @@
@driveway @guidance
Feature: Driveways intersections
Background:
Given the profile "car"
Given a grid size of 5 meters
Scenario: Road with a turn to service road
Given the node map
"""
a
~.
b----c----d
|
e
"""
And the ways
| nodes | highway | name | oneway |
| abc | trunk | first | yes |
| cd | trunk | second | yes |
| be | service | parking | yes |
When I route I should get
| waypoints | route | turns | locations |
| a,d | first,second | depart,arrive | a,d |
Scenario: Turn Instead of Ramp
Given the node map
"""
a
~.
b----c----d
|
e
"""
And the ways
| nodes | highway | name | oneway |
| ab | trunk | | yes |
| bc | trunk | | yes |
| cd | trunk | second | yes |
| be | service | parking | yes |
When I route I should get
| waypoints | route | turns | locations |
| a,d | ,second | depart,arrive | a,d |
@@ -37,3 +37,37 @@ Feature: Basic Roundabout
| h,a | gh,ab,ab | depart,roundabout turn left exit-3,arrive |
| h,d | gh,cd,cd | depart,roundabout turn straight exit-2,arrive |
| h,f | gh,ef,ef | depart,roundabout turn right exit-1,arrive |
# https://www.openstreetmap.org/way/223225602
Scenario: Enter and Exit with changing mode
Given the node map
"""
a
b
h g c d
e
f
"""
And the ways
| nodes | junction | highway |
| ab | | residential |
| cd | | residential |
| ef | | footway |
| gh | | footway |
| bgecb | roundabout | residential |
When I route I should get
| waypoints | route | turns |
| a,d | ab,cd,cd | depart,roundabout turn left exit-1,arrive |
| a,f | ab,ef,ef,ef | depart,roundabout turn left exit-1,notification right,arrive |
| a,h | ab,bgecb,gh,gh | depart,roundabout turn right exit-1,notification right,arrive |
| d,f | cd,ef,ef,ef | depart,roundabout turn sharp left exit-2,notification right,arrive |
| d,h | cd,gh,gh,gh | depart,roundabout turn left exit-2,notification right,arrive |
| d,a | cd,ab,ab | depart,roundabout turn right exit-1,arrive |
| f,h | ef,gh,gh,gh | depart,roundabout turn sharp left exit-3,notification right,arrive |
| f,a | ef,ab,ab | depart,roundabout turn straight exit-2,arrive |
| f,d | ef,cd,cd | depart,roundabout turn right exit-1,arrive |
| h,a | gh,ab,ab | depart,roundabout turn left exit-2,arrive |
| h,d | gh,cd,cd | depart,roundabout turn straight exit-1,arrive |
| h,f | gh,bgecb,ef,ef | depart,roundabout turn right exit-1,notification right,arrive |
+10 -10
View File
@@ -201,14 +201,14 @@ Feature: Simple Turns
| ef | residential | road | 2 | yes |
When I route I should get
| waypoints | route | turns | locations |
| a,c | road,road | depart,arrive | a,c |
| c,a | road,road | depart,arrive | c,a |
| g,a | turn,road,road | depart,turn left,arrive | g,b,a |
| g,c | turn,road,road | depart,turn right,arrive | g,b,c |
| g,f | turn,road,road | depart,turn left,arrive | g,e,f |
| c,f | road,road,road | depart,continue right,arrive | c,b,f |
| a,f | road,road,road | depart,continue uturn,arrive | a,b,f |
| waypoints | route | turns | locations |
| a,c | road,road | depart,arrive | a,c |
| c,a | road,road | depart,arrive | c,a |
| g,a | turn,road,road | depart,turn left,arrive | g,b,a |
| g,c | turn,road,road | depart,turn right,arrive | g,b,c |
| g,f | turn,road,road | depart,end of road left,arrive | g,e,f |
| c,f | road,road,road | depart,turn right,arrive | c,b,f |
| a,f | road,road,road | depart,continue uturn,arrive | a,b,f |
# http://www.openstreetmap.org/#map=19/52.48753/13.52838
Scenario: Traffic Circle
@@ -1312,8 +1312,8 @@ Feature: Simple Turns
# we don't care for turn instructions, this is a coordinate extraction bug check
When I route I should get
| waypoints | route | intersections |
| a,g | ab,bcdefgh | true:90,true:45 false:180 false:270;true:180 |
| waypoints | route | intersections |
| a,g | ab,bcdefgh,bcdefgh | true:90;true:45 false:180 false:270;true:180 |
#https://github.com/Project-OSRM/osrm-backend/pull/3469#issuecomment-270806580
Scenario: Oszillating Lower Priority Road
+29
View File
@@ -1215,3 +1215,32 @@ Feature: Turn Lane Guidance
| waypoints | route | turns | lanes |
| a,d | road,cross,cross | depart,turn left,arrive | ,none:true none:false right:false, |
| a,c | road,road | depart,arrive | , |
@4189
Scenario: U-turn after a traffic light
Given the node map
"""
j k
: :
f---g-h-i
: :
a-b-c-d-e
: :
l m
"""
And the nodes
| node | highway |
| b | traffic_signals |
And the ways
| nodes | name | lanes | turn:lanes | oneway |
| ab | road1 | 3 | left\|through\|through;right | yes |
| bcde | road1 | 2 | | yes |
| ihgf | road1 | 2 | | yes |
| jgcl | road2 | 2 | | yes |
| mdhk | road2 | 2 | | yes |
When I route I should get
| waypoints | route | turns | lanes | locations |
| a,f | road1,road1,road1 | depart,continue uturn,arrive | ,left:true straight:false straight;right:false, | a,d,f |
+30
View File
@@ -1319,3 +1319,33 @@ Feature: Simple Turns
When I route I should get
| waypoints | route | ref | turns |
| a,h | Road,,, | C 42,,C 42,C 42 | depart,on ramp right,merge slight left,arrive |
Scenario: End of road, T-intersection, no obvious turn, only one road allowed
Given the node map
"""
d
.
a . b . . c
' .
'e
.
f
"""
And the ways
| nodes | highway | oneway | ref |
| ab | primary | | B 191 |
| bc | primary | | B 191 |
| be | primary_link | yes | |
| dc | primary | | B 4;B 191 |
| ce | primary | | B 4 |
| ef | primary | | B 4 |
And the relations
| type | way:from | way:to | node:via | restriction |
| restriction | bc | ce | c | no_right_turn |
| restriction | be | ef | e | only_right_turn |
When I route I should get
| waypoints | route | turns |
| a,d | ab,dc,dc | depart,turn left,arrive |
+1 -1
View File
@@ -14,5 +14,5 @@ Feature: osrm-routed command line options: invalid options
Scenario: osrm-routed - Missing file
When I try to run "osrm-routed over-the-rainbow.osrm"
Then stderr should contain "over-the-rainbow.osrm"
And stderr should contain "not found"
And stderr should contain "Required files are missing"
And it should exit with an error
+35
View File
@@ -30,3 +30,38 @@ Feature: Alternative route
| 3 | 4 | bd,dc,ca,ab,bd,bd | |
| 5 | 6 | dc,ca,ab,bd,dc,dc | |
| 7 | 8 | ca,ab,bd,dc,ca,ca | |
@4111
Scenario: Alternative Loop Paths with single node path
Given the node map
"""
a1b2c3d
e f
"""
And the ways
| nodes | maxspeed |
| ab | 30 |
| bc | 3 |
| cd | 30 |
| ae | 30 |
| ef | 30 |
| fd | 30 |
And the query options
| alternatives | true |
When I route I should get
| from | to | route | alternative |
| b | c | bc,bc | ab,ae,ef,fd,cd,cd |
#| c | b | bc,bc | cd,fd,ef,ae,ab,ab | # alternative path depends on phantom snapping order
| 1 | c | ab,bc,bc | ab,ae,ef,fd,cd,cd |
#| c | 1 | bc,ab | cd,fd,ef,ae,ab | # alternative path depends on phantom snapping order
| 2 | c | bc,bc | |
| c | 2 | bc,bc | |
| 1 | 3 | ab,ae,ef,fd,cd | ab,bc,cd |
#| 3 | 1 | cd,fd,ef,ae,ab | cd,bc,ab | # alternative path depends on phantom snapping order
| b | 3 | bc,cd | ab,ae,ef,fd,cd |
#| 3 | b | cd,bc,bc | cd,fd,ef,ae,ab,ab | # alternative path depends on phantom snapping order
+3
View File
@@ -102,6 +102,9 @@ template <> struct HasShortestPathSearch<mld::Algorithm> final : std::true_type
template <> struct HasMapMatching<mld::Algorithm> final : std::true_type
{
};
template <> struct HasGetTileTurns<mld::Algorithm> final : std::true_type
{
};
}
}
}
+34 -10
View File
@@ -159,12 +159,12 @@ bool Engine<routing_algorithms::ch::Algorithm>::CheckCompability(const EngineCon
}
else
{
std::ifstream in(config.storage_config.hsgr_data_path.string().c_str());
if (!in)
if (!boost::filesystem::exists(config.storage_config.hsgr_data_path))
return false;
storage::io::FileReader in(config.storage_config.hsgr_data_path,
storage::io::FileReader::VerifyFingerprint);
in.seekg(0, std::ios::end);
auto size = in.tellg();
auto size = in.GetSize();
return size > 0;
}
}
@@ -190,14 +190,38 @@ bool Engine<routing_algorithms::corech::Algorithm>::CheckCompability(const Engin
}
else
{
std::ifstream in(config.storage_config.core_data_path.string().c_str());
if (!in)
if (!boost::filesystem::exists(config.storage_config.core_data_path))
return false;
storage::io::FileReader in(config.storage_config.core_data_path,
storage::io::FileReader::VerifyFingerprint);
in.seekg(0, std::ios::end);
std::size_t size = in.tellg();
// An empty core files is only the 8 byte size header plus the 8 byte Fingerprint.
return size > sizeof(std::uint64_t) + sizeof(util::FingerPrint);
auto size = in.GetSize();
return size > sizeof(std::uint64_t);
}
}
template <>
bool Engine<routing_algorithms::mld::Algorithm>::CheckCompability(const EngineConfig &config)
{
if (config.use_shared_memory)
{
storage::SharedMonitor<storage::SharedDataTimestamp> barrier;
using mutex_type = typename decltype(barrier)::mutex_type;
boost::interprocess::scoped_lock<mutex_type> current_region_lock(barrier.get_mutex());
auto mem = storage::makeSharedMemory(barrier.data().region);
auto layout = reinterpret_cast<storage::DataLayout *>(mem->Ptr());
return layout->GetBlockSize(storage::DataLayout::MLD_PARTITION) > 0;
}
else
{
if (!boost::filesystem::exists(config.storage_config.mld_partition_path))
return false;
storage::io::FileReader in(config.storage_config.mld_partition_path,
storage::io::FileReader::VerifyFingerprint);
auto size = in.GetSize();
return size > 0;
}
}
}
+26 -4
View File
@@ -14,10 +14,9 @@ namespace engine
{
namespace detail
{
constexpr double POLYLINE_DECODING_PRECISION = 1e5;
constexpr double POLYLINE_TO_COORDINATE = COORDINATE_PRECISION / POLYLINE_DECODING_PRECISION;
std::string encode(std::vector<int> &numbers);
std::int32_t decode_polyline_integer(std::string::const_iterator &first,
std::string::const_iterator last);
}
using CoordVectorForwardIter = std::vector<util::Coordinate>::const_iterator;
// Encodes geometry into polyline format.
@@ -57,7 +56,30 @@ std::string encodePolyline(CoordVectorForwardIter begin, CoordVectorForwardIter
// Decodes geometry from polyline format
// See: https://developers.google.com/maps/documentation/utilities/polylinealgorithm
std::vector<util::Coordinate> decodePolyline(const std::string &polyline);
template <unsigned POLYLINE_PRECISION = 100000>
std::vector<util::Coordinate> decodePolyline(const std::string &polyline)
{
double polyline_to_coordinate = COORDINATE_PRECISION / POLYLINE_PRECISION;
std::vector<util::Coordinate> coordinates;
std::int32_t latitude = 0, longitude = 0;
std::string::const_iterator first = polyline.begin();
const std::string::const_iterator last = polyline.end();
while (first != last)
{
const auto dlat = detail::decode_polyline_integer(first, last);
const auto dlon = detail::decode_polyline_integer(first, last);
latitude += dlat;
longitude += dlon;
coordinates.emplace_back(util::Coordinate{
util::FixedLongitude{static_cast<std::int32_t>(longitude * polyline_to_coordinate)},
util::FixedLatitude{static_cast<std::int32_t>(latitude * polyline_to_coordinate)}});
}
return coordinates;
}
}
}
-9
View File
@@ -221,15 +221,6 @@ RoutingAlgorithms<routing_algorithms::mld::Algorithm>::ManyToManySearch(
{
throw util::exception("ManyToManySearch is not implemented");
}
template <>
inline std::vector<routing_algorithms::TurnData>
RoutingAlgorithms<routing_algorithms::mld::Algorithm>::GetTileTurns(
const std::vector<datafacade::BaseDataFacade::RTreeLeaf> &,
const std::vector<std::size_t> &) const
{
throw util::exception("GetTileTurns is not implemented");
}
}
}
@@ -33,6 +33,11 @@ getTileTurns(const datafacade::ContiguousInternalMemoryDataFacade<ch::Algorithm>
const std::vector<RTreeLeaf> &edges,
const std::vector<std::size_t> &sorted_edge_indexes);
std::vector<TurnData>
getTileTurns(const datafacade::ContiguousInternalMemoryDataFacade<mld::Algorithm> &facade,
const std::vector<RTreeLeaf> &edges,
const std::vector<std::size_t> &sorted_edge_indexes);
} // namespace routing_algorithms
} // namespace engine
} // namespace osrm
@@ -18,6 +18,7 @@
#include "extractor/query_node.hpp"
#include "extractor/restriction_map.hpp"
#include "util/concurrent_id_map.hpp"
#include "util/deallocating_vector.hpp"
#include "util/guidance/bearing_class.hpp"
#include "util/guidance/entry_class.hpp"
@@ -41,6 +42,9 @@
#include <boost/filesystem/fstream.hpp>
#include <tbb/concurrent_unordered_map.h>
#include <tbb/concurrent_vector.h>
namespace osrm
{
namespace extractor
@@ -167,9 +171,9 @@ class EdgeBasedGraphFactory
std::size_t skipped_uturns_counter;
std::size_t skipped_barrier_turns_counter;
std::unordered_map<util::guidance::BearingClass, BearingClassID> bearing_class_hash;
util::ConcurrentIDMap<util::guidance::BearingClass, BearingClassID> bearing_class_hash;
std::vector<BearingClassID> bearing_class_by_node_based_node;
std::unordered_map<util::guidance::EntryClass, EntryClassID> entry_class_hash;
util::ConcurrentIDMap<util::guidance::EntryClass, EntryClassID> entry_class_hash;
};
} // namespace extractor
} // namespace osrm
@@ -0,0 +1,40 @@
#ifndef OSRM_EXTRACTOR_GUIDANCE_DRIVEWAY_HANDLER_HPP
#define OSRM_EXTRACTOR_GUIDANCE_DRIVEWAY_HANDLER_HPP
#include "extractor/guidance/intersection_handler.hpp"
namespace osrm
{
namespace extractor
{
namespace guidance
{
// Intersection handlers deal with all issues related to intersections.
class DrivewayHandler final : public IntersectionHandler
{
public:
DrivewayHandler(const IntersectionGenerator &intersection_generator,
const util::NodeBasedDynamicGraph &node_based_graph,
const std::vector<util::Coordinate> &coordinates,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table);
~DrivewayHandler() override final = default;
// check whether the handler can actually handle the intersection
bool canProcess(const NodeID nid,
const EdgeID via_eid,
const Intersection &intersection) const override final;
// process the intersection
Intersection operator()(const NodeID nid,
const EdgeID via_eid,
Intersection intersection) const override final;
};
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif /* OSRM_EXTRACTOR_GUIDANCE_DRIVEWAY_HANDLER_HPP */
@@ -27,7 +27,6 @@ namespace guidance
{
// Intersection handlers deal with all issues related to intersections.
// They assign appropriate turn operations to the TurnOperations.
// This base class provides both the interface and implementations for
// common functions.
class IntersectionHandler
@@ -114,6 +113,8 @@ class IntersectionHandler
// For this scenario returns intersection at `b` and `b`.
boost::optional<IntersectionHandler::IntersectionViewAndNode>
getNextIntersection(const NodeID at, const EdgeID via) const;
bool isSameName(const EdgeID source_edge_id, const EdgeID target_edge_id) const;
};
// Impl.
@@ -410,14 +411,11 @@ std::size_t IntersectionHandler::findObviousTurn(const EdgeID via_edge,
const auto &best_option_data =
node_based_graph.GetEdgeData(intersection[best_option].eid);
const auto adjusted_distinction_ratio = [&]() {
// not allowed competitors are easily distinct
if (!intersection[index].entry_allowed)
return 0.7 * DISTINCTION_RATIO;
// a bit less obvious are road classes
else if (in_way_data.road_classification == best_option_data.road_classification &&
best_option_data.road_classification.GetPriority() <
node_based_graph.GetEdgeData(intersection[index].eid)
.road_classification.GetPriority())
// obviousness by road classes
if (in_way_data.road_classification == best_option_data.road_classification &&
best_option_data.road_classification.GetPriority() <
node_based_graph.GetEdgeData(intersection[index].eid)
.road_classification.GetPriority())
return 0.8 * DISTINCTION_RATIO;
// if road classes are the same, we use the full ratio
else
@@ -20,7 +20,6 @@ namespace guidance
{
// Intersection handlers deal with all issues related to intersections.
// They assign appropriate turn operations to the TurnOperations.
class MotorwayHandler : public IntersectionHandler
{
public:
@@ -21,7 +21,6 @@ namespace guidance
{
// Intersection handlers deal with all issues related to intersections.
// They assign appropriate turn operations to the TurnOperations.
class SliproadHandler final : public IntersectionHandler
{
public:
@@ -2,6 +2,7 @@
#define OSRM_EXTRACTOR_TURN_ANALYSIS
#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"
@@ -87,6 +88,7 @@ class TurnAnalysis
const TurnHandler turn_handler;
const SliproadHandler sliproad_handler;
const SuppressModeHandler suppress_mode_handler;
const DrivewayHandler driveway_handler;
// Utility function, setting basic turn types. Prepares for normal turn handling.
Intersection
@@ -24,7 +24,6 @@ namespace guidance
{
// Intersection handlers deal with all issues related to intersections.
// They assign appropriate turn operations to the TurnOperations.
class TurnHandler : public IntersectionHandler
{
public:
@@ -65,12 +65,16 @@ const constexpr char *scenario_names[] = {"Simple",
class TurnLaneHandler
{
using UpgradableMutex = boost::interprocess::interprocess_upgradable_mutex;
using ScopedReaderLock = boost::interprocess::sharable_lock<UpgradableMutex>;
using ScopedWriterLock = boost::interprocess::scoped_lock<UpgradableMutex>;
public:
typedef std::vector<TurnLaneData> LaneDataVector;
TurnLaneHandler(const util::NodeBasedDynamicGraph &node_based_graph,
std::vector<std::uint32_t> &turn_lane_offsets,
std::vector<TurnLaneType::Mask> &turn_lane_masks,
const std::vector<std::uint32_t> &turn_lane_offsets,
const std::vector<TurnLaneType::Mask> &turn_lane_masks,
LaneDescriptionMap &lane_description_map,
const TurnAnalysis &turn_analysis,
util::guidance::LaneDataIdMap &id_map);
@@ -86,8 +90,8 @@ class TurnLaneHandler
// we need to be able to look at previous intersections to, in some cases, find the correct turn
// lanes for a turn
const util::NodeBasedDynamicGraph &node_based_graph;
std::vector<std::uint32_t> &turn_lane_offsets;
std::vector<TurnLaneType::Mask> &turn_lane_masks;
const std::vector<std::uint32_t> &turn_lane_offsets;
const std::vector<TurnLaneType::Mask> &turn_lane_masks;
LaneDescriptionMap &lane_description_map;
const TurnAnalysis &turn_analysis;
util::guidance::LaneDataIdMap &id_map;
@@ -10,6 +10,7 @@
#include <boost/functional/hash.hpp>
#include "util/concurrent_id_map.hpp"
#include "util/json_container.hpp"
#include "util/typedefs.hpp"
@@ -93,9 +94,9 @@ struct TurnLaneDescription_hash
}
};
typedef std::unordered_map<guidance::TurnLaneDescription,
LaneDescriptionID,
guidance::TurnLaneDescription_hash>
typedef util::ConcurrentIDMap<guidance::TurnLaneDescription,
LaneDescriptionID,
guidance::TurnLaneDescription_hash>
LaneDescriptionMap;
} // guidance
+3 -1
View File
@@ -22,7 +22,7 @@ struct ProfileProperties
: traffic_signal_penalty(0), u_turn_penalty(0),
max_speed_for_map_matching(DEFAULT_MAX_SPEED), continue_straight_at_waypoint(true),
use_turn_restrictions(false), left_hand_driving(false), fallback_to_duration(true),
weight_name{"duration"}
weight_name{"duration"}, call_tagless_node_function(true)
{
BOOST_ASSERT(weight_name[MAX_WEIGHT_NAME_LENGTH] == '\0');
}
@@ -88,6 +88,8 @@ struct ProfileProperties
char weight_name[MAX_WEIGHT_NAME_LENGTH + 1];
unsigned weight_precision = 1;
bool force_split_edges = false;
bool call_tagless_node_function = true;
};
}
}
+6 -7
View File
@@ -58,13 +58,12 @@ class ScriptingEnvironment
virtual void ProcessTurn(ExtractionTurn &turn) = 0;
virtual void ProcessSegment(ExtractionSegment &segment) = 0;
virtual void
ProcessElements(const std::vector<osmium::memory::Buffer::const_iterator> &osm_elements,
const RestrictionParser &restriction_parser,
tbb::concurrent_vector<std::pair<std::size_t, ExtractionNode>> &resulting_nodes,
tbb::concurrent_vector<std::pair<std::size_t, ExtractionWay>> &resulting_ways,
tbb::concurrent_vector<boost::optional<InputRestrictionContainer>>
&resulting_restrictions) = 0;
virtual void ProcessElements(
const osmium::memory::Buffer &buffer,
const RestrictionParser &restriction_parser,
std::vector<std::pair<const osmium::Node &, ExtractionNode>> &resulting_nodes,
std::vector<std::pair<const osmium::Way &, ExtractionWay>> &resulting_ways,
std::vector<boost::optional<InputRestrictionContainer>> &resulting_restrictions) = 0;
};
}
}
@@ -31,6 +31,11 @@ struct LuaScriptingContext final
bool has_way_function;
bool has_segment_function;
sol::function turn_function;
sol::function way_function;
sol::function node_function;
sol::function segment_function;
int api_version;
};
@@ -60,13 +65,12 @@ class Sol2ScriptingEnvironment final : public ScriptingEnvironment
void ProcessTurn(ExtractionTurn &turn) override;
void ProcessSegment(ExtractionSegment &segment) override;
void
ProcessElements(const std::vector<osmium::memory::Buffer::const_iterator> &osm_elements,
const RestrictionParser &restriction_parser,
tbb::concurrent_vector<std::pair<std::size_t, ExtractionNode>> &resulting_nodes,
tbb::concurrent_vector<std::pair<std::size_t, ExtractionWay>> &resulting_ways,
tbb::concurrent_vector<boost::optional<InputRestrictionContainer>>
&resulting_restrictions) override;
void ProcessElements(
const osmium::memory::Buffer &buffer,
const RestrictionParser &restriction_parser,
std::vector<std::pair<const osmium::Node &, ExtractionNode>> &resulting_nodes,
std::vector<std::pair<const osmium::Way &, ExtractionWay>> &resulting_ways,
std::vector<boost::optional<InputRestrictionContainer>> &resulting_restrictions) override;
private:
void InitContext(LuaScriptingContext &context);
+22 -10
View File
@@ -31,6 +31,15 @@ void write(storage::io::FileWriter &writer,
const detail::TurnDataContainerImpl<Ownership> &turn_data);
}
struct TurnData
{
extractor::guidance::TurnInstruction turn_instruction;
LaneDataID lane_data_id;
EntryClassID entry_class_id;
util::guidance::TurnBearing pre_turn_bearing;
util::guidance::TurnBearing post_turn_bearing;
};
namespace detail
{
template <storage::Ownership Ownership> class TurnDataContainerImpl
@@ -75,17 +84,20 @@ template <storage::Ownership Ownership> class TurnDataContainerImpl
// Used by EdgeBasedGraphFactory to fill data structure
template <typename = std::enable_if<Ownership == storage::Ownership::Container>>
void push_back(extractor::guidance::TurnInstruction turn_instruction,
LaneDataID lane_data_id,
EntryClassID entry_class_id,
util::guidance::TurnBearing pre_turn_bearing,
util::guidance::TurnBearing post_turn_bearing)
void push_back(const TurnData &data)
{
turn_instructions.push_back(turn_instruction);
lane_data_ids.push_back(lane_data_id);
entry_class_ids.push_back(entry_class_id);
pre_turn_bearings.push_back(pre_turn_bearing);
post_turn_bearings.push_back(post_turn_bearing);
turn_instructions.push_back(data.turn_instruction);
lane_data_ids.push_back(data.lane_data_id);
entry_class_ids.push_back(data.entry_class_id);
pre_turn_bearings.push_back(data.pre_turn_bearing);
post_turn_bearings.push_back(data.post_turn_bearing);
}
template <typename = std::enable_if<Ownership == storage::Ownership::Container>>
void append(const std::vector<TurnData> &others)
{
std::for_each(
others.begin(), others.end(), [this](const TurnData &other) { push_back(other); });
}
friend void serialization::read<Ownership>(storage::io::FileReader &reader,
+45
View File
@@ -1070,6 +1070,51 @@ argumentsToMatchParameter(const Nan::FunctionCallbackInfo<v8::Value> &args,
}
}
if (obj->Has(Nan::New("gaps").ToLocalChecked()))
{
v8::Local<v8::Value> gaps = obj->Get(Nan::New("gaps").ToLocalChecked());
if (gaps.IsEmpty())
return match_parameters_ptr();
if (!gaps->IsString())
{
Nan::ThrowError("Gaps must be a string: [split, ignore]");
return match_parameters_ptr();
}
const Nan::Utf8String gaps_utf8str(gaps);
std::string gaps_str{*gaps_utf8str, *gaps_utf8str + gaps_utf8str.length()};
if (gaps_str == "split")
{
params->gaps = osrm::MatchParameters::GapsType::Split;
}
else if (gaps_str == "ignore")
{
params->gaps = osrm::MatchParameters::GapsType::Ignore;
}
else
{
Nan::ThrowError("'gaps' param must be one of [split, ignore]");
return match_parameters_ptr();
}
}
if (obj->Has(Nan::New("tidy").ToLocalChecked()))
{
v8::Local<v8::Value> tidy = obj->Get(Nan::New("tidy").ToLocalChecked());
if (tidy.IsEmpty())
return match_parameters_ptr();
if (!tidy->IsBoolean())
{
Nan::ThrowError("tidy must be of type Boolean");
return match_parameters_ptr();
}
params->tidy = tidy->BooleanValue();
}
bool parsedSuccessfully = parseCommonParameters(obj, params);
if (!parsedSuccessfully)
{
+37
View File
@@ -0,0 +1,37 @@
#ifndef OSRM_ERRORCODES_HPP
#define OSRM_ERRORCODES_HPP
#include <string>
namespace osrm
{
/**
* Various error codes that can be returned by OSRM internal functions.
* Note: often, these translate into return codes from `int main()` functions.
* Thus, do not change the order - if adding new codes, append them to the
* end, so the code values do not change for users that are checking for
* certain values.
*/
enum ErrorCode
{
InvalidFingerprint = 2, // Start at 2 to avoid colliding with POSIX EXIT_FAILURE
IncompatibleFileVersion,
FileOpenError,
FileReadError,
FileWriteError,
FileIOError,
UnexpectedEndOfFile,
IncompatibleDataset,
UnknownAlgorithm
#ifndef NDEBUG
// Leave this at the end. In debug mode, we assert that the size of
// this enum matches the number of messages we have documented, and __ENDMARKER__
// is used as the "count" value.
,
__ENDMARKER__
#endif
};
}
#endif // OSRM_ERRORCODES_HPP
+1
View File
@@ -33,6 +33,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace osrm
{
using util::exception;
using util::RuntimeError;
}
#endif
+10 -2
View File
@@ -128,9 +128,16 @@ struct BaseParametersGrammar : boost::spirit::qi::grammar<Iterator, Signature>
},
qi::_1)];
polyline6_rule = qi::as_string[qi::lit("polyline6(") > +polyline_chars > ')']
[qi::_val = ph::bind(
[](const std::string &polyline) {
return engine::decodePolyline<1000000>(polyline);
},
qi::_1)];
query_rule =
((location_rule % ';') |
polyline_rule)[ph::bind(&engine::api::BaseParameters::coordinates, qi::_r1) = qi::_1];
((location_rule % ';') | polyline_rule |
polyline6_rule)[ph::bind(&engine::api::BaseParameters::coordinates, qi::_r1) = qi::_1];
radiuses_rule = qi::lit("radiuses=") >
(-(qi::double_ | unlimited_rule) %
@@ -176,6 +183,7 @@ struct BaseParametersGrammar : boost::spirit::qi::grammar<Iterator, Signature>
qi::rule<Iterator, osrm::engine::Bearing()> bearing_rule;
qi::rule<Iterator, osrm::util::Coordinate()> location_rule;
qi::rule<Iterator, std::vector<osrm::util::Coordinate>()> polyline_rule;
qi::rule<Iterator, std::vector<osrm::util::Coordinate>()> polyline6_rule;
qi::rule<Iterator, unsigned char()> base64_char;
qi::rule<Iterator, std::string()> polyline_chars;
+34 -21
View File
@@ -1,6 +1,8 @@
#ifndef OSRM_STORAGE_IO_HPP_
#define OSRM_STORAGE_IO_HPP_
#include "osrm/error_codes.hpp"
#include "util/exception.hpp"
#include "util/exception_utils.hpp"
#include "util/fingerprint.hpp"
@@ -10,6 +12,8 @@
#include <boost/filesystem/fstream.hpp>
#include <boost/iostreams/seek.hpp>
#include <cerrno>
#include <cstring>
#include <cstring>
#include <tuple>
#include <type_traits>
@@ -49,12 +53,16 @@ class FileReader
: filepath(filepath_), fingerprint(flag)
{
input_stream.open(filepath, std::ios::binary);
// Note: filepath.string() is wrapped in std::string() because it can
// return char * on some platforms, which makes the + operator not work
if (!input_stream)
throw util::exception("Error opening " + filepath.string());
throw util::RuntimeError(
filepath.string(), ErrorCode::FileOpenError, SOURCE_REF, std::strerror(errno));
if (flag == VerifyFingerprint && !ReadAndCheckFingerprint())
{
throw util::exception("Fingerprint mismatch in " + filepath_.string() + SOURCE_REF);
throw util::RuntimeError(filepath.string(), ErrorCode::InvalidFingerprint, SOURCE_REF);
}
}
@@ -66,7 +74,11 @@ class FileReader
if (file_size == boost::filesystem::ifstream::pos_type(-1))
{
throw util::exception("File size for " + filepath.string() + " failed " + SOURCE_REF);
throw util::RuntimeError("Unable to determine file size for " +
std::string(filepath.string()),
ErrorCode::FileIOError,
SOURCE_REF,
std::strerror(errno));
}
// restore the current position
@@ -101,10 +113,11 @@ class FileReader
{
if (result.eof())
{
throw util::exception("Error reading from " + filepath.string() +
": Unexpected end of file " + SOURCE_REF);
throw util::RuntimeError(
filepath.string(), ErrorCode::UnexpectedEndOfFile, SOURCE_REF);
}
throw util::exception("Error reading from " + filepath.string() + " " + SOURCE_REF);
throw util::RuntimeError(
filepath.string(), ErrorCode::FileReadError, SOURCE_REF, std::strerror(errno));
}
}
@@ -145,23 +158,19 @@ class FileReader
if (!loaded_fingerprint.IsValid())
{
util::Log(logERROR) << "Fingerprint magic number or checksum is invalid in "
<< filepath.string();
return false;
throw util::RuntimeError(filepath.string(), ErrorCode::InvalidFingerprint, SOURCE_REF);
}
if (!expected_fingerprint.IsDataCompatible(loaded_fingerprint))
{
util::Log(logERROR) << filepath.string()
<< " is not compatible with this version of OSRM";
util::Log(logERROR) << "It was prepared with OSRM "
<< loaded_fingerprint.GetMajorVersion() << "."
<< loaded_fingerprint.GetMinorVersion() << "."
<< loaded_fingerprint.GetPatchVersion() << " but you are running "
<< OSRM_VERSION;
util::Log(logERROR) << "Data is only compatible between minor releases.";
return false;
const std::string fileversion =
std::to_string(loaded_fingerprint.GetMajorVersion()) + "." +
std::to_string(loaded_fingerprint.GetMinorVersion()) + "." +
std::to_string(loaded_fingerprint.GetPatchVersion());
throw util::RuntimeError(std::string(filepath.string()) + " prepared with OSRM " +
fileversion + " but this is " + OSRM_VERSION,
ErrorCode::IncompatibleFileVersion,
SOURCE_REF);
}
return true;
@@ -192,7 +201,10 @@ class FileWriter
{
output_stream.open(filepath, std::ios::binary);
if (!output_stream)
throw util::exception("Error opening " + filepath.string());
{
throw util::RuntimeError(
filepath.string(), ErrorCode::FileOpenError, SOURCE_REF, std::strerror(errno));
}
if (flag == GenerateFingerprint)
{
@@ -216,7 +228,8 @@ class FileWriter
if (!result)
{
throw util::exception("Error writing to " + filepath.string());
throw util::RuntimeError(
filepath.string(), ErrorCode::FileWriteError, SOURCE_REF, std::strerror(errno));
}
}
+57
View File
@@ -0,0 +1,57 @@
#ifndef CONCURRENT_ID_MAP_HPP
#define CONCURRENT_ID_MAP_HPP
#include <boost/interprocess/sync/interprocess_upgradable_mutex.hpp>
#include <boost/interprocess/sync/scoped_lock.hpp>
#include <boost/interprocess/sync/sharable_lock.hpp>
#include <unordered_map>
namespace osrm
{
namespace util
{
/**
* This is a special purpose map for caching incrementing IDs
*/
template <typename KeyType, typename ValueType, typename HashType = std::hash<KeyType>>
struct ConcurrentIDMap
{
static_assert(std::is_unsigned<ValueType>::value, "Only unsigned integer types are supported.");
using UpgradableMutex = boost::interprocess::interprocess_upgradable_mutex;
using ScopedReaderLock = boost::interprocess::sharable_lock<UpgradableMutex>;
using ScopedWriterLock = boost::interprocess::scoped_lock<UpgradableMutex>;
std::unordered_map<KeyType, ValueType, HashType> data;
mutable UpgradableMutex mutex;
const ValueType ConcurrentFindOrAdd(const KeyType &key)
{
{
ScopedReaderLock sentry{mutex};
const auto result = data.find(key);
if (result != data.end())
{
return result->second;
}
}
{
ScopedWriterLock sentry{mutex};
const auto result = data.find(key);
if (result != data.end())
{
return result->second;
}
const auto id = static_cast<ValueType>(data.size());
data[key] = id;
return id;
}
}
};
} // util
} // osrm
#endif // CONCURRENT_ID_MAP_HPP
+70 -1
View File
@@ -28,10 +28,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef OSRM_EXCEPTION_HPP
#define OSRM_EXCEPTION_HPP
#include <array>
#include <exception>
#include <iostream>
#include <string>
#include <utility>
#include "osrm/error_codes.hpp"
#include <boost/format.hpp>
namespace osrm
@@ -39,7 +42,7 @@ namespace osrm
namespace util
{
class exception final : public std::exception
class exception : public std::exception
{
public:
explicit exception(const char *message) : message(message) {}
@@ -54,6 +57,72 @@ class exception final : public std::exception
virtual void anchor() const;
const std::string message;
};
/**
* Indicates a class of error that occurred that was caused by some kind of
* external input (common examples are out of disk space, file permission errors,
* user supplied bad data, etc).
*/
constexpr const std::array<const char *, 11> ErrorDescriptions = {{
"", // Dummy - ErrorCode values start at 2
"", // Dummy - ErrorCode values start at 2
"Fingerprint did not match the expected value", // InvalidFingerprint
"File is incompatible with this version of OSRM", // IncompatibleFileVersion
"Problem opening file", // FileOpenError
"Problem reading from file", // FileReadError
"Problem writing to file", // FileWriteError
"I/O error occurred", // FileIOError
"Unexpected end of file", // UnexpectedEndOfFile
"The dataset you are trying to load is not " // IncompatibleDataset
"compatible with the routing algorithm you want to use." // ...continued...
"Incompatible algorithm" // IncompatibleAlgorithm
}};
#ifndef NDEBUG
// Check that we have messages for every enum
static_assert(ErrorDescriptions.size() == ErrorCode::__ENDMARKER__,
"ErrorCode list and ErrorDescription lists are different sizes");
#endif
class RuntimeError : public exception
{
using Base = exception;
using Base::Base;
public:
explicit RuntimeError(const std::string &message,
const ErrorCode code_,
const std::string &sourceref,
const char *possiblecause = nullptr)
: Base(BuildMessage(message, code_, sourceref, possiblecause)), code(code_)
{
}
ErrorCode GetCode() const { return code; }
private:
// This function exists to 'anchor' the class, and stop the compiler from
// copying vtable and RTTI info into every object file that includes
// this header. (Caught by -Wweak-vtables under Clang.)
virtual void anchor() const;
const ErrorCode code;
static std::string BuildMessage(const std::string &message,
const ErrorCode code_,
const std::string &sourceref,
const char *possiblecause = nullptr)
{
std::string result;
result += ErrorDescriptions[code_];
result += ": " + std::string(message);
result += possiblecause != nullptr
? (std::string(" (possible cause: \"") + possiblecause + "\")")
: "";
result += " (at " + sourceref + ")";
return result;
}
};
}
}
+1 -1
View File
@@ -10,6 +10,6 @@
#define OSRM_SOURCE_FILE_ PROJECT_RELATIVE_PATH_(__FILE__)
// This is the macro to use
#define SOURCE_REF std::string(" (at ") + OSRM_SOURCE_FILE_ + ":" + std::to_string(__LINE__) + ")"
#define SOURCE_REF OSRM_SOURCE_FILE_ + ":" + std::to_string(__LINE__)
#endif // SOURCE_MACROS_HPP
+6 -6
View File
@@ -3,6 +3,7 @@
#include <iterator>
#include <numeric>
#include <utility>
namespace osrm
{
@@ -12,11 +13,11 @@ namespace util
// TODO: check why this is not an option here:
// std::adjacent_find(begin, end, [=](const auto& l, const auto& r){ return function(), false; });
template <typename ForwardIterator, typename Function>
Function for_each_pair(ForwardIterator begin, ForwardIterator end, Function function)
void for_each_pair(ForwardIterator begin, ForwardIterator end, Function &&function)
{
if (begin == end)
{
return function;
return;
}
auto next = begin;
@@ -24,19 +25,18 @@ Function for_each_pair(ForwardIterator begin, ForwardIterator end, Function func
while (next != end)
{
function(*begin, *next);
std::forward<Function>(function)(*begin, *next);
begin = std::next(begin);
next = std::next(next);
}
return function;
}
template <class ContainerT, typename Function>
Function for_each_pair(ContainerT &container, Function function)
void for_each_pair(ContainerT &container, Function &&function)
{
using std::begin;
using std::end;
return for_each_pair(begin(container), end(container), function);
for_each_pair(begin(container), end(container), std::forward<Function>(function));
}
} // namespace util
+2 -1
View File
@@ -7,6 +7,7 @@
#include <unordered_map>
#include <vector>
#include "util/concurrent_id_map.hpp"
#include "util/typedefs.hpp"
#include <boost/functional/hash.hpp>
@@ -97,7 +98,7 @@ class LaneTupleIdPair
}
};
using LaneDataIdMap = std::unordered_map<LaneTupleIdPair, LaneDataID, boost::hash<LaneTupleIdPair>>;
using LaneDataIdMap = ConcurrentIDMap<LaneTupleIdPair, LaneDataID, boost::hash<LaneTupleIdPair>>;
} // namespace guidance
} // namespace util
+30 -10
View File
@@ -10,6 +10,7 @@
#include <boost/iterator/iterator_facade.hpp>
#include <boost/iterator/reverse_iterator.hpp>
#include <tbb/atomic.h>
#include <array>
#include <cmath>
@@ -348,7 +349,7 @@ template <typename T, std::size_t Bits, storage::Ownership Ownership> class Pack
fill(initial_value);
}
PackedVector(util::ViewOrVector<std::uint64_t, Ownership> vec_, std::size_t num_elements)
PackedVector(util::ViewOrVector<WordT, Ownership> vec_, std::size_t num_elements)
: vec(std::move(vec_)), num_elements(num_elements)
{
initialize();
@@ -497,20 +498,39 @@ template <typename T, std::size_t Bits, storage::Ownership Ownership> class Pack
inline void set_value(const InternalIndex internal_index, const T value)
{
// ⚠ The method uses CAS spinlocks to prevent data races in parallel calls
// TBB internal atomic's are used for CAS on non-atomic data
// Parallel read and write access is not allowed
auto &lower_word = vec[internal_index.lower_word];
auto &upper_word = vec[internal_index.lower_word + 1];
lower_word = set_lower_value<WordT, T>(lower_word,
lower_mask[internal_index.element],
lower_offset[internal_index.element],
value);
upper_word = set_upper_value<WordT, T>(upper_word,
upper_mask[internal_index.element],
upper_offset[internal_index.element],
value);
// Lock-free update of the lower word
WordT local_lower_word, new_lower_word;
do
{
local_lower_word = lower_word;
new_lower_word = set_lower_value<WordT, T>(local_lower_word,
lower_mask[internal_index.element],
lower_offset[internal_index.element],
value);
} while (tbb::internal::as_atomic(lower_word)
.compare_and_swap(new_lower_word, local_lower_word) != local_lower_word);
// Lock-free update of the upper word
WordT local_upper_word, new_upper_word;
do
{
local_upper_word = upper_word;
new_upper_word = set_upper_value<WordT, T>(local_upper_word,
upper_mask[internal_index.element],
upper_offset[internal_index.element],
value);
} while (tbb::internal::as_atomic(upper_word)
.compare_and_swap(new_upper_word, local_upper_word) != local_upper_word);
}
util::ViewOrVector<std::uint64_t, Ownership> vec;
util::ViewOrVector<WordT, Ownership> vec;
std::uint64_t num_elements = 0;
};
}
+6677
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "osrm",
"version": "5.8.0-latest.1",
"version": "5.8.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": {
+5
View File
@@ -15,6 +15,11 @@ properties.continue_straight_at_waypoint = false
properties.weight_name = 'duration'
--properties.weight_name = 'cyclability'
-- Set to true if you need to call the node_function for every node.
-- Generally can be left as false to avoid unnecessary Lua calls
-- (which slow down pre-processing).
properties.call_tagless_node_function = false
local default_speed = 15
local walking_speed = 6
+6
View File
@@ -20,6 +20,12 @@ properties.weight_name = 'routability'
-- For shortest distance without penalties for accessibility
--properties.weight_name = 'distance'
-- Set to true if you need to call the node_function for every node.
-- Generally can be left as false to avoid unnecessary Lua calls
-- (which slow down pre-processing).
properties.call_tagless_node_function = false
local profile = {
default_mode = mode.driving,
default_speed = 10,
+5
View File
@@ -14,6 +14,11 @@ properties.continue_straight_at_waypoint = false
properties.weight_name = 'duration'
--properties.weight_name = 'routability'
-- Set to true if you need to call the node_function for every node.
-- Generally can be left as false to avoid unnecessary Lua calls
-- (which slow down pre-processing).
properties.call_tagless_node_function = false
local walking_speed = 5
local profile = {
+5
View File
@@ -3,6 +3,11 @@ api_version = 1
properties.force_split_edges = true
-- Set to true if you need to call the node_function for every node.
-- Generally can be left as false to avoid unnecessary Lua calls
-- (which slow down pre-processing).
properties.call_tagless_node_function = false
-- Minimalist node_ and way_functions in order to test source_ and segment_functions
function node_function (node, result)
+5
View File
@@ -1,6 +1,11 @@
api_version = 1
-- Rasterbot profile
-- Set to true if you need to call the node_function for every node.
-- Generally can be left as false to avoid unnecessary Lua calls
-- (which slow down pre-processing).
properties.call_tagless_node_function = false
-- Minimalist node_ and way_functions in order to test source_ and segment_functions
function node_function (node, result)
+5
View File
@@ -22,6 +22,11 @@ properties.use_turn_restrictions = true
properties.max_speed_for_map_matching = 30/3.6 --km -> m/s
properties.weight_name = 'duration'
-- Set to true if you need to call the node_function for every node.
-- Generally can be left as false to avoid unnecessary Lua calls
-- (which slow down pre-processing).
properties.call_tagless_node_function = false
local uturn_penalty = 20
local traffic_light_penalty = 7 -- seconds
@@ -61,6 +61,22 @@ bool isShortAndUndisturbed(const RouteStep &step)
return is_short && noIntermediaryIntersections(step);
}
// On dual carriageways, we might want to use u-turns in combination with new-name instructions.
// Otherwise a u-turn should never be part of a collapsing instructions.
bool noBadUTurnCombination(const RouteStepIterator first, const RouteStepIterator second)
{
auto has_uturn = hasModifier(*first, DirectionModifier::UTurn) ||
hasModifier(*second, DirectionModifier::UTurn);
auto const from_name_change_into_uturn =
hasTurnType(*first, TurnType::NewName) && hasModifier(*second, DirectionModifier::UTurn);
auto const uturn_into_name_change =
hasTurnType(*second, TurnType::NewName) && hasModifier(*first, DirectionModifier::UTurn);
return !has_uturn || from_name_change_into_uturn || uturn_into_name_change;
}
} // namespace
bool basicCollapsePreconditions(const RouteStepIterator first, const RouteStepIterator second)
@@ -70,7 +86,10 @@ bool basicCollapsePreconditions(const RouteStepIterator first, const RouteStepIt
const auto waypoint_type = hasWaypointType(*first) || hasWaypointType(*second);
return !has_roundabout_type && !waypoint_type && haveSameMode(*first, *second);
const auto contains_bad_uturn = !noBadUTurnCombination(first, second);
return !has_roundabout_type && !waypoint_type && haveSameMode(*first, *second) &&
!contains_bad_uturn;
}
bool basicCollapsePreconditions(const RouteStepIterator first,
@@ -81,8 +100,11 @@ bool basicCollapsePreconditions(const RouteStepIterator first,
hasRoundaboutType(second->maneuver.instruction) ||
hasRoundaboutType(third->maneuver.instruction);
const auto contains_bad_uturn =
!noBadUTurnCombination(first, second) && !noBadUTurnCombination(second, third);
// require modes to match up
return !has_roundabout_type && haveSameMode(*first, *second, *third);
return !has_roundabout_type && haveSameMode(*first, *second, *third) && !contains_bad_uturn;
}
bool isStaggeredIntersection(const RouteStepIterator step_prior_to_intersection,
+10 -5
View File
@@ -171,9 +171,11 @@ void closeOffRoundabout(const bool on_roundabout,
if (!guidance::haveSameMode(exit_step, prev_step))
{
BOOST_ASSERT(leavesRoundabout(exit_step.maneuver.instruction));
prev_step.maneuver.instruction = exit_step.maneuver.instruction;
if (!entersRoundabout(prev_step.maneuver.instruction))
prev_step.maneuver.exit = exit_step.maneuver.exit;
{
prev_step.maneuver.instruction = exit_step.maneuver.instruction;
}
prev_step.maneuver.exit = exit_step.maneuver.exit;
exit_step.maneuver.instruction.type = TurnType::Notification;
step_index--;
}
@@ -198,9 +200,12 @@ void closeOffRoundabout(const bool on_roundabout,
{
auto &propagation_step = steps[propagation_index];
auto &next_step = steps[propagation_index + 1];
propagation_step.ElongateBy(next_step);
propagation_step.maneuver.exit = next_step.maneuver.exit;
next_step.Invalidate();
if (guidance::haveSameMode(propagation_step, next_step))
{
propagation_step.ElongateBy(next_step);
propagation_step.maneuver.exit = next_step.maneuver.exit;
next_step.Invalidate();
}
if (entersRoundabout(propagation_step.maneuver.instruction))
{
+129 -40
View File
@@ -428,6 +428,12 @@ void encodeVectorTile(const datafacade::ContiguousInternalMemoryDataFacadeBase &
const auto &edge = edges[edge_index];
const auto geometry_id = get_geometry_id(edge);
// Get coordinates for start/end nodes of segment (NodeIDs u and v)
const auto a = facade.GetCoordinateOfNode(edge.u);
const auto b = facade.GetCoordinateOfNode(edge.v);
// Calculate the length in meters
const double length = osrm::util::coordinate_calculation::haversineDistance(a, b);
// Weight values
const auto forward_weight_vector =
facade.GetUncompressedForwardWeights(geometry_id);
@@ -439,6 +445,14 @@ void encodeVectorTile(const datafacade::ContiguousInternalMemoryDataFacadeBase &
use_line_value(forward_weight);
use_line_value(reverse_weight);
std::uint32_t forward_rate =
static_cast<std::uint32_t>(round(length / forward_weight * 10.));
std::uint32_t reverse_rate =
static_cast<std::uint32_t>(round(length / reverse_weight * 10.));
use_line_value(forward_rate);
use_line_value(reverse_rate);
// Duration values
const auto forward_duration_vector =
facade.GetUncompressedForwardDurations(geometry_id);
@@ -489,9 +503,9 @@ void encodeVectorTile(const datafacade::ContiguousInternalMemoryDataFacadeBase &
const auto reverse_duration =
reverse_duration_vector[reverse_duration_vector.size() -
edge.fwd_segment_position - 1];
const auto forward_datasource =
const auto forward_datasource_idx =
forward_datasource_vector[edge.fwd_segment_position];
const auto reverse_datasource =
const auto reverse_datasource_idx =
reverse_datasource_vector[reverse_datasource_vector.size() -
edge.fwd_segment_position - 1];
@@ -516,14 +530,16 @@ void encodeVectorTile(const datafacade::ContiguousInternalMemoryDataFacadeBase &
&component_id,
&id,
&max_datasource_id,
&used_line_ints](const FixedLine &tile_line,
const std::uint32_t speed_kmh,
const std::size_t weight,
const std::size_t duration,
const DatasourceID datasource,
const std::size_t name_idx,
std::int32_t &start_x,
std::int32_t &start_y) {
&used_line_ints](
const FixedLine &tile_line,
const std::uint32_t speed_kmh_idx,
const std::uint32_t rate_idx,
const std::size_t weight_idx,
const std::size_t duration_idx,
const DatasourceID datasource_idx,
const std::size_t name_idx,
std::int32_t &start_x,
std::int32_t &start_y) {
// Here, we save the two attributes for our feature: the speed and
// the is_small boolean. We only serve up speeds from 0-139, so all we
// do is save the first
@@ -547,23 +563,27 @@ void encodeVectorTile(const datafacade::ContiguousInternalMemoryDataFacadeBase &
feature_writer, util::vector_tile::FEATURE_ATTRIBUTES_TAG);
field.add_element(0); // "speed" tag key offset
field.add_element(std::min(
speed_kmh_idx, 127u)); // save the speed value, capped at 127
field.add_element(1); // "is_small" tag key offset
field.add_element(
std::min(speed_kmh, 127u)); // save the speed value, capped at 127
field.add_element(1); // "is_small" tag key offset
field.add_element(128 +
(component_id.is_tiny ? 0 : 1)); // is_small feature
field.add_element(2); // "datasource" tag key offset
field.add_element(130 + datasource); // datasource value offset
field.add_element(3); // "weight" tag key offset
128 + (component_id.is_tiny ? 0 : 1)); // is_small feature offset
field.add_element(2); // "datasource" tag key offset
field.add_element(130 + datasource_idx); // datasource value offset
field.add_element(3); // "weight" tag key offset
field.add_element(130 + max_datasource_id + 1 +
weight); // weight value offset
field.add_element(4); // "duration" tag key offset
weight_idx); // weight value offset
field.add_element(4); // "duration" tag key offset
field.add_element(130 + max_datasource_id + 1 +
duration); // duration value offset
field.add_element(5); // "name" tag key offset
duration_idx); // duration value offset
field.add_element(5); // "name" tag key offset
field.add_element(130 + max_datasource_id + 1 + used_line_ints.size() +
name_idx); // name value offset
field.add_element(6); // rate tag key offset
field.add_element(130 + max_datasource_id + 1 +
rate_idx); // rate goes in used_line_ints
}
{
@@ -581,17 +601,26 @@ void encodeVectorTile(const datafacade::ContiguousInternalMemoryDataFacadeBase &
std::int32_t start_y = 0;
// Calculate the speed for this line
std::uint32_t speed_kmh =
// Speeds are looked up in a simple 1:1 table, so the speed value == lookup
// table index
std::uint32_t speed_kmh_idx =
static_cast<std::uint32_t>(round(length / forward_duration * 10 * 3.6));
// Rate values are in meters per weight-unit - and similar to speeds, we
// present 1 decimal place of precision (these values are added as
// double/10) lower down
std::uint32_t forward_rate =
static_cast<std::uint32_t>(round(length / forward_weight * 10.));
auto tile_line = coordinatesToTileLine(a, b, tile_bbox);
if (!tile_line.empty())
{
encode_tile_line(tile_line,
speed_kmh,
speed_kmh_idx,
line_int_offsets[forward_rate],
line_int_offsets[forward_weight],
line_int_offsets[forward_duration],
forward_datasource,
forward_datasource_idx,
name_offset,
start_x,
start_y);
@@ -606,17 +635,26 @@ void encodeVectorTile(const datafacade::ContiguousInternalMemoryDataFacadeBase &
std::int32_t start_y = 0;
// Calculate the speed for this line
std::uint32_t speed_kmh =
// Speeds are looked up in a simple 1:1 table, so the speed value == lookup
// table index
std::uint32_t speed_kmh_idx =
static_cast<std::uint32_t>(round(length / reverse_duration * 10 * 3.6));
// Rate values are in meters per weight-unit - and similar to speeds, we
// present 1 decimal place of precision (these values are added as
// double/10) lower down
std::uint32_t reverse_rate =
static_cast<std::uint32_t>(round(length / reverse_weight * 10.));
auto tile_line = coordinatesToTileLine(b, a, tile_bbox);
if (!tile_line.empty())
{
encode_tile_line(tile_line,
speed_kmh,
speed_kmh_idx,
line_int_offsets[reverse_rate],
line_int_offsets[reverse_weight],
line_int_offsets[reverse_duration],
reverse_datasource,
reverse_datasource_idx,
name_offset,
start_x,
start_y);
@@ -634,6 +672,7 @@ void encodeVectorTile(const datafacade::ContiguousInternalMemoryDataFacadeBase &
line_layer_writer.add_string(util::vector_tile::KEY_TAG, "weight");
line_layer_writer.add_string(util::vector_tile::KEY_TAG, "duration");
line_layer_writer.add_string(util::vector_tile::KEY_TAG, "name");
line_layer_writer.add_string(util::vector_tile::KEY_TAG, "rate");
// Now, we write out the possible speed value arrays and possible is_tiny
// values. Field type 4 is the "values" field. It's a variable type field,
@@ -695,19 +734,21 @@ void encodeVectorTile(const datafacade::ContiguousInternalMemoryDataFacadeBase &
{
// we need to pre-encode all values here because we need the full offsets later
// for encoding the actual features.
std::vector<std::tuple<util::Coordinate, unsigned, unsigned, unsigned>>
std::vector<std::tuple<util::Coordinate, unsigned, unsigned, unsigned, unsigned>>
encoded_turn_data(all_turn_data.size());
std::transform(
all_turn_data.begin(),
all_turn_data.end(),
encoded_turn_data.begin(),
[&](const routing_algorithms::TurnData &t) {
auto angle_idx = use_point_int_value(t.in_angle);
auto turn_idx = use_point_int_value(t.turn_angle);
auto duration_idx =
use_point_float_value(t.duration / 10.0); // Note conversion to float here
return std::make_tuple(t.coordinate, angle_idx, turn_idx, duration_idx);
});
std::transform(all_turn_data.begin(),
all_turn_data.end(),
encoded_turn_data.begin(),
[&](const routing_algorithms::TurnData &t) {
auto angle_idx = use_point_int_value(t.in_angle);
auto turn_idx = use_point_int_value(t.turn_angle);
auto duration_idx = use_point_float_value(
t.duration / 10.0); // Note conversion to float here
auto weight_idx = use_point_float_value(
t.weight / 10.0); // Note conversion to float here
return std::make_tuple(
t.coordinate, angle_idx, turn_idx, duration_idx, weight_idx);
});
// Now write the points layer for turn penalty data:
// Add a layer object to the PBF stream. 3=='layer' from the vector tile spec
@@ -734,7 +775,7 @@ void encodeVectorTile(const datafacade::ContiguousInternalMemoryDataFacadeBase &
util::vector_tile::GEOMETRY_TYPE_POINT); // geometry type
feature_writer.add_uint64(util::vector_tile::ID_TAG, id++); // id
{
// Write out the 3 properties we want on the feature. These
// Write out the 4 properties we want on the feature. These
// refer to indexes in the properties lookup table, which we
// add to the tile after we add all features.
protozero::packed_field_uint32 field(
@@ -745,6 +786,8 @@ void encodeVectorTile(const datafacade::ContiguousInternalMemoryDataFacadeBase &
field.add_element(std::get<2>(point_turn_data));
field.add_element(2); // "cost" tag key offset
field.add_element(used_point_ints.size() + std::get<3>(point_turn_data));
field.add_element(3); // "weight" tag key offset
field.add_element(used_point_ints.size() + std::get<4>(point_turn_data));
}
{
// Add the geometry as the last field in this feature
@@ -772,6 +815,7 @@ void encodeVectorTile(const datafacade::ContiguousInternalMemoryDataFacadeBase &
point_layer_writer.add_string(util::vector_tile::KEY_TAG, "bearing_in");
point_layer_writer.add_string(util::vector_tile::KEY_TAG, "turn_angle");
point_layer_writer.add_string(util::vector_tile::KEY_TAG, "cost");
point_layer_writer.add_string(util::vector_tile::KEY_TAG, "weight");
// Now, save the lists of integers and floats that our features refer to.
for (const auto &value : used_point_ints)
@@ -787,6 +831,51 @@ void encodeVectorTile(const datafacade::ContiguousInternalMemoryDataFacadeBase &
values_writer.add_float(util::vector_tile::VARIANT_TYPE_FLOAT, value);
}
}
// OSM Node tile layer
{
protozero::pbf_writer point_layer_writer(tile_writer, util::vector_tile::LAYER_TAG);
point_layer_writer.add_uint32(util::vector_tile::VERSION_TAG, 2); // version
point_layer_writer.add_string(util::vector_tile::NAME_TAG, "osmnodes"); // name
point_layer_writer.add_uint32(util::vector_tile::EXTENT_TAG,
util::vector_tile::EXTENT); // extent
std::vector<NodeID> internal_nodes;
internal_nodes.reserve(edges.size() * 2);
for (const auto &edge : edges)
{
internal_nodes.push_back(edge.u);
internal_nodes.push_back(edge.v);
}
std::sort(internal_nodes.begin(), internal_nodes.end());
auto new_end = std::unique(internal_nodes.begin(), internal_nodes.end());
internal_nodes.resize(new_end - internal_nodes.begin());
for (const auto &internal_node : internal_nodes)
{
const auto coord = facade.GetCoordinateOfNode(internal_node);
const auto tile_point = coordinatesToTilePoint(coord, tile_bbox);
if (!boost::geometry::within(point_t(tile_point.x, tile_point.y), clip_box))
{
continue;
}
protozero::pbf_writer feature_writer(point_layer_writer,
util::vector_tile::FEATURE_TAG);
// Field 3 is the "geometry type" field. Value 1 is "point"
feature_writer.add_enum(util::vector_tile::GEOMETRY_TAG,
util::vector_tile::GEOMETRY_TYPE_POINT); // geometry type
const auto osmid =
static_cast<OSMNodeID::value_type>(facade.GetOSMNodeIDOfNode(internal_node));
feature_writer.add_uint64(util::vector_tile::ID_TAG, osmid); // id
// There are no additional properties, just the ID and the geometry
{
// Add the geometry as the last field in this feature
protozero::packed_field_uint32 geometry(
feature_writer, util::vector_tile::FEATURE_GEOMETRIES_TAG);
encodePoint(tile_point, geometry);
}
}
}
}
// protozero serializes data during object destructors, so once the scope closes,
// our result buffer will have all the tile data encoded into it.
+17 -42
View File
@@ -52,54 +52,29 @@ std::string encode(std::vector<int> &numbers)
}
return output;
}
}
std::vector<util::Coordinate> decodePolyline(const std::string &geometry_string)
// https://developers.google.com/maps/documentation/utilities/polylinealgorithm
std::int32_t decode_polyline_integer(std::string::const_iterator &first,
std::string::const_iterator last)
{
// https://developers.google.com/maps/documentation/utilities/polylinealgorithm
auto decode_polyline_integer = [](auto &first, auto last) {
// varint coding parameters
const std::uint32_t bits_in_chunk = 5;
const std::uint32_t continuation_bit = 1 << bits_in_chunk;
const std::uint32_t chunk_mask = (1 << bits_in_chunk) - 1;
// varint coding parameters
const std::uint32_t bits_in_chunk = 5;
const std::uint32_t continuation_bit = 1 << bits_in_chunk;
const std::uint32_t chunk_mask = (1 << bits_in_chunk) - 1;
std::uint32_t result = 0;
for (std::uint32_t value = continuation_bit, shift = 0;
(value & continuation_bit) && (shift < CHAR_BIT * sizeof(result) - 1) && first != last;
++first, shift += bits_in_chunk)
{
value = *first - 63; // convert ASCII coding [?..~] to an integer [0..63]
result |= (value & chunk_mask) << shift;
}
// change "zig-zag" sign coding to two's complement
result = ((result & 1) == 1) ? ~(result >> 1) : (result >> 1);
return static_cast<std::int32_t>(result);
};
auto polyline_to_coordinate = [](auto value) {
return static_cast<std::int32_t>(value * detail::POLYLINE_TO_COORDINATE);
};
std::vector<util::Coordinate> coordinates;
std::int32_t latitude = 0, longitude = 0;
std::string::const_iterator first = geometry_string.begin();
const std::string::const_iterator last = geometry_string.end();
while (first != last)
std::uint32_t result = 0;
for (std::uint32_t value = continuation_bit, shift = 0;
(value & continuation_bit) && (shift < CHAR_BIT * sizeof(result) - 1) && first != last;
++first, shift += bits_in_chunk)
{
const auto dlat = decode_polyline_integer(first, last);
const auto dlon = decode_polyline_integer(first, last);
latitude += dlat;
longitude += dlon;
coordinates.emplace_back(
util::Coordinate{util::FixedLongitude{polyline_to_coordinate(longitude)},
util::FixedLatitude{polyline_to_coordinate(latitude)}});
value = *first - 63; // convert ASCII coding [?..~] to an integer [0..63]
result |= (value & chunk_mask) << shift;
}
return coordinates;
// change "zig-zag" sign coding to two's complement
result = ((result & 1) == 1) ? ~(result >> 1) : (result >> 1);
return static_cast<std::int32_t>(result);
}
}
}
}
@@ -24,7 +24,7 @@ namespace ch
namespace
{
const double constexpr VIAPATH_ALPHA = 0.10;
const double constexpr VIAPATH_ALPHA = 0.25; // alternative is local optimum on 25% sub-paths
const double constexpr VIAPATH_EPSILON = 0.15; // alternative at most 15% longer
const double constexpr VIAPATH_GAMMA = 0.75; // alternative shares at most 75% with the shortest.
@@ -398,8 +398,7 @@ bool viaNodeCandidatePassesTTest(
{
return false;
}
const EdgeWeight T_threshold =
static_cast<EdgeWeight>(VIAPATH_EPSILON * weight_of_shortest_path);
const EdgeWeight T_threshold = static_cast<EdgeWeight>(VIAPATH_ALPHA * weight_of_shortest_path);
EdgeWeight unpacked_until_weight = 0;
std::stack<SearchSpaceEdge> unpack_stack;
@@ -732,7 +731,7 @@ alternativePathSearch(SearchEngineData<Algorithm> &engine_working_data,
(approximated_sharing <= upper_bound_to_shortest_path_weight * VIAPATH_GAMMA);
const bool stretch_passes =
(approximated_weight - approximated_sharing) <
((1. + VIAPATH_ALPHA) * (upper_bound_to_shortest_path_weight - approximated_sharing));
((1. + VIAPATH_EPSILON) * (upper_bound_to_shortest_path_weight - approximated_sharing));
if (weight_passes && sharing_passes && stretch_passes)
{
+139 -70
View File
@@ -7,31 +7,33 @@ namespace engine
namespace routing_algorithms
{
std::vector<TurnData>
getTileTurns(const datafacade::ContiguousInternalMemoryDataFacade<ch::Algorithm> &facade,
const std::vector<RTreeLeaf> &edges,
const std::vector<std::size_t> &sorted_edge_indexes)
namespace
{
std::vector<TurnData> all_turn_data;
// Struct to hold info on all the EdgeBasedNodes that are visible in our tile
// When we create these, we insure that (source, target) and packed_geometry_id
// are all pointed in the same direction.
struct EdgeBasedNodeInfo
{
bool is_geometry_forward; // Is the geometry forward or reverse?
unsigned packed_geometry_id;
};
// Struct to hold info on all the EdgeBasedNodes that are visible in our tile
// When we create these, we insure that (source, target) and packed_geometry_id
// are all pointed in the same direction.
struct EdgeBasedNodeInfo
{
bool is_geometry_forward; // Is the geometry forward or reverse?
unsigned packed_geometry_id;
};
struct SegmentData
{
NodeID target_node;
EdgeID edge_based_node_id;
};
template <typename edge_extractor, typename datafacade>
std::vector<TurnData> generateTurns(const datafacade &facade,
const std::vector<RTreeLeaf> &edges,
const std::vector<std::size_t> &sorted_edge_indexes,
edge_extractor const &find_edge)
{
// Lookup table for edge-based-nodes
std::unordered_map<NodeID, EdgeBasedNodeInfo> edge_based_node_info;
struct SegmentData
{
NodeID target_node;
EdgeID edge_based_node_id;
};
std::unordered_map<NodeID, std::vector<SegmentData>> directed_graph;
// Reserve enough space for unique edge-based-nodes on every edge.
// Only a tile with all unique edges will use this much, but
// it saves us a bunch of re-allocations during iteration.
@@ -41,8 +43,10 @@ getTileTurns(const datafacade::ContiguousInternalMemoryDataFacade<ch::Algorithm>
return facade.GetGeometryIndex(edge.forward_segment_id.id).id;
};
// Build an adjacency list for all the road segments visible in
// the tile
// To build a tile, we can only rely on the r-tree to quickly find all data visible within the
// tile itself. The Rtree returns a series of segments that may or may not offer turns
// associated with them. To be able to extract turn penalties, we extract a node based graph
// from our edge based representation.
for (const auto &edge_index : sorted_edge_indexes)
{
const auto &edge = edges[edge_index];
@@ -79,28 +83,32 @@ getTileTurns(const datafacade::ContiguousInternalMemoryDataFacade<ch::Algorithm>
}
}
// Make sure we traverse the startnodes in a consistent order
// to ensure identical PBF encoding on all platforms.
std::vector<NodeID> sorted_startnodes;
sorted_startnodes.reserve(directed_graph.size());
std::transform(directed_graph.begin(),
directed_graph.end(),
std::back_inserter(sorted_startnodes),
[](auto const &node) { return node.first; });
std::sort(sorted_startnodes.begin(), sorted_startnodes.end());
std::vector<TurnData> all_turn_data;
// Given a turn:
// u---v
// |
// w
// uv is the "approach"
// vw is the "exit"
std::vector<contractor::QueryEdge::EdgeData> unpacked_shortcut;
std::vector<EdgeWeight> approach_weight_vector;
std::vector<EdgeWeight> approach_duration_vector;
// Make sure we traverse the startnodes in a consistent order
// to ensure identical PBF encoding on all platforms.
std::vector<NodeID> sorted_startnodes;
sorted_startnodes.reserve(directed_graph.size());
for (const auto &startnode : directed_graph)
sorted_startnodes.push_back(startnode.first);
std::sort(sorted_startnodes.begin(), sorted_startnodes.end());
// Look at every node in the directed graph we created
for (const auto &startnode : sorted_startnodes)
{
const auto &nodedata = directed_graph[startnode];
BOOST_ASSERT(directed_graph.find(startnode) != directed_graph.end());
const auto &nodedata = directed_graph.find(startnode)->second;
// For all the outgoing edges from the node
for (const auto &approachedge : nodedata)
{
@@ -110,7 +118,7 @@ getTileTurns(const datafacade::ContiguousInternalMemoryDataFacade<ch::Algorithm>
continue;
// For each of the outgoing edges from our target coordinate
for (const auto &exit_edge : directed_graph[approachedge.target_node])
for (const auto &exit_edge : directed_graph.find(approachedge.target_node)->second)
{
// If the next edge has the same edge_based_node_id, then it's
// not a turn, so skip it
@@ -132,55 +140,32 @@ getTileTurns(const datafacade::ContiguousInternalMemoryDataFacade<ch::Algorithm>
//
// would offer a backward edge at `b` to `a` (due to the oneway from a to b)
// but could also offer a shortcut (b-c-a) from `b` to `a` which is longer.
EdgeID smaller_edge_id =
facade.FindSmallestEdge(approachedge.edge_based_node_id,
exit_edge.edge_based_node_id,
[](const contractor::QueryEdge::EdgeData &data) {
return data.forward && !data.shortcut;
});
EdgeID edge_based_edge_id =
find_edge(approachedge.edge_based_node_id, exit_edge.edge_based_node_id);
// Depending on how the graph is constructed, we might have to look for
// a backwards edge instead. They're equivalent, just one is available for
// a forward routing search, and one is used for the backwards dijkstra
// steps. Their weight should be the same, we can use either one.
// If we didn't find a forward edge, try for a backward one
if (SPECIAL_EDGEID == smaller_edge_id)
if (edge_based_edge_id != SPECIAL_EDGEID)
{
smaller_edge_id =
facade.FindSmallestEdge(exit_edge.edge_based_node_id,
approachedge.edge_based_node_id,
[](const contractor::QueryEdge::EdgeData &data) {
return data.backward && !data.shortcut;
});
}
// If no edge was found, it means that there's no connection between these
// nodes, due to oneways or turn restrictions. Given the edge-based-nodes
// that we're examining here, we *should* only find directly-connected
// edges, not shortcuts
if (smaller_edge_id != SPECIAL_EDGEID)
{
const auto &data = facade.GetEdgeData(smaller_edge_id);
BOOST_ASSERT_MSG(!data.shortcut, "Connecting edge must not be a shortcut");
const auto &data = facade.GetEdgeData(edge_based_edge_id);
// Now, calculate the sum of the weight of all the segments.
if (edge_based_node_info[approachedge.edge_based_node_id].is_geometry_forward)
if (edge_based_node_info.find(approachedge.edge_based_node_id)
->second.is_geometry_forward)
{
approach_weight_vector = facade.GetUncompressedForwardWeights(
edge_based_node_info[approachedge.edge_based_node_id]
.packed_geometry_id);
edge_based_node_info.find(approachedge.edge_based_node_id)
->second.packed_geometry_id);
approach_duration_vector = facade.GetUncompressedForwardDurations(
edge_based_node_info[approachedge.edge_based_node_id]
.packed_geometry_id);
edge_based_node_info.find(approachedge.edge_based_node_id)
->second.packed_geometry_id);
}
else
{
approach_weight_vector = facade.GetUncompressedReverseWeights(
edge_based_node_info[approachedge.edge_based_node_id]
.packed_geometry_id);
edge_based_node_info.find(approachedge.edge_based_node_id)
->second.packed_geometry_id);
approach_duration_vector = facade.GetUncompressedReverseDurations(
edge_based_node_info[approachedge.edge_based_node_id]
.packed_geometry_id);
edge_based_node_info.find(approachedge.edge_based_node_id)
->second.packed_geometry_id);
}
const auto sum_node_weight = std::accumulate(approach_weight_vector.begin(),
approach_weight_vector.end(),
@@ -239,6 +224,90 @@ getTileTurns(const datafacade::ContiguousInternalMemoryDataFacade<ch::Algorithm>
return all_turn_data;
}
} // namespace
// CH Version of finding all turn penalties. Here is where the actual work is happening
std::vector<TurnData>
getTileTurns(const datafacade::ContiguousInternalMemoryDataFacade<ch::Algorithm> &facade,
const std::vector<RTreeLeaf> &edges,
const std::vector<std::size_t> &sorted_edge_indexes)
{
// Define how to find the representative edge between two edge based nodes for a CH
struct EdgeFinderCH
{
EdgeFinderCH(const datafacade::ContiguousInternalMemoryDataFacade<ch::Algorithm> &facade)
: facade(facade)
{
}
const datafacade::ContiguousInternalMemoryDataFacade<ch::Algorithm> &facade;
EdgeID operator()(const NodeID approach_node, const NodeID exit_node) const
{
// Find the connection between our source road and the target node
// Since we only want to find direct edges, we cannot check shortcut edges here.
// Otherwise we might find a forward edge even though a shorter backward edge
// exists (due to oneways).
//
// a > - > - > - b
// | |
// |------ c ----|
//
// would offer a backward edge at `b` to `a` (due to the oneway from a to b)
// but could also offer a shortcut (b-c-a) from `b` to `a` which is longer.
EdgeID edge_id = facade.FindSmallestEdge(
approach_node, exit_node, [](const contractor::QueryEdge::EdgeData &data) {
return data.forward && !data.shortcut;
});
// Depending on how the graph is constructed, we might have to look for
// a backwards edge instead. They're equivalent, just one is available for
// a forward routing search, and one is used for the backwards dijkstra
// steps. Their weight should be the same, we can use either one.
// If we didn't find a forward edge, try for a backward one
if (SPECIAL_EDGEID == edge_id)
{
edge_id = facade.FindSmallestEdge(
exit_node, approach_node, [](const contractor::QueryEdge::EdgeData &data) {
return data.backward && !data.shortcut;
});
}
BOOST_ASSERT_MSG(edge_id == SPECIAL_EDGEID || !facade.GetEdgeData(edge_id).shortcut,
"Connecting edge must not be a shortcut");
return edge_id;
}
};
EdgeFinderCH edge_finder(facade);
return generateTurns(facade, edges, sorted_edge_indexes, edge_finder);
}
// MLD version to find all turns
std::vector<TurnData>
getTileTurns(const datafacade::ContiguousInternalMemoryDataFacade<mld::Algorithm> &facade,
const std::vector<RTreeLeaf> &edges,
const std::vector<std::size_t> &sorted_edge_indexes)
{
// Define how to find the representative edge between two edge-based-nodes for a MLD
struct EdgeFinderMLD
{
EdgeFinderMLD(const datafacade::ContiguousInternalMemoryDataFacade<mld::Algorithm> &facade)
: facade(facade)
{
}
const datafacade::ContiguousInternalMemoryDataFacade<mld::Algorithm> &facade;
EdgeID operator()(const NodeID approach_node, const NodeID exit_node) const
{
return facade.FindEdge(approach_node, exit_node);
}
};
EdgeFinderMLD edge_finder(facade);
return generateTurns(facade, edges, sorted_edge_indexes, edge_finder);
}
} // namespace routing_algorithms
} // namespace engine
} // namespace osrm
+313 -178
View File
@@ -30,6 +30,11 @@
#include <string>
#include <unordered_map>
#include <tbb/blocked_range.h>
#include <tbb/parallel_for.h>
#include <tbb/pipeline.h>
#include <tbb/task_scheduler_init.h>
namespace osrm
{
namespace extractor
@@ -319,6 +324,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
const std::string &turn_duration_penalties_filename,
const std::string &turn_penalties_index_filename)
{
util::Log() << "Generating edge-expanded edges ";
std::size_t node_based_edge_counter = 0;
@@ -362,189 +368,318 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
const auto weight_multiplier =
scripting_environment.GetProfileProperties().GetWeightMultiplier();
// The following block generates the edge-based-edges using a parallel processing
// pipeline. Sets of intersection IDs are batched in groups of GRAINSIZE (100)
// `generator_stage`,
// then those groups are processed in parallel `processor_stage`. Finally, results are
// appended to the various buffer vectors by the `output_stage` in the same order
// that the `generator_stage` created them in (tbb::filter::serial_in_order creates this
// guarantee). The order needs to be maintained because we depend on it later in the
// processing pipeline.
{
util::UnbufferedLog log;
util::Percent progress(log, m_node_based_graph->GetNumberOfNodes());
const NodeID node_count = m_node_based_graph->GetNumberOfNodes();
util::Percent progress(log, node_count);
// This counter is used to keep track of how far along we've made it
std::uint64_t nodes_completed = 0;
// going over all nodes (which form the center of an intersection), we compute all
// possible turns along these intersections.
for (const auto node_at_center_of_intersection :
util::irange(0u, m_node_based_graph->GetNumberOfNodes()))
{
progress.PrintStatus(node_at_center_of_intersection);
const auto shape_result =
turn_analysis.ComputeIntersectionShapes(node_at_center_of_intersection);
NodeID current_node = 0;
// 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, but the cost we need to pay
// to
// generate edges here is worth the additional search overhead.
//
// a -> b <-> c
// |
// v
// d
//
// will have:
// a: b,rev=0
// b: a,rev=1 c,rev=0 d,rev=0
// c: b,rev=0
//
// 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))
{
const NodeID node_along_road_entering =
m_node_based_graph->GetTarget(outgoing_edge);
// Handle intersections in sets of 100. The pipeline below has a serial bottleneck
// during the writing phase, so we want to make the parallel workers do more work
// to give the serial final stage time to complete its tasks.
const constexpr unsigned GRAINSIZE = 100;
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);
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]);
intersection = turn_lane_handler.assignTurnLanes(
node_along_road_entering, incoming_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);
const auto entry_class_id = [&](const util::guidance::EntryClass entry_class) {
if (0 == entry_class_hash.count(entry_class))
{
const auto id = static_cast<std::uint16_t>(entry_class_hash.size());
entry_class_hash[entry_class] = id;
return id;
}
else
{
return entry_class_hash.find(entry_class)->second;
}
}(turn_classification.first);
const auto bearing_class_id =
[&](const util::guidance::BearingClass bearing_class) {
if (0 == bearing_class_hash.count(bearing_class))
{
const auto id = static_cast<std::uint32_t>(bearing_class_hash.size());
bearing_class_hash[bearing_class] = id;
return id;
}
else
{
return bearing_class_hash.find(bearing_class)->second;
}
}(turn_classification.second);
bearing_class_by_node_based_node[node_at_center_of_intersection] = bearing_class_id;
for (const auto &turn : intersection)
// First part of the pipeline generates iterator ranges of IDs in sets of GRAINSIZE
tbb::filter_t<void, tbb::blocked_range<NodeID>> generator_stage(
tbb::filter::serial_in_order, [&](tbb::flow_control &fc) -> tbb::blocked_range<NodeID> {
if (current_node < node_count)
{
// only keep valid turns
if (!turn.entry_allowed)
continue;
// only add an edge if turn is not prohibited
const EdgeData &edge_data1 = m_node_based_graph->GetEdgeData(incoming_edge);
const EdgeData &edge_data2 = m_node_based_graph->GetEdgeData(turn.eid);
BOOST_ASSERT(edge_data1.edge_id != edge_data2.edge_id);
BOOST_ASSERT(!edge_data1.reversed);
BOOST_ASSERT(!edge_data2.reversed);
// the following is the core of the loop.
turn_data_container.push_back(
turn.instruction,
turn.lane_data_id,
entry_class_id,
util::guidance::TurnBearing(intersection[0].bearing),
util::guidance::TurnBearing(turn.bearing));
// compute weight and duration penalties
auto is_traffic_light = m_traffic_lights.count(node_at_center_of_intersection);
ExtractionTurn extracted_turn(turn, is_traffic_light);
extracted_turn.source_restricted = edge_data1.restricted;
extracted_turn.target_restricted = edge_data2.restricted;
scripting_environment.ProcessTurn(extracted_turn);
// turn penalties are limited to [-2^15, 2^15) which roughly
// translates to 54 minutes and fits signed 16bit deci-seconds
auto weight_penalty =
boost::numeric_cast<TurnPenalty>(extracted_turn.weight * weight_multiplier);
auto duration_penalty =
boost::numeric_cast<TurnPenalty>(extracted_turn.duration * 10.);
BOOST_ASSERT(SPECIAL_NODEID != edge_data1.edge_id);
BOOST_ASSERT(SPECIAL_NODEID != edge_data2.edge_id);
// NOTE: potential overflow here if we hit 2^32 routable edges
BOOST_ASSERT(m_edge_based_edge_list.size() <=
std::numeric_limits<NodeID>::max());
auto turn_id = m_edge_based_edge_list.size();
auto weight =
boost::numeric_cast<EdgeWeight>(edge_data1.weight + weight_penalty);
auto duration =
boost::numeric_cast<EdgeWeight>(edge_data1.duration + duration_penalty);
m_edge_based_edge_list.emplace_back(edge_data1.edge_id,
edge_data2.edge_id,
turn_id,
weight,
duration,
true,
false);
BOOST_ASSERT(turn_weight_penalties.size() == turn_id);
turn_weight_penalties.push_back(weight_penalty);
BOOST_ASSERT(turn_duration_penalties.size() == turn_id);
turn_duration_penalties.push_back(duration_penalty);
// We write out the mapping between the edge-expanded edges and the
// original nodes. Since each edge represents a possible maneuver, external
// programs can use this to quickly perform updates to edge weights in order
// to penalize certain turns.
// If this edge is 'trivial' -- where the compressed edge corresponds
// exactly to an original OSM segment -- we can pull the turn's preceding
// node ID directly with `node_along_road_entering`; otherwise, we need to
// look up the node immediately preceding the turn from the compressed edge
// container.
const bool isTrivial = m_compressed_edge_container.IsTrivial(incoming_edge);
const auto &from_node =
isTrivial ? node_along_road_entering
: m_compressed_edge_container.GetLastEdgeSourceID(incoming_edge);
const auto &via_node =
m_compressed_edge_container.GetLastEdgeTargetID(incoming_edge);
const auto &to_node =
m_compressed_edge_container.GetFirstEdgeTargetID(turn.eid);
lookup::TurnIndexBlock turn_index_block = {from_node, via_node, to_node};
turn_penalties_index_file.WriteOne(turn_index_block);
auto next_node = std::min(current_node + GRAINSIZE, node_count);
auto result = tbb::blocked_range<NodeID>(current_node, next_node);
current_node = next_node;
return result;
}
}
else
{
fc.stop();
return tbb::blocked_range<NodeID>(node_count, node_count);
}
});
// This struct is the buffered output of the `processor_stage`. This data is
// appended to the various output arrays/files by the `output_stage`.
struct IntersectionData
{
std::size_t nodes_processed = 0;
std::vector<lookup::TurnIndexBlock> turn_indexes;
std::vector<EdgeBasedEdge> edges_list;
std::vector<TurnPenalty> turn_weight_penalties;
std::vector<TurnPenalty> turn_duration_penalties;
std::vector<TurnData> turn_data_container;
};
// Second part of the pipeline is where the intersection analysis is done for
// each intersection
tbb::filter_t<tbb::blocked_range<NodeID>, std::shared_ptr<IntersectionData>>
processor_stage(
tbb::filter::parallel, [&](const tbb::blocked_range<NodeID> &intersection_node_range) {
auto buffer = std::make_shared<IntersectionData>();
buffer->nodes_processed =
intersection_node_range.end() - intersection_node_range.begin();
// If we get fed a 0-length range for some reason, we can just return right away
if (buffer->nodes_processed == 0)
return buffer;
for (auto node_at_center_of_intersection = intersection_node_range.begin(),
end = intersection_node_range.end();
node_at_center_of_intersection < end;
++node_at_center_of_intersection)
{
// 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 shape_result =
turn_analysis.ComputeIntersectionShapes(node_at_center_of_intersection);
// 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,
// but the cost we need to pay to generate edges here is worth the additional
// search overhead.
//
// a -> b <-> c
// |
// v
// d
//
// will have:
// a: b,rev=0
// b: a,rev=1 c,rev=0 d,rev=0
// c: b,rev=0
//
// 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))
{
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);
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]);
intersection = turn_lane_handler.assignTurnLanes(
node_along_road_entering, incoming_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);
const auto entry_class_id =
entry_class_hash.ConcurrentFindOrAdd(turn_classification.first);
const auto bearing_class_id =
bearing_class_hash.ConcurrentFindOrAdd(turn_classification.second);
// 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;
for (const auto &turn : intersection)
{
// only keep valid turns
if (!turn.entry_allowed)
continue;
// only add an edge if turn is not prohibited
const EdgeData &edge_data1 =
m_node_based_graph->GetEdgeData(incoming_edge);
const EdgeData &edge_data2 = m_node_based_graph->GetEdgeData(turn.eid);
BOOST_ASSERT(edge_data1.edge_id != edge_data2.edge_id);
BOOST_ASSERT(!edge_data1.reversed);
BOOST_ASSERT(!edge_data2.reversed);
// the following is the core of the loop.
buffer->turn_data_container.push_back(
{turn.instruction,
turn.lane_data_id,
entry_class_id,
util::guidance::TurnBearing(intersection[0].bearing),
util::guidance::TurnBearing(turn.bearing)});
// compute weight and duration penalties
auto is_traffic_light =
m_traffic_lights.count(node_at_center_of_intersection);
ExtractionTurn extracted_turn(turn, is_traffic_light);
extracted_turn.source_restricted = edge_data1.restricted;
extracted_turn.target_restricted = edge_data2.restricted;
scripting_environment.ProcessTurn(extracted_turn);
// turn penalties are limited to [-2^15, 2^15) which roughly
// translates to 54 minutes and fits signed 16bit deci-seconds
auto weight_penalty = boost::numeric_cast<TurnPenalty>(
extracted_turn.weight * weight_multiplier);
auto duration_penalty =
boost::numeric_cast<TurnPenalty>(extracted_turn.duration * 10.);
BOOST_ASSERT(SPECIAL_NODEID != edge_data1.edge_id);
BOOST_ASSERT(SPECIAL_NODEID != edge_data2.edge_id);
// auto turn_id = m_edge_based_edge_list.size();
auto weight =
boost::numeric_cast<EdgeWeight>(edge_data1.weight + weight_penalty);
auto duration = boost::numeric_cast<EdgeWeight>(edge_data1.duration +
duration_penalty);
buffer->edges_list.emplace_back(
edge_data1.edge_id,
edge_data2.edge_id,
SPECIAL_NODEID, // This will be updated once the main loop
// completes!
weight,
duration,
true,
false);
BOOST_ASSERT(buffer->turn_weight_penalties.size() ==
buffer->edges_list.size() - 1);
buffer->turn_weight_penalties.push_back(weight_penalty);
BOOST_ASSERT(buffer->turn_duration_penalties.size() ==
buffer->edges_list.size() - 1);
buffer->turn_duration_penalties.push_back(duration_penalty);
// We write out the mapping between the edge-expanded edges and the
// original nodes. Since each edge represents a possible maneuver,
// external programs can use this to quickly perform updates to edge
// weights in order to penalize certain turns.
// If this edge is 'trivial' -- where the compressed edge corresponds
// exactly to an original OSM segment -- we can pull the turn's
// preceding node ID directly with `node_along_road_entering`;
// otherwise, we need to look up the node immediately preceding the turn
// from the compressed edge container.
const bool isTrivial =
m_compressed_edge_container.IsTrivial(incoming_edge);
const auto &from_node =
isTrivial ? node_along_road_entering
: m_compressed_edge_container.GetLastEdgeSourceID(
incoming_edge);
const auto &via_node =
m_compressed_edge_container.GetLastEdgeTargetID(incoming_edge);
const auto &to_node =
m_compressed_edge_container.GetFirstEdgeTargetID(turn.eid);
buffer->turn_indexes.push_back({from_node, via_node, to_node});
}
}
}
return buffer;
});
// Because we write TurnIndexBlock data as we go, we'll
// buffer them into groups of 1000 to reduce the syscall
// count by 1000x. This doesn't need much memory, but
// greatly reduces the syscall overhead of writing lots
// of small objects
const constexpr int TURN_INDEX_WRITE_BUFFER_SIZE = 1000;
std::vector<lookup::TurnIndexBlock> turn_indexes_write_buffer;
turn_indexes_write_buffer.reserve(TURN_INDEX_WRITE_BUFFER_SIZE);
// Last part of the pipeline puts all the calculated data into the serial buffers
tbb::filter_t<std::shared_ptr<IntersectionData>, void> output_stage(
tbb::filter::serial_in_order, [&](const std::shared_ptr<IntersectionData> buffer) {
nodes_completed += buffer->nodes_processed;
progress.PrintStatus(nodes_completed);
// NOTE: potential overflow here if we hit 2^32 routable edges
m_edge_based_edge_list.append(buffer->edges_list.begin(), buffer->edges_list.end());
BOOST_ASSERT(m_edge_based_edge_list.size() <= std::numeric_limits<NodeID>::max());
turn_weight_penalties.insert(turn_weight_penalties.end(),
buffer->turn_weight_penalties.begin(),
buffer->turn_weight_penalties.end());
turn_duration_penalties.insert(turn_duration_penalties.end(),
buffer->turn_duration_penalties.begin(),
buffer->turn_duration_penalties.end());
turn_data_container.append(buffer->turn_data_container);
turn_indexes_write_buffer.insert(turn_indexes_write_buffer.end(),
buffer->turn_indexes.begin(),
buffer->turn_indexes.end());
// Buffer writes to reduce syscall count
if (turn_indexes_write_buffer.size() >= TURN_INDEX_WRITE_BUFFER_SIZE)
{
turn_penalties_index_file.WriteFrom(turn_indexes_write_buffer.data(),
turn_indexes_write_buffer.size());
turn_indexes_write_buffer.clear();
}
});
// Now, execute the pipeline. The value of "5" here was chosen by experimentation
// on a 16-CPU machine and seemed to give the best performance. This value needs
// to be balanced with the GRAINSIZE above - ideally, the pipeline puts as much work
// as possible in the `intersection_handler` step so that those parallel workers don't
// get blocked too much by the slower (io-performing) `buffer_storage`
tbb::parallel_pipeline(tbb::task_scheduler_init::default_num_threads() * 5,
generator_stage & processor_stage & output_stage);
// Flush the turn_indexes_write_buffer if it's not empty
if (!turn_indexes_write_buffer.empty())
{
turn_penalties_index_file.WriteFrom(turn_indexes_write_buffer.data(),
turn_indexes_write_buffer.size());
turn_indexes_write_buffer.clear();
}
}
util::Log() << "Reunmbering turns";
// Now, update the turn_id property on every EdgeBasedEdge - it will equal the
// position in the m_edge_based_edge_list array for each object.
tbb::parallel_for(tbb::blocked_range<NodeID>(0, m_edge_based_edge_list.size()),
[this](const tbb::blocked_range<NodeID> &range) {
for (auto x = range.begin(), end = range.end(); x != end; ++x)
{
m_edge_based_edge_list[x].data.turn_id = x;
}
});
// write weight penalties per turn
BOOST_ASSERT(turn_weight_penalties.size() == turn_duration_penalties.size());
{
@@ -559,17 +694,17 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
storage::serialization::write(writer, turn_duration_penalties);
}
util::Log() << "Created " << entry_class_hash.size() << " entry classes and "
<< bearing_class_hash.size() << " Bearing Classes";
util::Log() << "Created " << entry_class_hash.data.size() << " entry classes and "
<< bearing_class_hash.data.size() << " Bearing Classes";
util::Log() << "Writing Turn Lane Data to File...";
{
storage::io::FileWriter writer(turn_lane_data_filename,
storage::io::FileWriter::GenerateFingerprint);
std::vector<util::guidance::LaneTupleIdPair> lane_data(lane_data_map.size());
std::vector<util::guidance::LaneTupleIdPair> lane_data(lane_data_map.data.size());
// extract lane data sorted by ID
for (auto itr : lane_data_map)
for (auto itr : lane_data_map.data)
lane_data[itr.second] = itr.first;
storage::serialization::write(writer, lane_data);
@@ -591,8 +726,8 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
std::vector<util::guidance::BearingClass> EdgeBasedGraphFactory::GetBearingClasses() const
{
std::vector<util::guidance::BearingClass> result(bearing_class_hash.size());
for (const auto &pair : bearing_class_hash)
std::vector<util::guidance::BearingClass> result(bearing_class_hash.data.size());
for (const auto &pair : bearing_class_hash.data)
{
BOOST_ASSERT(pair.second < result.size());
result[pair.second] = pair.first;
@@ -612,8 +747,8 @@ std::vector<BearingClassID> &EdgeBasedGraphFactory::GetBearingClassIds()
std::vector<util::guidance::EntryClass> EdgeBasedGraphFactory::GetEntryClasses() const
{
std::vector<util::guidance::EntryClass> result(entry_class_hash.size());
for (const auto &pair : entry_class_hash)
std::vector<util::guidance::EntryClass> result(entry_class_hash.data.size());
for (const auto &pair : entry_class_hash.data)
{
BOOST_ASSERT(pair.second < result.size());
result[pair.second] = pair.first;
+67 -46
View File
@@ -33,12 +33,13 @@
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/iterator/function_input_iterator.hpp>
#include <boost/optional/optional.hpp>
#include <boost/scope_exit.hpp>
#include <osmium/io/any_input.hpp>
#include <tbb/concurrent_vector.h>
#include <tbb/pipeline.h>
#include <tbb/task_scheduler_init.h>
#include <cstdlib>
@@ -76,8 +77,9 @@ transformTurnLaneMapIntoArrays(const guidance::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.size() + 2); // empty ID + sentinel
for (auto entry = turn_lane_map.begin(); entry != turn_lane_map.end(); ++entry)
std::vector<std::uint32_t> turn_lane_offsets(turn_lane_map.data.size() +
2); // empty ID + 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();
// inplace prefix sum
@@ -85,7 +87,7 @@ transformTurnLaneMapIntoArrays(const guidance::LaneDescriptionMap &turn_lane_map
// allocate the current masks
std::vector<guidance::TurnLaneType::Mask> turn_lane_masks(turn_lane_offsets.back());
for (auto entry = turn_lane_map.begin(); entry != turn_lane_map.end(); ++entry)
for (auto entry = turn_lane_map.data.begin(); entry != turn_lane_map.data.end(); ++entry)
std::copy(entry->first.begin(),
entry->first.end(),
turn_lane_masks.begin() + turn_lane_offsets[entry->second]);
@@ -252,11 +254,6 @@ std::vector<TurnRestriction> Extractor::ParseOSMData(ScriptingEnvironment &scrip
timestamp_file.WriteFrom(timestamp.c_str(), timestamp.length());
// initialize vectors holding parsed objects
tbb::concurrent_vector<std::pair<std::size_t, ExtractionNode>> resulting_nodes;
tbb::concurrent_vector<std::pair<std::size_t, ExtractionWay>> resulting_ways;
tbb::concurrent_vector<boost::optional<InputRestrictionContainer>> resulting_restrictions;
std::vector<std::string> restrictions = scripting_environment.GetRestrictions();
// setup restriction parser
const RestrictionParser restriction_parser(
@@ -264,46 +261,70 @@ std::vector<TurnRestriction> Extractor::ParseOSMData(ScriptingEnvironment &scrip
config.parse_conditionals,
restrictions);
// create a vector of iterators into the buffer
for (std::vector<osmium::memory::Buffer::const_iterator> osm_elements;
const osmium::memory::Buffer buffer = reader.read();
osm_elements.clear())
std::mutex process_mutex;
using SharedBuffer = std::shared_ptr<const osmium::memory::Buffer>;
struct ParsedBuffer
{
for (auto iter = std::begin(buffer), end = std::end(buffer); iter != end; ++iter)
{
osm_elements.push_back(iter);
}
SharedBuffer buffer;
std::vector<std::pair<const osmium::Node &, ExtractionNode>> resulting_nodes;
std::vector<std::pair<const osmium::Way &, ExtractionWay>> resulting_ways;
std::vector<boost::optional<InputRestrictionContainer>> resulting_restrictions;
};
// clear resulting vectors
resulting_nodes.clear();
resulting_ways.clear();
resulting_restrictions.clear();
tbb::filter_t<void, SharedBuffer> buffer_reader(
tbb::filter::serial_in_order, [&](tbb::flow_control &fc) {
if (auto buffer = reader.read())
{
return std::make_shared<const osmium::memory::Buffer>(std::move(buffer));
}
else
{
fc.stop();
return SharedBuffer{};
}
});
tbb::filter_t<SharedBuffer, std::shared_ptr<ParsedBuffer>> buffer_transform(
tbb::filter::parallel, [&](const SharedBuffer buffer) {
if (!buffer)
return std::shared_ptr<ParsedBuffer>{};
scripting_environment.ProcessElements(osm_elements,
restriction_parser,
resulting_nodes,
resulting_ways,
resulting_restrictions);
auto parsed_buffer = std::make_shared<ParsedBuffer>();
parsed_buffer->buffer = buffer;
scripting_environment.ProcessElements(*buffer,
restriction_parser,
parsed_buffer->resulting_nodes,
parsed_buffer->resulting_ways,
parsed_buffer->resulting_restrictions);
return parsed_buffer;
});
tbb::filter_t<std::shared_ptr<ParsedBuffer>, void> buffer_storage(
tbb::filter::serial_in_order, [&](const std::shared_ptr<ParsedBuffer> parsed_buffer) {
if (!parsed_buffer)
return;
number_of_nodes += parsed_buffer->resulting_nodes.size();
// put parsed objects thru extractor callbacks
for (const auto &result : parsed_buffer->resulting_nodes)
{
extractor_callbacks->ProcessNode(result.first, result.second);
}
number_of_ways += parsed_buffer->resulting_ways.size();
for (const auto &result : parsed_buffer->resulting_ways)
{
extractor_callbacks->ProcessWay(result.first, result.second);
}
number_of_relations += parsed_buffer->resulting_restrictions.size();
for (const auto &result : parsed_buffer->resulting_restrictions)
{
extractor_callbacks->ProcessRestriction(result);
}
});
// Number of pipeline tokens that yielded the best speedup was about 1.5 * num_cores
tbb::parallel_pipeline(tbb::task_scheduler_init::default_num_threads() * 1.5,
buffer_reader & buffer_transform & buffer_storage);
number_of_nodes += resulting_nodes.size();
// put parsed objects thru extractor callbacks
for (const auto &result : resulting_nodes)
{
extractor_callbacks->ProcessNode(
static_cast<const osmium::Node &>(*(osm_elements[result.first])), result.second);
}
number_of_ways += resulting_ways.size();
for (const auto &result : resulting_ways)
{
extractor_callbacks->ProcessWay(
static_cast<const osmium::Way &>(*(osm_elements[result.first])), result.second);
}
number_of_relations += resulting_restrictions.size();
for (const auto &result : resulting_restrictions)
{
extractor_callbacks->ProcessRestriction(result);
}
}
TIMER_STOP(parsing);
util::Log() << "Parsing finished after " << TIMER_SEC(parsing) << " seconds";
@@ -311,7 +332,7 @@ std::vector<TurnRestriction> Extractor::ParseOSMData(ScriptingEnvironment &scrip
<< " ways, and " << number_of_relations << " relations";
// take control over the turn lane map
turn_lane_map = extractor_callbacks->moveOutLaneDescriptionMap();
turn_lane_map.data = extractor_callbacks->moveOutLaneDescriptionMap().data;
extractor_callbacks.reset();
+2 -13
View File
@@ -41,7 +41,7 @@ ExtractorCallbacks::ExtractorCallbacks(ExtractionContainers &extraction_containe
{
// we reserved 0, 1, 2, 3 for the empty case
string_map[MapKey("", "", "", "")] = 0;
lane_description_map[TurnLaneDescription()] = 0;
lane_description_map.data[TurnLaneDescription()] = 0;
}
/**
@@ -251,18 +251,7 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti
return INVALID_LANE_DESCRIPTIONID;
TurnLaneDescription lane_description = laneStringToDescription(std::move(lane_string));
const auto lane_description_itr = lane_description_map.find(lane_description);
if (lane_description_itr == lane_description_map.end())
{
const LaneDescriptionID new_id =
boost::numeric_cast<LaneDescriptionID>(lane_description_map.size());
lane_description_map[lane_description] = new_id;
return new_id;
}
else
{
return lane_description_itr->second;
}
return lane_description_map.ConcurrentFindOrAdd(lane_description);
};
// Deduplicates street names, refs, destinations, pronunciation based on the string_map.
+45 -43
View File
@@ -111,43 +111,6 @@ void GraphCompressor::Compress(const std::unordered_set<NodeID> &barrier_nodes,
BOOST_ASSERT(graph.GetEdgeData(forward_e2).name_id ==
graph.GetEdgeData(reverse_e2).name_id);
// Do not compress edge if it crosses a traffic signal.
// This can't be done in CanCombineWith, becase we only store the
// traffic signals in the `traffic_lights` list, which EdgeData
// doesn't have access to.
const bool has_node_penalty = traffic_lights.find(node_v) != traffic_lights.end();
if (has_node_penalty)
continue;
// Get weights before graph is modified
const auto forward_weight1 = fwd_edge_data1.weight;
const auto forward_weight2 = fwd_edge_data2.weight;
const auto forward_duration1 = fwd_edge_data1.duration;
const auto forward_duration2 = fwd_edge_data2.duration;
BOOST_ASSERT(0 != forward_weight1);
BOOST_ASSERT(0 != forward_weight2);
const auto reverse_weight1 = rev_edge_data1.weight;
const auto reverse_weight2 = rev_edge_data2.weight;
const auto reverse_duration1 = rev_edge_data1.duration;
const auto reverse_duration2 = rev_edge_data2.duration;
BOOST_ASSERT(0 != reverse_weight1);
BOOST_ASSERT(0 != reverse_weight2);
// add weight of e2's to e1
graph.GetEdgeData(forward_e1).weight += forward_weight2;
graph.GetEdgeData(reverse_e1).weight += reverse_weight2;
// add duration of e2's to e1
graph.GetEdgeData(forward_e1).duration += forward_duration2;
graph.GetEdgeData(reverse_e1).duration += reverse_duration2;
// extend e1's to targets of e2's
graph.SetTarget(forward_e1, node_w);
graph.SetTarget(reverse_e1, node_u);
/*
* Remember Lane Data for compressed parts. This handles scenarios where lane-data
* is
@@ -185,12 +148,51 @@ void GraphCompressor::Compress(const std::unordered_set<NodeID> &barrier_nodes,
return front;
return back;
};
graph.GetEdgeData(forward_e1).lane_description_id =
selectLaneID(graph.GetEdgeData(forward_e1).lane_description_id,
fwd_edge_data2.lane_description_id);
graph.GetEdgeData(reverse_e1).lane_description_id =
selectLaneID(graph.GetEdgeData(reverse_e1).lane_description_id,
rev_edge_data2.lane_description_id);
graph.GetEdgeData(forward_e1).lane_description_id = selectLaneID(
fwd_edge_data1.lane_description_id, fwd_edge_data2.lane_description_id);
graph.GetEdgeData(reverse_e1).lane_description_id = selectLaneID(
rev_edge_data1.lane_description_id, rev_edge_data2.lane_description_id);
graph.GetEdgeData(forward_e2).lane_description_id = selectLaneID(
fwd_edge_data2.lane_description_id, fwd_edge_data1.lane_description_id);
graph.GetEdgeData(reverse_e2).lane_description_id = selectLaneID(
rev_edge_data2.lane_description_id, rev_edge_data1.lane_description_id);
// Do not compress edge if it crosses a traffic signal.
// This can't be done in CanCombineWith, becase we only store the
// traffic signals in the `traffic_lights` list, which EdgeData
// doesn't have access to.
const bool has_node_penalty = traffic_lights.find(node_v) != traffic_lights.end();
if (has_node_penalty)
continue;
// Get weights before graph is modified
const auto forward_weight1 = fwd_edge_data1.weight;
const auto forward_weight2 = fwd_edge_data2.weight;
const auto forward_duration1 = fwd_edge_data1.duration;
const auto forward_duration2 = fwd_edge_data2.duration;
BOOST_ASSERT(0 != forward_weight1);
BOOST_ASSERT(0 != forward_weight2);
const auto reverse_weight1 = rev_edge_data1.weight;
const auto reverse_weight2 = rev_edge_data2.weight;
const auto reverse_duration1 = rev_edge_data1.duration;
const auto reverse_duration2 = rev_edge_data2.duration;
BOOST_ASSERT(0 != reverse_weight1);
BOOST_ASSERT(0 != reverse_weight2);
// add weight of e2's to e1
graph.GetEdgeData(forward_e1).weight += forward_weight2;
graph.GetEdgeData(reverse_e1).weight += reverse_weight2;
// add duration of e2's to e1
graph.GetEdgeData(forward_e1).duration += forward_duration2;
graph.GetEdgeData(reverse_e1).duration += reverse_duration2;
// extend e1's to targets of e2's
graph.SetTarget(forward_e1, node_w);
graph.SetTarget(reverse_e1, node_u);
// remove e2's (if bidir, otherwise only one)
graph.DeleteEdge(node_v, forward_e2);
@@ -0,0 +1,80 @@
#include "extractor/guidance/driveway_handler.hpp"
#include "util/assert.hpp"
using osrm::extractor::guidance::getTurnDirection;
using osrm::util::angularDeviation;
namespace osrm
{
namespace extractor
{
namespace guidance
{
DrivewayHandler::DrivewayHandler(const IntersectionGenerator &intersection_generator,
const util::NodeBasedDynamicGraph &node_based_graph,
const std::vector<util::Coordinate> &coordinates,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table)
: IntersectionHandler(node_based_graph,
coordinates,
name_table,
street_name_suffix_table,
intersection_generator)
{
}
// The intersection without major roads needs to pass by service roads (bd, be)
// d
// .
// a--->b--->c
// .
// e
bool DrivewayHandler::canProcess(const NodeID /*nid*/,
const EdgeID /*via_eid*/,
const Intersection &intersection) const
{
const auto from_eid = intersection.getUTurnRoad().eid;
if (intersection.size() <= 2 ||
node_based_graph.GetEdgeData(from_eid).road_classification.IsLowPriorityRoadClass())
return false;
auto low_priority_count =
std::count_if(intersection.begin(), intersection.end(), [this](const auto &road) {
return node_based_graph.GetEdgeData(road.eid)
.road_classification.IsLowPriorityRoadClass();
});
// Process intersection if it has two edges with normal priority and one is the entry edge,
// and also has at least one edge with lower priority
return static_cast<std::size_t>(low_priority_count) + 2 == intersection.size();
}
Intersection DrivewayHandler::
operator()(const NodeID nid, const EdgeID source_edge_id, Intersection intersection) const
{
auto road =
std::find_if(intersection.begin() + 1, intersection.end(), [this](const auto &road) {
return !node_based_graph.GetEdgeData(road.eid)
.road_classification.IsLowPriorityRoadClass();
});
(void)nid;
OSRM_ASSERT(road != intersection.end(), coordinates[nid]);
if (road->instruction == TurnInstruction::INVALID())
return intersection;
OSRM_ASSERT(road->instruction.type == TurnType::Turn, coordinates[nid]);
road->instruction.type =
isSameName(source_edge_id, road->eid) ? TurnType::NoTurn : TurnType::NewName;
return intersection;
}
} // namespace guidance
} // namespace extractor
} // namespace osrm
@@ -436,6 +436,19 @@ IntersectionHandler::getNextIntersection(const NodeID at, const EdgeID via) cons
IntersectionViewAndNode{std::move(intersection), intersection_node});
}
bool IntersectionHandler::isSameName(const EdgeID source_edge_id, const EdgeID target_edge_id) const
{
const auto &source_edge_data = node_based_graph.GetEdgeData(source_edge_id);
const auto &target_edge_data = node_based_graph.GetEdgeData(target_edge_id);
return source_edge_data.name_id != EMPTY_NAMEID && //
target_edge_data.name_id != EMPTY_NAMEID && //
!util::guidance::requiresNameAnnounced(source_edge_data.name_id,
target_edge_data.name_id,
name_table,
street_name_suffix_table); //
}
} // namespace guidance
} // namespace extractor
} // namespace osrm
+2 -12
View File
@@ -473,17 +473,7 @@ operator()(const NodeID /*nid*/, const EdgeID source_edge_id, Intersection inter
// In those cases the obvious non-Sliproad is now obvious and we discard the Fork turn type.
if (sliproad_found && main_road.instruction.type == TurnType::Fork)
{
const auto &source_edge_data = node_based_graph.GetEdgeData(source_edge_id);
const auto &main_road_data = node_based_graph.GetEdgeData(main_road.eid);
const auto same_name = source_edge_data.name_id != EMPTY_NAMEID && //
main_road_data.name_id != EMPTY_NAMEID && //
!util::guidance::requiresNameAnnounced(source_edge_data.name_id,
main_road_data.name_id,
name_table,
street_name_suffix_table); //
if (same_name)
if (isSameName(source_edge_id, main_road.eid))
{
if (angularDeviation(main_road.angle, STRAIGHT_ANGLE) < 5)
intersection[*obvious].instruction.type = TurnType::Suppressed;
@@ -492,7 +482,7 @@ operator()(const NodeID /*nid*/, const EdgeID source_edge_id, Intersection inter
intersection[*obvious].instruction.direction_modifier =
getTurnDirection(intersection[*obvious].angle);
}
else if (main_road_data.name_id != EMPTY_NAMEID)
else if (node_based_graph.GetEdgeData(main_road.eid).name_id != EMPTY_NAMEID)
{
intersection[*obvious].instruction.type = TurnType::NewName;
intersection[*obvious].instruction.direction_modifier =
+13 -2
View File
@@ -68,7 +68,12 @@ TurnAnalysis::TurnAnalysis(const util::NodeBasedDynamicGraph &node_based_graph,
node_based_graph,
coordinates,
name_table,
street_name_suffix_table)
street_name_suffix_table),
driveway_handler(intersection_generator,
node_based_graph,
coordinates,
name_table,
street_name_suffix_table)
{
}
@@ -132,8 +137,14 @@ Intersection TurnAnalysis::AssignTurnTypes(const NodeID node_prior_to_intersecti
// set initial defaults for normal turns and modifier based on angle
intersection =
setTurnTypes(node_prior_to_intersection, entering_via_edge, std::move(intersection));
if (motorway_handler.canProcess(
if (driveway_handler.canProcess(
node_prior_to_intersection, entering_via_edge, intersection))
{
intersection = driveway_handler(
node_prior_to_intersection, entering_via_edge, std::move(intersection));
}
else if (motorway_handler.canProcess(
node_prior_to_intersection, entering_via_edge, intersection))
{
intersection = motorway_handler(
node_prior_to_intersection, entering_via_edge, std::move(intersection));
+5 -16
View File
@@ -34,8 +34,8 @@ std::size_t getNumberOfTurns(const Intersection &intersection)
} // namespace
TurnLaneHandler::TurnLaneHandler(const util::NodeBasedDynamicGraph &node_based_graph,
std::vector<std::uint32_t> &turn_lane_offsets,
std::vector<TurnLaneType::Mask> &turn_lane_masks,
const std::vector<std::uint32_t> &turn_lane_offsets,
const std::vector<TurnLaneType::Mask> &turn_lane_masks,
LaneDescriptionMap &lane_description_map,
const TurnAnalysis &turn_analysis,
util::guidance::LaneDataIdMap &id_map)
@@ -180,7 +180,7 @@ TurnLaneScenario TurnLaneHandler::deduceScenario(const NodeID at,
(intersection.size() == 2 &&
((lane_description_id != INVALID_LANE_DESCRIPTIONID &&
lane_description_id ==
node_based_graph.GetEdgeData(intersection[1].eid).lane_description_id) ||
node_based_graph.GetEdgeData(intersection[1].eid).lane_description_id) &&
angularDeviation(intersection[1].angle, STRAIGHT_ANGLE) < FUZZY_ANGLE_DIFFERENCE));
if (is_going_straight_and_turns_continue)
@@ -781,19 +781,8 @@ Intersection TurnLaneHandler::handleSliproadTurn(Intersection intersection,
}
}
const auto combined_id = [&]() {
auto itr = lane_description_map.find(combined_description);
if (lane_description_map.find(combined_description) == lane_description_map.end())
{
const auto new_id = boost::numeric_cast<LaneDescriptionID>(lane_description_map.size());
lane_description_map[combined_description] = new_id;
return new_id;
}
else
{
return itr->second;
}
}();
const auto combined_id = lane_description_map.ConcurrentFindOrAdd(combined_description);
return simpleMatchTuplesToTurns(std::move(intersection), lane_data, combined_id);
}
+1 -10
View File
@@ -209,16 +209,7 @@ Intersection triviallyMatchLanesToTurns(Intersection intersection,
util::guidance::LaneTupleIdPair key{{LaneID(data.to - data.from + 1), data.from},
lane_string_id};
auto lane_data_id = boost::numeric_cast<LaneDataID>(lane_data_to_id.size());
const auto it = lane_data_to_id.find(key);
if (it == lane_data_to_id.end())
lane_data_to_id.insert({key, lane_data_id});
else
lane_data_id = it->second;
// set lane id instead after the switch:
road.lane_data_id = lane_data_id;
road.lane_data_id = lane_data_to_id.ConcurrentFindOrAdd(key);
};
if (!lane_data.empty() && lane_data.front().tag == TurnLaneType::uturn)
+2 -1
View File
@@ -108,7 +108,8 @@ int SourceContainer::LoadRasterSource(const std::string &path_string,
boost::filesystem::path filepath(path_string);
if (!boost::filesystem::exists(filepath))
{
throw util::exception(path_string + " does not exist" + SOURCE_REF);
throw util::RuntimeError(
path_string, ErrorCode::FileOpenError, SOURCE_REF, "File not found");
}
RasterGrid rasterData{filepath, ncols, nrows};
+60 -67
View File
@@ -249,7 +249,9 @@ void Sol2ScriptingEnvironment::InitContext(LuaScriptingContext &context)
"max_turn_weight",
sol::property(&ProfileProperties::GetMaxTurnWeight),
"force_split_edges",
&ProfileProperties::force_split_edges);
&ProfileProperties::force_split_edges,
"call_tagless_node_function",
&ProfileProperties::call_tagless_node_function);
context.state.new_usertype<std::vector<std::string>>(
"vector",
@@ -427,15 +429,16 @@ void Sol2ScriptingEnvironment::InitContext(LuaScriptingContext &context)
context.state.script_file(file_name);
sol::function turn_function = context.state["turn_function"];
sol::function node_function = context.state["node_function"];
sol::function way_function = context.state["way_function"];
sol::function segment_function = context.state["segment_function"];
// cache references to functions for faster execution
context.turn_function = context.state["turn_function"];
context.node_function = context.state["node_function"];
context.way_function = context.state["way_function"];
context.segment_function = context.state["segment_function"];
context.has_turn_penalty_function = turn_function.valid();
context.has_node_function = node_function.valid();
context.has_way_function = way_function.valid();
context.has_segment_function = segment_function.valid();
context.has_turn_penalty_function = context.turn_function.valid();
context.has_node_function = context.node_function.valid();
context.has_way_function = context.way_function.valid();
context.has_segment_function = context.segment_function.valid();
// Check profile API version
auto maybe_version = context.state.get<sol::optional<int>>("api_version");
@@ -486,59 +489,54 @@ LuaScriptingContext &Sol2ScriptingEnvironment::GetSol2Context()
}
void Sol2ScriptingEnvironment::ProcessElements(
const std::vector<osmium::memory::Buffer::const_iterator> &osm_elements,
const osmium::memory::Buffer &buffer,
const RestrictionParser &restriction_parser,
tbb::concurrent_vector<std::pair<std::size_t, ExtractionNode>> &resulting_nodes,
tbb::concurrent_vector<std::pair<std::size_t, ExtractionWay>> &resulting_ways,
tbb::concurrent_vector<boost::optional<InputRestrictionContainer>> &resulting_restrictions)
std::vector<std::pair<const osmium::Node &, ExtractionNode>> &resulting_nodes,
std::vector<std::pair<const osmium::Way &, ExtractionWay>> &resulting_ways,
std::vector<boost::optional<InputRestrictionContainer>> &resulting_restrictions)
{
// parse OSM entities in parallel, store in resulting vectors
tbb::parallel_for(
tbb::blocked_range<std::size_t>(0, osm_elements.size()),
[&](const tbb::blocked_range<std::size_t> &range) {
ExtractionNode result_node;
ExtractionWay result_way;
std::vector<InputRestrictionContainer> result_res;
auto &local_context = this->GetSol2Context();
ExtractionNode result_node;
ExtractionWay result_way;
std::vector<InputRestrictionContainer> result_res;
auto &local_context = this->GetSol2Context();
for (auto x = range.begin(), end = range.end(); x != end; ++x)
for (auto entity = buffer.cbegin(), end = buffer.cend(); entity != end; ++entity)
{
switch (entity->type())
{
case osmium::item_type::node:
result_node.clear();
if (local_context.has_node_function &&
(!static_cast<const osmium::Node &>(*entity).tags().empty() ||
local_context.properties.call_tagless_node_function))
{
const auto entity = osm_elements[x];
switch (entity->type())
{
case osmium::item_type::node:
result_node.clear();
if (local_context.has_node_function)
{
local_context.ProcessNode(static_cast<const osmium::Node &>(*entity),
result_node);
}
resulting_nodes.push_back(std::make_pair(x, std::move(result_node)));
break;
case osmium::item_type::way:
result_way.clear();
if (local_context.has_way_function)
{
local_context.ProcessWay(static_cast<const osmium::Way &>(*entity),
result_way);
}
resulting_ways.push_back(std::make_pair(x, std::move(result_way)));
break;
case osmium::item_type::relation:
result_res.clear();
result_res =
restriction_parser.TryParse(static_cast<const osmium::Relation &>(*entity));
for (const InputRestrictionContainer &r : result_res)
{
resulting_restrictions.push_back(r);
}
break;
default:
break;
}
local_context.ProcessNode(static_cast<const osmium::Node &>(*entity), result_node);
}
});
resulting_nodes.push_back(std::pair<const osmium::Node &, ExtractionNode>(
static_cast<const osmium::Node &>(*entity), std::move(result_node)));
break;
case osmium::item_type::way:
result_way.clear();
if (local_context.has_way_function)
{
local_context.ProcessWay(static_cast<const osmium::Way &>(*entity), result_way);
}
resulting_ways.push_back(std::pair<const osmium::Way &, ExtractionWay>(
static_cast<const osmium::Way &>(*entity), std::move(result_way)));
break;
case osmium::item_type::relation:
result_res.clear();
result_res =
restriction_parser.TryParse(static_cast<const osmium::Relation &>(*entity));
for (const InputRestrictionContainer &r : result_res)
{
resulting_restrictions.push_back(r);
}
break;
default:
break;
}
}
}
std::vector<std::string> Sol2ScriptingEnvironment::GetNameSuffixList()
@@ -590,13 +588,12 @@ void Sol2ScriptingEnvironment::ProcessTurn(ExtractionTurn &turn)
{
auto &context = GetSol2Context();
sol::function turn_function = context.state["turn_function"];
switch (context.api_version)
{
case 1:
if (context.has_turn_penalty_function)
{
turn_function(turn);
context.turn_function(turn);
// Turn weight falls back to the duration value in deciseconds
// or uses the extracted unit-less weight value
@@ -611,7 +608,7 @@ void Sol2ScriptingEnvironment::ProcessTurn(ExtractionTurn &turn)
if (turn.turn_type != guidance::TurnType::NoTurn)
{
// Get turn duration and convert deci-seconds to seconds
turn.duration = static_cast<double>(turn_function(turn.angle)) / 10.;
turn.duration = static_cast<double>(context.turn_function(turn.angle)) / 10.;
BOOST_ASSERT(turn.weight == 0);
// add U-turn penalty
@@ -642,14 +639,14 @@ void Sol2ScriptingEnvironment::ProcessSegment(ExtractionSegment &segment)
if (context.has_segment_function)
{
sol::function segment_function = context.state["segment_function"];
switch (context.api_version)
{
case 1:
segment_function(segment);
context.segment_function(segment);
break;
case 0:
segment_function(segment.source, segment.target, segment.distance, segment.duration);
context.segment_function(
segment.source, segment.target, segment.distance, segment.duration);
segment.weight = segment.duration; // back-compatibility fallback to duration
break;
}
@@ -660,8 +657,6 @@ void LuaScriptingContext::ProcessNode(const osmium::Node &node, ExtractionNode &
{
BOOST_ASSERT(state.lua_state() != nullptr);
sol::function node_function = state["node_function"];
node_function(node, result);
}
@@ -669,8 +664,6 @@ void LuaScriptingContext::ProcessWay(const osmium::Way &way, ExtractionWay &resu
{
BOOST_ASSERT(state.lua_state() != nullptr);
sol::function way_function = state["way_function"];
way_function(way, result);
}
}
+40 -2
View File
@@ -22,10 +22,33 @@ OSRM::OSRM(engine::EngineConfig &config)
using CoreCH = engine::routing_algorithms::corech::Algorithm;
using MLD = engine::routing_algorithms::mld::Algorithm;
// First, check that necessary core data is available
if (!config.use_shared_memory && !config.storage_config.IsValid())
{
throw util::exception("Required files are missing, cannot continue. Have all the "
"pre-processing steps been run?");
}
else if (config.use_shared_memory)
{
storage::SharedMonitor<storage::SharedDataTimestamp> barrier;
using mutex_type = typename decltype(barrier)::mutex_type;
boost::interprocess::scoped_lock<mutex_type> current_region_lock(barrier.get_mutex());
auto mem = storage::makeSharedMemory(barrier.data().region);
auto layout = reinterpret_cast<storage::DataLayout *>(mem->Ptr());
if (layout->GetBlockSize(storage::DataLayout::NAME_CHAR_DATA) == 0)
throw util::exception(
"No name data loaded, cannot continue. Have you run osrm-datastore to load data?");
}
// Now, check that the algorithm requested can be used with the data
// that's available.
if (config.algorithm == EngineConfig::Algorithm::CoreCH ||
config.algorithm == EngineConfig::Algorithm::CH)
{
bool corech_compatible = engine::Engine<CoreCH>::CheckCompability(config);
bool ch_compatible = engine::Engine<CH>::CheckCompability(config);
// Activate CoreCH if we can because it is faster
if (config.algorithm == EngineConfig::Algorithm::CH && corech_compatible)
@@ -33,10 +56,25 @@ OSRM::OSRM(engine::EngineConfig &config)
config.algorithm = EngineConfig::Algorithm::CoreCH;
}
// throw error if dataset is not usable with CoreCH
// throw error if dataset is not usable with CoreCH or CH
if (config.algorithm == EngineConfig::Algorithm::CoreCH && !corech_compatible)
{
throw util::exception("Dataset is not compatible with CoreCH.");
throw util::RuntimeError("Dataset is not compatible with CoreCH.",
ErrorCode::IncompatibleDataset,
SOURCE_REF);
}
else if (config.algorithm == EngineConfig::Algorithm::CH && !ch_compatible)
{
throw util::exception("Dataset is not compatible with CH");
}
}
else if (config.algorithm == EngineConfig::Algorithm::MLD)
{
bool mld_compatible = engine::Engine<MLD>::CheckCompability(config);
// throw error if dataset is not usable with MLD
if (!mld_compatible)
{
throw util::exception("Dataset is not compatible with MLD.");
}
}
+2 -10
View File
@@ -14,9 +14,9 @@ bool CheckFileList(const std::vector<boost::filesystem::path> &files)
bool success = true;
for (auto &path : files)
{
if (!boost::filesystem::is_regular_file(path))
if (!boost::filesystem::exists(path))
{
util::Log(logWARNING) << "Missing/Broken File: " << path.string();
util::Log(logERROR) << "Missing File: " << path.string();
success = false;
}
}
@@ -62,14 +62,6 @@ bool StorageConfig::IsValid() const
return false;
}
// TODO: add algorithm checks
// CH files
CheckFileList({hsgr_data_path, core_data_path});
// MLD files
CheckFileList({mld_partition_path, mld_storage_path, mld_graph_path});
return true;
}
}
+11 -1
View File
@@ -1,6 +1,7 @@
#include "storage/io.hpp"
#include "osrm/contractor.hpp"
#include "osrm/contractor_config.hpp"
#include "osrm/exception.hpp"
#include "util/log.hpp"
#include "util/timezones.hpp"
#include "util/version.hpp"
@@ -187,9 +188,18 @@ int main(int argc, char *argv[]) try
return EXIT_SUCCESS;
}
catch (const osrm::RuntimeError &e)
{
util::DumpSTXXLStats();
util::DumpMemoryStats();
util::Log(logERROR) << e.what();
return e.GetCode();
}
catch (const std::bad_alloc &e)
{
util::Log(logERROR) << "[exception] " << e.what();
util::DumpSTXXLStats();
util::DumpMemoryStats();
util::Log(logERROR) << e.what();
util::Log(logERROR) << "Please provide more memory or consider using a larger swapfile";
return EXIT_FAILURE;
}
+8
View File
@@ -1,5 +1,6 @@
#include "customizer/customizer.hpp"
#include "osrm/exception.hpp"
#include "util/log.hpp"
#include "util/meminfo.hpp"
#include "util/version.hpp"
@@ -167,8 +168,15 @@ int main(int argc, char *argv[]) try
return exitcode;
}
catch (const osrm::RuntimeError &e)
{
util::DumpMemoryStats();
util::Log(logERROR) << e.what();
return e.GetCode();
}
catch (const std::bad_alloc &e)
{
util::DumpMemoryStats();
util::Log(logERROR) << "[exception] " << e.what();
util::Log(logERROR) << "Please provide more memory or consider using a larger swapfile";
return EXIT_FAILURE;
+17
View File
@@ -1,3 +1,4 @@
#include "osrm/exception.hpp"
#include "osrm/extractor.hpp"
#include "osrm/extractor_config.hpp"
#include "util/log.hpp"
@@ -168,8 +169,24 @@ int main(int argc, char *argv[]) try
return EXIT_SUCCESS;
}
catch (const osrm::RuntimeError &e)
{
util::DumpSTXXLStats();
util::DumpMemoryStats();
util::Log(logERROR) << e.what();
return e.GetCode();
}
catch (const std::system_error &e)
{
util::DumpSTXXLStats();
util::DumpMemoryStats();
util::Log(logERROR) << e.what();
return e.code().value();
}
catch (const std::bad_alloc &e)
{
util::DumpSTXXLStats();
util::DumpMemoryStats();
util::Log(logERROR) << "[exception] " << e.what();
util::Log(logERROR) << "Please provide more memory or consider using a larger swapfile";
return EXIT_FAILURE;
+9
View File
@@ -1,6 +1,7 @@
#include "partition/partition_config.hpp"
#include "partition/partitioner.hpp"
#include "osrm/exception.hpp"
#include "util/log.hpp"
#include "util/meminfo.hpp"
#include "util/timing_util.hpp"
@@ -238,8 +239,16 @@ int main(int argc, char *argv[]) try
return exitcode;
}
catch (const osrm::RuntimeError &e)
{
util::DumpMemoryStats();
util::Log(logERROR) << e.what();
return EXIT_FAILURE;
return e.GetCode();
}
catch (const std::bad_alloc &e)
{
util::DumpMemoryStats();
util::Log(logERROR) << "[exception] " << e.what();
util::Log(logERROR) << "Please provide more memory or consider using a larger swapfile";
return EXIT_FAILURE;
+15 -23
View File
@@ -1,9 +1,11 @@
#include "server/server.hpp"
#include "util/exception.hpp"
#include "util/exception_utils.hpp"
#include "util/log.hpp"
#include "util/meminfo.hpp"
#include "util/version.hpp"
#include "osrm/engine_config.hpp"
#include "osrm/exception.hpp"
#include "osrm/osrm.hpp"
#include "osrm/storage_config.hpp"
@@ -57,7 +59,7 @@ EngineConfig::Algorithm stringToAlgorithm(const std::string &algorithm)
return EngineConfig::Algorithm::CoreCH;
if (algorithm == "MLD")
return EngineConfig::Algorithm::MLD;
throw util::exception("Invalid algorithm name: " + algorithm);
throw util::RuntimeError(algorithm, ErrorCode::UnknownAlgorithm, SOURCE_REF);
}
// generate boost::program_options object for the routing part
@@ -221,33 +223,17 @@ int main(int argc, const char *argv[]) try
{
config.storage_config = storage::StorageConfig(base_path);
}
if (!config.use_shared_memory && !config.storage_config.IsValid())
{
util::Log(logERROR) << "Required files are missing, cannot continue";
return EXIT_FAILURE;
}
if (!config.IsValid())
{
if (base_path.empty() != config.use_shared_memory)
{
util::Log(logWARNING) << "Path settings and shared memory conflicts.";
}
else
{
auto required_files = {config.storage_config.ram_index_path,
config.storage_config.file_index_path,
config.storage_config.hsgr_data_path,
config.storage_config.node_based_nodes_data_path,
config.storage_config.edge_based_nodes_data_path,
config.storage_config.edges_data_path,
config.storage_config.core_data_path,
config.storage_config.geometries_path,
config.storage_config.datasource_indexes_path,
config.storage_config.names_data_path,
config.storage_config.properties_path};
for (auto file : required_files)
{
if (!boost::filesystem::is_regular_file(file))
{
util::Log(logWARNING) << file << " is not found";
}
}
}
return EXIT_FAILURE;
}
config.algorithm = stringToAlgorithm(algorithm);
@@ -331,8 +317,14 @@ int main(int argc, const char *argv[]) try
routing_server.reset();
util::Log() << "shutdown completed";
}
catch (const osrm::RuntimeError &e)
{
util::Log(logERROR) << e.what();
return e.GetCode();
}
catch (const std::bad_alloc &e)
{
util::DumpMemoryStats();
util::Log(logWARNING) << "[exception] " << e.what();
util::Log(logWARNING) << "Please provide more memory or consider using a larger swapfile";
return EXIT_FAILURE;
+8 -1
View File
@@ -1,8 +1,9 @@
#include "storage/shared_memory.hpp"
#include "storage/shared_monitor.hpp"
#include "storage/storage.hpp"
#include "util/exception.hpp"
#include "osrm/exception.hpp"
#include "util/log.hpp"
#include "util/meminfo.hpp"
#include "util/typedefs.hpp"
#include "util/version.hpp"
@@ -172,8 +173,14 @@ int main(const int argc, const char *argv[]) try
return storage.Run(max_wait);
}
catch (const osrm::RuntimeError &e)
{
util::Log(logERROR) << e.what();
return e.GetCode();
}
catch (const std::bad_alloc &e)
{
util::DumpMemoryStats();
util::Log(logERROR) << "[exception] " << e.what();
util::Log(logERROR) << "Please provide more memory or disable locking the virtual "
"address space (note: this makes OSRM swap, i.e. slow)";
+1
View File
@@ -17,5 +17,6 @@ namespace util
{
void exception::anchor() const {}
void RuntimeError::anchor() const {}
}
}
+19 -3
View File
@@ -23,9 +23,12 @@ test('constructor: does not accept more than one parameter', function(assert) {
});
test('constructor: throws if necessary files do not exist', function(assert) {
assert.plan(1);
assert.throws(function() { new OSRM("missing.osrm"); },
/Error opening missing.osrm.names/);
assert.plan(2);
assert.throws(function() { new OSRM('missing.osrm'); },
/Required files are missing, cannot continue/);
assert.throws(function() { new OSRM({path: 'missing.osrm', algorithm: 'MLD'}); },
/Required files are missing, cannot continue/);
});
test('constructor: takes a shared memory argument', function(assert) {
@@ -82,6 +85,19 @@ test('constructor: loads CoreCH if given as algorithm', function(assert) {
assert.ok(osrm);
});
test('constructor: autoswitches to CoreCH for a CH dataset if capable', function(assert) {
assert.plan(1);
var osrm = new OSRM({algorithm: 'CH', path: monaco_corech_path});
assert.ok(osrm);
});
test('constructor: throws if data doesn\'t match algorithm', function(assert) {
assert.plan(3);
assert.throws(function() { new OSRM({algorithm: 'CoreCH', path: monaco_mld_path}); });
assert.throws(function() { new OSRM({algorithm: 'CoreCH', path: monaco_path}); });
assert.throws(function() { new OSRM({algorithm: 'MLD', path: monaco_path}); });
});
require('./route.js');
require('./trip.js');
require('./match.js');
+28 -1
View File
@@ -133,7 +133,9 @@ test('match: match in Monaco with all options', function(assert) {
steps: true,
annotations: true,
overview: 'false',
geometries: 'geojson'
geometries: 'geojson',
gaps: 'split',
tidy: false
};
osrm.match(options, function(err, response) {
assert.ifError(err);
@@ -196,3 +198,28 @@ test('match: throws on invalid timestamps param', function(assert) {
assert.throws(function() { osrm.match(options, function(err, response) {}) },
/Timestamp array must have the same size as the coordinates array/);
});
test('match: throws on invalid gaps param', function(assert) {
assert.plan(2);
var osrm = new OSRM(data_path);
var options = {
coordinates: three_test_coordinates,
gaps: ['invalid gaps param']
};
assert.throws(function() { osrm.match(options, function(err, response) {}) },
/Gaps must be a string: \[split, ignore\]/);
options.gaps = 'invalid gaps param';
assert.throws(function() { osrm.match(options, function(err, response) {}) },
/'gaps' param must be one of \[split, ignore\]/);
});
test('match: throws on invalid tidy param', function(assert) {
assert.plan(1);
var osrm = new OSRM(data_path);
var options = {
coordinates: three_test_coordinates,
tidy: 'invalid tidy param'
};
assert.throws(function() { osrm.match(options, function(err, response) {}) },
/tidy must be of type Boolean/);
});
+49
View File
@@ -0,0 +1,49 @@
#include "engine/polyline_compressor.hpp"
#include "util/coordinate.hpp"
#include <boost/test/test_case_template.hpp>
#include <boost/test/unit_test.hpp>
#include <string>
#include <vector>
BOOST_AUTO_TEST_SUITE(polyline_compression)
BOOST_AUTO_TEST_CASE(polyline5_test_case)
{
using namespace osrm::engine;
using namespace osrm::util;
const std::vector<Coordinate> coords({{FixedLongitude{-73990171}, FixedLatitude{40714701}},
{FixedLongitude{-73991801}, FixedLatitude{40717571}},
{FixedLongitude{-73985751}, FixedLatitude{40715651}}});
const std::vector<Coordinate> coords_truncated(
{{FixedLongitude{-73990170}, FixedLatitude{40714700}},
{FixedLongitude{-73991800}, FixedLatitude{40717570}},
{FixedLongitude{-73985750}, FixedLatitude{40715650}}});
BOOST_CHECK_EQUAL(encodePolyline(coords.begin(), coords.end()), "{aowFperbM}PdI~Jyd@");
BOOST_CHECK(std::equal(coords_truncated.begin(),
coords_truncated.end(),
decodePolyline(encodePolyline(coords.begin(), coords.end())).begin()));
}
BOOST_AUTO_TEST_CASE(polyline6_test_case)
{
using namespace osrm::engine;
using namespace osrm::util;
const std::vector<Coordinate> coords({{FixedLongitude{-73990171}, FixedLatitude{40714701}},
{FixedLongitude{-73991801}, FixedLatitude{40717571}},
{FixedLongitude{-73985751}, FixedLatitude{40715651}}});
BOOST_CHECK_EQUAL(encodePolyline<1000000>(coords.begin(), coords.end()),
"y{_tlAt`_clCkrDzdB~vBcyJ");
BOOST_CHECK(std::equal(
coords.begin(),
coords.end(),
decodePolyline<1000000>(encodePolyline<1000000>(coords.begin(), coords.end())).begin()));
}
BOOST_AUTO_TEST_SUITE_END()
+34
View File
@@ -0,0 +1,34 @@
#include <boost/test/test_case_template.hpp>
#include <boost/test/unit_test.hpp>
#include "fixture.hpp"
#include "osrm/exception.hpp"
BOOST_AUTO_TEST_SUITE(table)
BOOST_AUTO_TEST_CASE(test_incompatible_with_mld)
{
// Can't use the MLD algorithm with CH data
BOOST_CHECK_THROW(
getOSRM(OSRM_TEST_DATA_DIR "/ch/monaco.osrm", osrm::EngineConfig::Algorithm::MLD),
osrm::exception);
}
BOOST_AUTO_TEST_CASE(test_incompatible_with_corech)
{
// Note - CH-only data can't be used with the CoreCH algorithm
BOOST_CHECK_THROW(
getOSRM(OSRM_TEST_DATA_DIR "/ch/monaco.osrm", osrm::EngineConfig::Algorithm::CoreCH),
osrm::exception);
}
BOOST_AUTO_TEST_CASE(test_incompatible_with_ch)
{
// Can't use the CH algorithm with MLD data
BOOST_CHECK_THROW(
getOSRM(OSRM_TEST_DATA_DIR "/mld/monaco.osrm", osrm::EngineConfig::Algorithm::CH),
osrm::exception);
}
BOOST_AUTO_TEST_SUITE_END()
+4 -1
View File
@@ -9,11 +9,14 @@
// I couldn't get Boost.UnitTest to provide a test suite level fixture with custom
// arguments per test suite (osrm base path from argv), so this has to suffice.
inline osrm::OSRM getOSRM(const std::string &base_path)
inline osrm::OSRM
getOSRM(const std::string &base_path,
osrm::EngineConfig::Algorithm algorithm = osrm::EngineConfig::Algorithm::CH)
{
osrm::EngineConfig config;
config.storage_config = {base_path};
config.use_shared_memory = false;
config.algorithm = algorithm;
return osrm::OSRM{config};
}
+1
View File
@@ -7,6 +7,7 @@
#include "osrm/coordinate.hpp"
#include "osrm/engine_config.hpp"
#include "osrm/exception.hpp"
#include "osrm/json_container.hpp"
#include "osrm/json_container.hpp"
#include "osrm/osrm.hpp"
+339 -68
View File
@@ -11,6 +11,7 @@
#include "osrm/osrm.hpp"
#include "osrm/status.hpp"
#include "util/typedefs.hpp"
#include "util/vector_tile.hpp"
#include <protozero/pbf_reader.hpp>
@@ -20,25 +21,38 @@
BOOST_AUTO_TEST_SUITE(tile)
BOOST_AUTO_TEST_CASE(test_tile)
void validate_value(protozero::pbf_reader value)
{
using namespace osrm;
while (value.next())
{
switch (value.tag())
{
case util::vector_tile::VARIANT_TYPE_BOOL:
value.get_bool();
break;
case util::vector_tile::VARIANT_TYPE_DOUBLE:
value.get_double();
break;
case util::vector_tile::VARIANT_TYPE_FLOAT:
value.get_float();
break;
case util::vector_tile::VARIANT_TYPE_STRING:
value.get_string();
break;
case util::vector_tile::VARIANT_TYPE_UINT64:
value.get_uint64();
break;
case util::vector_tile::VARIANT_TYPE_SINT64:
value.get_sint64();
break;
}
}
}
auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/ch/monaco.osrm");
// This tile should contain most of monaco
TileParameters params{17059, 11948, 15};
std::string result;
const auto rc = osrm.Tile(params, result);
BOOST_CHECK(rc == Status::Ok);
BOOST_CHECK(result.size() > 114000);
protozero::pbf_reader tile_message(result);
tile_message.next();
BOOST_CHECK_EQUAL(tile_message.tag(), util::vector_tile::LAYER_TAG); // must be a layer
protozero::pbf_reader layer_message = tile_message.get_message();
void validate_feature_layer(protozero::pbf_reader &layer_message)
{
using namespace osrm;
const auto check_feature = [](protozero::pbf_reader feature_message) {
feature_message.next(); // advance parser to first entry
@@ -55,7 +69,7 @@ BOOST_AUTO_TEST_CASE(test_tile)
auto property_iter_pair = feature_message.get_packed_uint32();
auto value_begin = property_iter_pair.begin();
auto value_end = property_iter_pair.end();
BOOST_CHECK_EQUAL(std::distance(value_begin, value_end), 12);
BOOST_CHECK_EQUAL(std::distance(value_begin, value_end), 14);
auto iter = value_begin;
BOOST_CHECK_EQUAL(*iter++, 0); // speed key
BOOST_CHECK_LT(*iter++, 128); // speed value
@@ -73,6 +87,9 @@ BOOST_AUTO_TEST_CASE(test_tile)
// name
BOOST_CHECK_EQUAL(*iter++, 5);
BOOST_CHECK_GT(*iter++, 130);
// rate
BOOST_CHECK_EQUAL(*iter++, 6);
BOOST_CHECK_GT(*iter++, 130);
BOOST_CHECK(iter == value_end);
// geometry
feature_message.next();
@@ -80,33 +97,6 @@ BOOST_AUTO_TEST_CASE(test_tile)
BOOST_CHECK_GT(std::distance(geometry_iter_pair.begin(), geometry_iter_pair.end()), 1);
};
const auto check_value = [](protozero::pbf_reader value) {
while (value.next())
{
switch (value.tag())
{
case util::vector_tile::VARIANT_TYPE_BOOL:
value.get_bool();
break;
case util::vector_tile::VARIANT_TYPE_DOUBLE:
value.get_double();
break;
case util::vector_tile::VARIANT_TYPE_FLOAT:
value.get_float();
break;
case util::vector_tile::VARIANT_TYPE_STRING:
value.get_string();
break;
case util::vector_tile::VARIANT_TYPE_UINT64:
value.get_uint64();
break;
case util::vector_tile::VARIANT_TYPE_SINT64:
value.get_sint64();
break;
}
}
};
auto number_of_speed_keys = 0u;
auto number_of_speed_values = 0u;
@@ -131,7 +121,7 @@ BOOST_AUTO_TEST_CASE(test_tile)
number_of_speed_keys++;
break;
case util::vector_tile::VARIANT_TAG:
check_value(layer_message.get_message());
validate_value(layer_message.get_message());
number_of_speed_values++;
break;
default:
@@ -140,11 +130,13 @@ BOOST_AUTO_TEST_CASE(test_tile)
}
}
BOOST_CHECK_EQUAL(number_of_speed_keys, 6);
BOOST_CHECK_EQUAL(number_of_speed_keys, 7);
BOOST_CHECK_GT(number_of_speed_values, 128); // speed value resolution
}
tile_message.next();
layer_message = tile_message.get_message();
void validate_turn_layer(protozero::pbf_reader &layer_message)
{
using namespace osrm;
const auto check_turn_feature = [](protozero::pbf_reader feature_message) {
feature_message.next(); // advance parser to first entry
@@ -159,13 +151,15 @@ BOOST_AUTO_TEST_CASE(test_tile)
BOOST_CHECK_EQUAL(feature_message.tag(), util::vector_tile::FEATURE_ATTRIBUTES_TAG);
// properties
auto feature_iter_pair = feature_message.get_packed_uint32();
BOOST_CHECK_EQUAL(std::distance(feature_iter_pair.begin(), feature_iter_pair.end()), 6);
BOOST_CHECK_EQUAL(std::distance(feature_iter_pair.begin(), feature_iter_pair.end()), 8);
auto iter = feature_iter_pair.begin();
BOOST_CHECK_EQUAL(*iter++, 0); // bearing_in key
*iter++;
BOOST_CHECK_EQUAL(*iter++, 1); // turn_angle key
*iter++;
BOOST_CHECK_EQUAL(*iter++, 2); // cost key
BOOST_CHECK_EQUAL(*iter++, 2); // turn cost (duration) key
*iter++; // skip value check, can be valud uint32
BOOST_CHECK_EQUAL(*iter++, 3); // turn weight key
*iter++; // skip value check, can be valud uint32
BOOST_CHECK(iter == feature_iter_pair.end());
// geometry
@@ -199,7 +193,7 @@ BOOST_AUTO_TEST_CASE(test_tile)
number_of_turn_keys++;
break;
case util::vector_tile::VARIANT_TAG:
check_value(layer_message.get_message());
validate_value(layer_message.get_message());
break;
default:
BOOST_CHECK(false); // invalid tag
@@ -207,15 +201,120 @@ BOOST_AUTO_TEST_CASE(test_tile)
}
}
BOOST_CHECK_EQUAL(number_of_turn_keys, 3);
BOOST_CHECK_EQUAL(number_of_turn_keys, 4);
BOOST_CHECK(number_of_turns_found > 700);
}
BOOST_AUTO_TEST_CASE(test_tile_turns)
void validate_node_layer(protozero::pbf_reader &layer_message)
{
using namespace osrm;
auto number_of_nodes_found = 0u;
const auto check_osmnode_feature = [](protozero::pbf_reader feature_message) {
feature_message.next(); // advance parser to first entry
BOOST_CHECK_EQUAL(feature_message.tag(), util::vector_tile::GEOMETRY_TAG);
BOOST_CHECK_EQUAL(feature_message.get_enum(), util::vector_tile::GEOMETRY_TYPE_POINT);
feature_message.next(); // advance to next entry
BOOST_CHECK_EQUAL(feature_message.tag(), util::vector_tile::ID_TAG);
feature_message.get_uint64(); // id
feature_message.next(); // advance to next entry
// Note - on this layer, there should be no feature attributes, the next thing
// we get should be the geometry
BOOST_CHECK_EQUAL(feature_message.tag(), util::vector_tile::FEATURE_GEOMETRIES_TAG);
auto geometry_iter_pair = feature_message.get_packed_uint32();
BOOST_CHECK_GT(std::distance(geometry_iter_pair.begin(), geometry_iter_pair.end()), 1);
};
while (layer_message.next())
{
switch (layer_message.tag())
{
case util::vector_tile::VERSION_TAG:
BOOST_CHECK_EQUAL(layer_message.get_uint32(), 2);
break;
case util::vector_tile::NAME_TAG:
BOOST_CHECK_EQUAL(layer_message.get_string(), "osmnodes");
break;
case util::vector_tile::EXTENT_TAG:
BOOST_CHECK_EQUAL(layer_message.get_uint32(), util::vector_tile::EXTENT);
break;
case util::vector_tile::FEATURE_TAG:
check_osmnode_feature(layer_message.get_message());
number_of_nodes_found++;
break;
case util::vector_tile::KEY_TAG:
BOOST_CHECK(false); // There should be no properties on node features
break;
case util::vector_tile::VARIANT_TAG:
BOOST_CHECK(false); // There should be no properties on node features
break;
default:
BOOST_CHECK(false); // invalid tag
break;
}
}
BOOST_CHECK_EQUAL(number_of_nodes_found, 1791);
}
void validate_tile(const osrm::OSRM &osrm)
{
using namespace osrm;
// This tile should contain most of monaco
TileParameters params{17059, 11948, 15};
std::string result;
const auto rc = osrm.Tile(params, result);
BOOST_CHECK(rc == Status::Ok);
BOOST_CHECK(result.size() > 114000);
protozero::pbf_reader tile_message(result);
tile_message.next();
BOOST_CHECK_EQUAL(tile_message.tag(), util::vector_tile::LAYER_TAG); // must be a layer
protozero::pbf_reader layer_message = tile_message.get_message();
validate_feature_layer(layer_message);
tile_message.next();
BOOST_CHECK_EQUAL(tile_message.tag(), util::vector_tile::LAYER_TAG); // must be a layer
layer_message = tile_message.get_message();
validate_turn_layer(layer_message);
tile_message.next();
BOOST_CHECK_EQUAL(tile_message.tag(), util::vector_tile::LAYER_TAG); // must be a layer
layer_message = tile_message.get_message();
validate_node_layer(layer_message);
}
BOOST_AUTO_TEST_CASE(test_tile_ch)
{
using namespace osrm;
auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/ch/monaco.osrm", osrm::EngineConfig::Algorithm::CH);
validate_tile(osrm);
}
BOOST_AUTO_TEST_CASE(test_tile_corech)
{
using namespace osrm;
auto osrm =
getOSRM(OSRM_TEST_DATA_DIR "/corech/monaco.osrm", osrm::EngineConfig::Algorithm::CoreCH);
validate_tile(osrm);
}
BOOST_AUTO_TEST_CASE(test_tile_mld)
{
using namespace osrm;
auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/mld/monaco.osrm", osrm::EngineConfig::Algorithm::MLD);
validate_tile(osrm);
}
void test_tile_turns(const osrm::OSRM &osrm)
{
using namespace osrm;
auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/ch/monaco.osrm");
// Small tile where we can test all the values
TileParameters params{272953, 191177, 19};
@@ -236,7 +335,8 @@ BOOST_AUTO_TEST_CASE(test_tile_turns)
std::vector<int> found_bearing_in_indexes;
std::vector<int> found_turn_angles_indexes;
std::vector<int> found_penalties_indexes;
std::vector<int> found_time_penalties_indexes;
std::vector<int> found_weight_penalties_indexes;
const auto check_turn_feature = [&](protozero::pbf_reader feature_message) {
feature_message.next(); // advance parser to first entry
@@ -251,14 +351,16 @@ BOOST_AUTO_TEST_CASE(test_tile_turns)
BOOST_CHECK_EQUAL(feature_message.tag(), util::vector_tile::FEATURE_ATTRIBUTES_TAG);
// properties
auto feature_iter_pair = feature_message.get_packed_uint32();
BOOST_CHECK_EQUAL(std::distance(feature_iter_pair.begin(), feature_iter_pair.end()), 6);
BOOST_CHECK_EQUAL(std::distance(feature_iter_pair.begin(), feature_iter_pair.end()), 8);
auto iter = feature_iter_pair.begin();
BOOST_CHECK_EQUAL(*iter++, 0); // bearing_in key
found_bearing_in_indexes.push_back(*iter++);
BOOST_CHECK_EQUAL(*iter++, 1); // turn_angle key
found_turn_angles_indexes.push_back(*iter++);
BOOST_CHECK_EQUAL(*iter++, 2); // cost key
found_penalties_indexes.push_back(*iter++); // skip value check, can be valud uint32
BOOST_CHECK_EQUAL(*iter++, 2); // "cost" key (actually duration)
found_time_penalties_indexes.push_back(*iter++); // skip value check, can be valud uint32
BOOST_CHECK_EQUAL(*iter++, 3); // "weight" key
found_weight_penalties_indexes.push_back(*iter++); // skip value check, can be valud uint32
BOOST_CHECK(iter == feature_iter_pair.end());
// geometry
feature_message.next();
@@ -323,16 +425,28 @@ BOOST_AUTO_TEST_CASE(test_tile_turns)
}
// Verify that we got the expected turn penalties
std::vector<float> actual_turn_penalties;
for (const auto &i : found_penalties_indexes)
std::vector<float> actual_time_turn_penalties;
for (const auto &i : found_time_penalties_indexes)
{
BOOST_CHECK(float_vals.count(i) == 1);
actual_turn_penalties.push_back(float_vals[i]);
actual_time_turn_penalties.push_back(float_vals[i]);
}
std::sort(actual_turn_penalties.begin(), actual_turn_penalties.end());
const std::vector<float> expected_turn_penalties = {
std::sort(actual_time_turn_penalties.begin(), actual_time_turn_penalties.end());
const std::vector<float> expected_time_turn_penalties = {
0, 0, 0, 0, 0, 0, .1f, .1f, .3f, .4f, 1.2f, 1.9f, 5.3f, 5.5f, 5.8f, 7.1f, 7.2f, 7.2f};
CHECK_EQUAL_RANGE(actual_turn_penalties, expected_turn_penalties);
CHECK_EQUAL_RANGE(actual_time_turn_penalties, expected_time_turn_penalties);
// Verify that we got the expected turn penalties
std::vector<float> actual_weight_turn_penalties;
for (const auto &i : found_weight_penalties_indexes)
{
BOOST_CHECK(float_vals.count(i) == 1);
actual_weight_turn_penalties.push_back(float_vals[i]);
}
std::sort(actual_weight_turn_penalties.begin(), actual_weight_turn_penalties.end());
const std::vector<float> expected_weight_turn_penalties = {
0, 0, 0, 0, 0, 0, .1f, .1f, .3f, .4f, 1.2f, 1.9f, 5.3f, 5.5f, 5.8f, 7.1f, 7.2f, 7.2f};
CHECK_EQUAL_RANGE(actual_weight_turn_penalties, expected_weight_turn_penalties);
// Verify the expected turn angles
std::vector<std::int64_t> actual_turn_angles;
@@ -359,11 +473,34 @@ BOOST_AUTO_TEST_CASE(test_tile_turns)
CHECK_EQUAL_RANGE(actual_turn_bearings, expected_turn_bearings);
}
BOOST_AUTO_TEST_CASE(test_tile_speeds)
BOOST_AUTO_TEST_CASE(test_tile_turns_ch)
{
using namespace osrm;
auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/ch/monaco.osrm", osrm::EngineConfig::Algorithm::CH);
auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/ch/monaco.osrm");
test_tile_turns(osrm);
}
BOOST_AUTO_TEST_CASE(test_tile_turns_corech)
{
using namespace osrm;
auto osrm =
getOSRM(OSRM_TEST_DATA_DIR "/corech/monaco.osrm", osrm::EngineConfig::Algorithm::CoreCH);
test_tile_turns(osrm);
}
BOOST_AUTO_TEST_CASE(test_tile_turns_mld)
{
using namespace osrm;
auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/mld/monaco.osrm", osrm::EngineConfig::Algorithm::MLD);
test_tile_turns(osrm);
}
void test_tile_speeds(const osrm::OSRM &osrm)
{
using namespace osrm;
// Small tile so we can test all the values
// TileParameters params{272953, 191177, 19};
@@ -383,8 +520,10 @@ BOOST_AUTO_TEST_CASE(test_tile_speeds)
std::vector<int> found_speed_indexes;
std::vector<int> found_component_indexes;
std::vector<int> found_datasource_indexes;
std::vector<int> found_weight_indexes;
std::vector<int> found_duration_indexes;
std::vector<int> found_name_indexes;
std::vector<int> found_rate_indexes;
const auto check_feature = [&](protozero::pbf_reader feature_message) {
feature_message.next(); // advance parser to first entry
@@ -401,7 +540,7 @@ BOOST_AUTO_TEST_CASE(test_tile_speeds)
auto property_iter_pair = feature_message.get_packed_uint32();
auto value_begin = property_iter_pair.begin();
auto value_end = property_iter_pair.end();
BOOST_CHECK_EQUAL(std::distance(value_begin, value_end), 12);
BOOST_CHECK_EQUAL(std::distance(value_begin, value_end), 14);
auto iter = value_begin;
BOOST_CHECK_EQUAL(*iter++, 0); // speed key
found_speed_indexes.push_back(*iter++);
@@ -411,12 +550,14 @@ BOOST_AUTO_TEST_CASE(test_tile_speeds)
BOOST_CHECK_EQUAL(*iter++, 2); // data source key
found_datasource_indexes.push_back(*iter++);
BOOST_CHECK_EQUAL(*iter++, 3); // weight key
found_duration_indexes.push_back(*iter++);
found_weight_indexes.push_back(*iter++);
BOOST_CHECK_EQUAL(*iter++, 4); // duration key
found_duration_indexes.push_back(*iter++);
// name
BOOST_CHECK_EQUAL(*iter++, 5);
found_name_indexes.push_back(*iter++);
BOOST_CHECK_EQUAL(*iter++, 6);
found_rate_indexes.push_back(*iter++);
BOOST_CHECK(iter == value_end);
// geometry
feature_message.next();
@@ -522,4 +663,134 @@ BOOST_AUTO_TEST_CASE(test_tile_speeds)
BOOST_CHECK(actual_names == expected_names);
}
BOOST_AUTO_TEST_CASE(test_tile_speeds_ch)
{
using namespace osrm;
auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/ch/monaco.osrm", osrm::EngineConfig::Algorithm::CH);
test_tile_speeds(osrm);
}
BOOST_AUTO_TEST_CASE(test_tile_speeds_corech)
{
using namespace osrm;
auto osrm =
getOSRM(OSRM_TEST_DATA_DIR "/corech/monaco.osrm", osrm::EngineConfig::Algorithm::CoreCH);
test_tile_speeds(osrm);
}
BOOST_AUTO_TEST_CASE(test_tile_speeds_mld)
{
using namespace osrm;
auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/mld/monaco.osrm", osrm::EngineConfig::Algorithm::MLD);
test_tile_speeds(osrm);
}
void test_tile_nodes(const osrm::OSRM &osrm)
{
using namespace osrm;
// Small tile so we can test all the values
// TileParameters params{272953, 191177, 19};
// TileParameters params{136477, 95580, 18};
// Small tile where we can test all the values
TileParameters params{272953, 191177, 19};
std::string result;
const auto rc = osrm.Tile(params, result);
BOOST_CHECK(rc == Status::Ok);
BOOST_CHECK_GT(result.size(), 128);
protozero::pbf_reader tile_message(result);
tile_message.next();
BOOST_CHECK_EQUAL(tile_message.tag(), util::vector_tile::LAYER_TAG); // must be a layer
// Skip the segments and turns layers
tile_message.skip();
tile_message.next();
tile_message.skip();
// Get the osmnodes layer
tile_message.next();
protozero::pbf_reader layer_message = tile_message.get_message();
std::vector<OSMNodeID::value_type> found_node_ids;
const auto check_feature = [&](protozero::pbf_reader feature_message) {
feature_message.next(); // advance parser to first entry
BOOST_CHECK_EQUAL(feature_message.tag(), util::vector_tile::GEOMETRY_TAG);
BOOST_CHECK_EQUAL(feature_message.get_enum(), util::vector_tile::GEOMETRY_TYPE_POINT);
feature_message.next(); // advance to next entry
BOOST_CHECK_EQUAL(feature_message.tag(), util::vector_tile::ID_TAG);
found_node_ids.push_back(feature_message.get_uint64()); // id
feature_message.next(); // advance to next entry
BOOST_CHECK_EQUAL(feature_message.tag(), util::vector_tile::FEATURE_GEOMETRIES_TAG);
auto geometry_iter_pair = feature_message.get_packed_uint32();
BOOST_CHECK_GT(std::distance(geometry_iter_pair.begin(), geometry_iter_pair.end()), 1);
};
while (layer_message.next())
{
switch (layer_message.tag())
{
case util::vector_tile::VERSION_TAG:
BOOST_CHECK_EQUAL(layer_message.get_uint32(), 2);
break;
case util::vector_tile::NAME_TAG:
BOOST_CHECK_EQUAL(layer_message.get_string(), "osmnodes");
break;
case util::vector_tile::EXTENT_TAG:
BOOST_CHECK_EQUAL(layer_message.get_uint32(), util::vector_tile::EXTENT);
break;
case util::vector_tile::FEATURE_TAG:
check_feature(layer_message.get_message());
break;
case util::vector_tile::KEY_TAG:
BOOST_CHECK(false); // There should be no keys
break;
case util::vector_tile::VARIANT_TAG:
BOOST_CHECK(false); // There should be no values
break;
default:
BOOST_CHECK(false); // invalid tag
break;
}
}
std::sort(found_node_ids.begin(), found_node_ids.end());
const std::vector<OSMNodeID::value_type> expected_node_ids = {
25191722, 25191725, 357300400, 1737389138, 1737389140, 2241375220};
BOOST_CHECK(found_node_ids == expected_node_ids);
}
BOOST_AUTO_TEST_CASE(test_tile_nodes_ch)
{
using namespace osrm;
auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/ch/monaco.osrm", osrm::EngineConfig::Algorithm::CH);
test_tile_nodes(osrm);
}
BOOST_AUTO_TEST_CASE(test_tile_nodes_corech)
{
using namespace osrm;
auto osrm =
getOSRM(OSRM_TEST_DATA_DIR "/corech/monaco.osrm", osrm::EngineConfig::Algorithm::CoreCH);
test_tile_nodes(osrm);
}
BOOST_AUTO_TEST_CASE(test_tile_nodes_mld)
{
using namespace osrm;
auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/mld/monaco.osrm", osrm::EngineConfig::Algorithm::MLD);
test_tile_nodes(osrm);
}
BOOST_AUTO_TEST_SUITE_END()
+18 -8
View File
@@ -93,9 +93,9 @@ BOOST_AUTO_TEST_CASE(valid_urls)
CHECK_EQUAL_RANGE(reference_5.query, result_5->query);
BOOST_CHECK_EQUAL(reference_5.prefix_length, result_5->prefix_length);
// tile
api::ParsedURL reference_6{"route", 1, "profile", "tile(1,2,3).mvt", 18UL};
auto result_6 = api::parseURL("/route/v1/profile/tile(1,2,3).mvt");
// polyline6
api::ParsedURL reference_6{"route", 1, "profile", "polyline6(_ibE?_seK_seK_seK_seK)?", 18UL};
auto result_6 = api::parseURL("/route/v1/profile/polyline6(_ibE?_seK_seK_seK_seK)?");
BOOST_CHECK(result_6);
BOOST_CHECK_EQUAL(reference_6.service, result_6->service);
BOOST_CHECK_EQUAL(reference_6.version, result_6->version);
@@ -103,17 +103,27 @@ BOOST_AUTO_TEST_CASE(valid_urls)
CHECK_EQUAL_RANGE(reference_6.query, result_6->query);
BOOST_CHECK_EQUAL(reference_6.prefix_length, result_6->prefix_length);
// polyline with %HEX
api::ParsedURL reference_7{
"match", 1, "car", "polyline(}jmwFz~ubMyCa@`@yDJqE)?你好&\n \"%~", 14UL};
auto result_7 = api::parseURL(
"/match/v1/car/polyline(%7DjmwFz~ubMyCa@%60@yDJqE)?%e4%bd%a0%e5%a5%bd&%0A%20%22%25%7e");
// tile
api::ParsedURL reference_7{"route", 1, "profile", "tile(1,2,3).mvt", 18UL};
auto result_7 = api::parseURL("/route/v1/profile/tile(1,2,3).mvt");
BOOST_CHECK(result_7);
BOOST_CHECK_EQUAL(reference_7.service, result_7->service);
BOOST_CHECK_EQUAL(reference_7.version, result_7->version);
BOOST_CHECK_EQUAL(reference_7.profile, result_7->profile);
CHECK_EQUAL_RANGE(reference_7.query, result_7->query);
BOOST_CHECK_EQUAL(reference_7.prefix_length, result_7->prefix_length);
// polyline with %HEX
api::ParsedURL reference_8{
"match", 1, "car", "polyline(}jmwFz~ubMyCa@`@yDJqE)?你好&\n \"%~", 14UL};
auto result_8 = api::parseURL(
"/match/v1/car/polyline(%7DjmwFz~ubMyCa@%60@yDJqE)?%e4%bd%a0%e5%a5%bd&%0A%20%22%25%7e");
BOOST_CHECK(result_8);
BOOST_CHECK_EQUAL(reference_8.service, result_8->service);
BOOST_CHECK_EQUAL(reference_8.version, result_8->version);
BOOST_CHECK_EQUAL(reference_8.profile, result_8->profile);
CHECK_EQUAL_RANGE(reference_8.query, result_8->query);
BOOST_CHECK_EQUAL(reference_8.prefix_length, result_8->prefix_length);
}
BOOST_AUTO_TEST_SUITE_END()
+14 -9
View File
@@ -49,11 +49,12 @@ BOOST_AUTO_TEST_CASE(io_nonexistent_file)
osrm::storage::io::FileReader::VerifyFingerprint);
BOOST_REQUIRE_MESSAGE(false, "Should not get here");
}
catch (const osrm::util::exception &e)
catch (const osrm::util::RuntimeError &e)
{
const std::string expected("Error opening non_existent_test_io.tmp");
const std::string expected("Problem opening file: " + IO_NONEXISTENT_FILE);
const std::string got(e.what());
BOOST_REQUIRE(std::equal(expected.begin(), expected.end(), got.begin()));
BOOST_REQUIRE(e.GetCode() == osrm::ErrorCode::FileOpenError);
}
}
@@ -83,12 +84,12 @@ BOOST_AUTO_TEST_CASE(file_too_small)
osrm::storage::serialization::read(infile, buffer);
BOOST_REQUIRE_MESSAGE(false, "Should not get here");
}
catch (const osrm::util::exception &e)
catch (const osrm::util::RuntimeError &e)
{
const std::string expected(
"Error reading from file_too_small_test_io.tmp: Unexpected end of file");
const std::string expected("Unexpected end of file: " + IO_TOO_SMALL_FILE);
const std::string got(e.what());
BOOST_REQUIRE(std::equal(expected.begin(), expected.end(), got.begin()));
BOOST_REQUIRE(e.GetCode() == osrm::ErrorCode::UnexpectedEndOfFile);
}
}
@@ -111,11 +112,13 @@ BOOST_AUTO_TEST_CASE(io_corrupt_fingerprint)
osrm::storage::io::FileReader::VerifyFingerprint);
BOOST_REQUIRE_MESSAGE(false, "Should not get here");
}
catch (const osrm::util::exception &e)
catch (const osrm::util::RuntimeError &e)
{
const std::string expected("Fingerprint mismatch in corrupt_fingerprint_file_test_io.tmp");
const std::string expected("Fingerprint did not match the expected value: " +
IO_CORRUPT_FINGERPRINT_FILE);
const std::string got(e.what());
BOOST_REQUIRE(std::equal(expected.begin(), expected.end(), got.begin()));
BOOST_REQUIRE(e.GetCode() == osrm::ErrorCode::InvalidFingerprint);
}
}
@@ -146,11 +149,13 @@ BOOST_AUTO_TEST_CASE(io_incompatible_fingerprint)
osrm::storage::io::FileReader::VerifyFingerprint);
BOOST_REQUIRE_MESSAGE(false, "Should not get here");
}
catch (const osrm::util::exception &e)
catch (const osrm::util::RuntimeError &e)
{
const std::string expected("Fingerprint mismatch in " + IO_INCOMPATIBLE_FINGERPRINT_FILE);
const std::string expected("Fingerprint did not match the expected value: " +
IO_INCOMPATIBLE_FINGERPRINT_FILE);
const std::string got(e.what());
BOOST_REQUIRE(std::equal(expected.begin(), expected.end(), got.begin()));
BOOST_REQUIRE(e.GetCode() == osrm::ErrorCode::InvalidFingerprint);
}
}