Compare commits
68 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 838b2a750e | |||
| c2dc7e9cd0 | |||
| 0fc1aa2711 | |||
| 89cf6d9e74 | |||
| 61c430c098 | |||
| 4b75cb8b0e | |||
| 53f87c08b5 | |||
| e23dc8977f | |||
| 421dc5b6ec | |||
| 247f1c120f | |||
| e011c60e12 | |||
| 26c909b64b | |||
| acb7916996 | |||
| 40b5045a0a | |||
| b17f40862c | |||
| 7702ebde61 | |||
| 6951eadc18 | |||
| cf2573157f | |||
| 80c55119d2 | |||
| 7323221e3b | |||
| fb5bd818d9 | |||
| 43a4e8db12 | |||
| 302390696e | |||
| 2508629d6c | |||
| 90c390d7a6 | |||
| 28178b12c7 | |||
| 95442d45aa | |||
| 6eb4f090f9 | |||
| b4ad6588ed | |||
| 56282b0e3f | |||
| 2385602500 | |||
| 628a154d7f | |||
| efaed59b9b | |||
| bdee13dea8 | |||
| c615910874 | |||
| 9e1398c68a | |||
| af10692d2d | |||
| e7abe37b10 | |||
| bc8617a9f4 | |||
| 00e243b23b | |||
| 27324d0270 | |||
| 0c838fb60c | |||
| 0713ef5862 | |||
| bee1ba8854 | |||
| da252c7597 | |||
| a4460abc83 | |||
| 261636febb | |||
| 21f15f0a29 | |||
| bd068ff2a6 | |||
| d09f5c0e3a | |||
| 45140ca9f7 | |||
| c6be2e768a | |||
| 6339395cba | |||
| bd6492bb38 | |||
| f93b331817 | |||
| 20e4096c4b | |||
| 97952a9289 | |||
| 960a595268 | |||
| a3c94ef632 | |||
| 84fd38ac9c | |||
| 303a8fae32 | |||
| 21686ee8a9 | |||
| c8b142a676 | |||
| 58061a68c4 | |||
| 9c11197768 | |||
| f347efb006 | |||
| af8ddac2af | |||
| b1358de9bb |
@@ -13,3 +13,4 @@
|
||||
!profiles/*
|
||||
!profiles/lib/*
|
||||
!profiles/examples/*
|
||||
!scripts/node_install.sh
|
||||
|
||||
+11
-5
@@ -7,10 +7,6 @@ git:
|
||||
sudo: required
|
||||
dist: trusty
|
||||
|
||||
node_js:
|
||||
- "4"
|
||||
- "6"
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
|
||||
@@ -19,6 +15,7 @@ branches:
|
||||
- master
|
||||
# enable building tags
|
||||
- /^v\d+\.\d+(\.\d+)?(-\S*)?$/
|
||||
- 5.12
|
||||
|
||||
cache:
|
||||
yarn: true
|
||||
@@ -144,7 +141,15 @@ matrix:
|
||||
osx_image: xcode8.2
|
||||
compiler: "mason-osx-release"
|
||||
# we use the xcode provides clang and don't install our own
|
||||
env: ENABLE_MASON=ON BUILD_TYPE='Release' CUCUMBER_TIMEOUT=60000 CCOMPILER='clang' CXXCOMPILER='clang++' ENABLE_ASSERTIONS=ON ENABLE_LTO=ON
|
||||
env: ENABLE_MASON=ON BUILD_TYPE='Release' CUCUMBER_TIMEOUT=60000 CCOMPILER='clang' CXXCOMPILER='clang++' ENABLE_ASSERTIONS=ON ENABLE_LTO=ON NODE="4"
|
||||
after_success:
|
||||
- ./scripts/travis/publish.sh
|
||||
|
||||
- os: osx
|
||||
osx_image: xcode8.2
|
||||
compiler: "mason-osx-release"
|
||||
# we use the xcode provides clang and don't install our own
|
||||
env: ENABLE_MASON=ON BUILD_TYPE='Release' CUCUMBER_TIMEOUT=60000 CCOMPILER='clang' CXXCOMPILER='clang++' ENABLE_ASSERTIONS=ON ENABLE_LTO=ON NODE="6"
|
||||
after_success:
|
||||
- ./scripts/travis/publish.sh
|
||||
|
||||
@@ -361,6 +366,7 @@ script:
|
||||
- pushd ${OSRM_BUILD_DIR}
|
||||
- ./unit_tests/library-tests
|
||||
- ./unit_tests/extractor-tests
|
||||
- ./unit_tests/contractor-tests
|
||||
- ./unit_tests/engine-tests
|
||||
- ./unit_tests/util-tests
|
||||
- ./unit_tests/server-tests
|
||||
|
||||
+22
-2
@@ -1,5 +1,25 @@
|
||||
# UNRELEASED
|
||||
- Adds new instruction types at the exit of roundabouts and rotaries `exit roundabout` and `exit rotary`.
|
||||
# 5.12.0
|
||||
- Changes from 5.11.0
|
||||
- Guidance
|
||||
- now announcing turning onto oneways at the end of a road (e.g. onto dual carriageways)
|
||||
- Adds new instruction types at the exit of roundabouts and rotaries `exit roundabout` and `exit rotary`.
|
||||
- HTTP:
|
||||
- New query parameter for route/table/match/trip plugings:
|
||||
`exclude=` that can be used to exclude certain classes (e.g. exclude=motorway, exclude=toll).
|
||||
This is configurable in the profile.
|
||||
- NodeJS:
|
||||
- New query option `exclude` for the route/table/match/trip plugins. (e.g. `exclude: ["motorway", "toll"]`)
|
||||
- Profile:
|
||||
- New property for profile table: `excludable` that can be used to configure which classes are excludable at query time.
|
||||
- New optional property for profile table: `classes` that allows you to specify which classes you expect to be used.
|
||||
We recommend this for better error messages around classes, otherwise the possible class names are infered automatically.
|
||||
- Traffic:
|
||||
- If traffic data files contain an empty 4th column, they will update edge durations but not modify the edge weight. This is useful for
|
||||
updating ETAs returned, without changing route selection (for example, in a distance-based profile with traffic data loaded).
|
||||
- Infrastructure:
|
||||
- New file `.osrm.cell_metrics` created by `osrm-customize`.
|
||||
- Debug tiles:
|
||||
- Added new properties `type` and `modifier` to `turns` layer, useful for viewing guidance calculated turn types on the map
|
||||
|
||||
# 5.11.0
|
||||
- Changes from 5.10:
|
||||
|
||||
+4
-4
@@ -468,7 +468,7 @@ if(ENABLE_MASON)
|
||||
install(FILES ${TBBGlob} DESTINATION lib)
|
||||
endforeach()
|
||||
if(APPLE)
|
||||
set(LINKER_FLAGS "${TBB_LINKER_RPATHS} -Wl,-rpath -Wl,@executable_path")
|
||||
set(LINKER_FLAGS "${TBB_LINKER_RPATHS} -Wl,-rpath -Wl,@loader_path")
|
||||
elseif(UNIX)
|
||||
set(LINKER_FLAGS "${TBB_LINKER_RPATHS} '-Wl,-rpath,$ORIGIN' -Wl,-z,origin")
|
||||
endif()
|
||||
@@ -692,10 +692,10 @@ set(UTIL_LIBRARIES
|
||||
# Libraries
|
||||
target_link_libraries(osrm ${ENGINE_LIBRARIES})
|
||||
target_link_libraries(osrm_update ${UPDATER_LIBRARIES})
|
||||
target_link_libraries(osrm_contract ${CONTRACTOR_LIBRARIES} osrm_update)
|
||||
target_link_libraries(osrm_contract ${CONTRACTOR_LIBRARIES} osrm_update osrm_store)
|
||||
target_link_libraries(osrm_extract ${EXTRACTOR_LIBRARIES})
|
||||
target_link_libraries(osrm_partition ${PARTITIONER_LIBRARIES})
|
||||
target_link_libraries(osrm_customize ${CUSTOMIZER_LIBRARIES} osrm_update)
|
||||
target_link_libraries(osrm_customize ${CUSTOMIZER_LIBRARIES} osrm_update osrm_store)
|
||||
target_link_libraries(osrm_store ${STORAGE_LIBRARIES})
|
||||
|
||||
# BUILD_COMPONENTS
|
||||
@@ -738,7 +738,7 @@ file(GLOB VariantGlob third_party/variant/include/mapbox/*.hpp)
|
||||
file(GLOB LibraryGlob include/osrm/*.hpp)
|
||||
file(GLOB ParametersGlob include/engine/api/*_parameters.hpp)
|
||||
set(EngineHeader include/engine/status.hpp include/engine/engine_config.hpp include/engine/hint.hpp include/engine/bearing.hpp include/engine/approach.hpp include/engine/phantom_node.hpp)
|
||||
set(UtilHeader include/util/coordinate.hpp include/util/json_container.hpp include/util/typedefs.hpp include/util/alias.hpp include/util/exception.hpp)
|
||||
set(UtilHeader include/util/coordinate.hpp include/util/json_container.hpp include/util/typedefs.hpp include/util/alias.hpp include/util/exception.hpp include/util/bearing.hpp)
|
||||
set(ExtractorHeader include/extractor/extractor.hpp include/storage/io_config.hpp include/extractor/extractor_config.hpp include/extractor/travel_mode.hpp)
|
||||
set(PartitionerHeader include/partition/partitioner.hpp include/partition/partition_config.hpp)
|
||||
set(ContractorHeader include/contractor/contractor.hpp include/contractor/contractor_config.hpp)
|
||||
|
||||
+4
-4
@@ -1,7 +1,7 @@
|
||||
module.exports = {
|
||||
default: '--strict --tags ~@stress --tags ~@todo --require features/support --require features/step_definitions',
|
||||
verify: '--strict --tags ~@stress --tags ~@todo -f progress --require features/support --require features/step_definitions',
|
||||
default: '--strict --tags ~@stress --tags ~@mld --tags ~@todo --require features/support --require features/step_definitions',
|
||||
verify: '--strict --tags ~@stress --tags ~@mld --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 --require features/support --require features/step_definitions -f progress'
|
||||
}
|
||||
mld: '--strict --tags ~@stress --tags ~@todo --tags ~@alternative --tags ~@ch --require features/support --require features/step_definitions -f progress'
|
||||
};
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ A guard (ScopedGeojsonLoggerGuard) requires a logging policy. Per default we pro
|
||||
|
||||
The initialisation to do so looks like this:
|
||||
`util::ScopedGeojsonLoggerGuard<util::NodeIdVectorToLineString> geojson_guard( "debug.geojson", data-for-conversion);`
|
||||
Make sure to give the guar a name, so it actually gets a lifetime.
|
||||
Make sure to give the guard a name, so it actually gets a lifetime.
|
||||
|
||||
The field `data-for-conversion` can be an arbitrary long set of features and needs to match the parameters used for constructing our policy (in this case `util::NodeIdVectorToLineString`).
|
||||
|
||||
|
||||
+10
-1
@@ -31,6 +31,7 @@ To pass parameters to each location some options support an array like encoding:
|
||||
|generate\_hints |`true` (default), `false` |Adds a Hint to the response which can be used in subsequent requests, see `hints` parameter. |
|
||||
|hints |`{hint};{hint}[;{hint} ...]` |Hint from previous request to derive position in street network. |
|
||||
|approaches |`{approach};{approach}[;{approach} ...]` |Keep waypoints on curb side. |
|
||||
|exclude |`{class}[,{class}]` |Additive list of classes to avoid, order does not matter. |
|
||||
|
||||
Where the elements follow the following format:
|
||||
|
||||
@@ -40,12 +41,13 @@ Where the elements follow the following format:
|
||||
|radius |`double >= 0` or `unlimited` (default) |
|
||||
|hint |Base64 `string` |
|
||||
|approach |`curb` or `unrestricted` (default) |
|
||||
|class |A class name determined by the profile or `none`. |
|
||||
|
||||
```
|
||||
{option}={element};{element}[;{element} ... ]
|
||||
```
|
||||
|
||||
The number of elements must match exactly the number of locations. If you don't want to pass a value but instead use the default you can pass an empty `element`.
|
||||
The number of elements must match exactly the number of locations (except for `generate_hints` and `exclude`). If you don't want to pass a value but instead use the default you can pass an empty `element`.
|
||||
|
||||
Example: 2nd location use the default value for `option`:
|
||||
|
||||
@@ -59,6 +61,9 @@ Example: 2nd location use the default value for `option`:
|
||||
# Query on Berlin with three coordinates:
|
||||
curl 'http://router.project-osrm.org/route/v1/driving/13.388860,52.517037;13.397634,52.529407;13.428555,52.523219?overview=false'
|
||||
|
||||
# Query on Berlin excluding the usage of motorways:
|
||||
curl 'http://router.project-osrm.org/route/v1/driving/13.388860,52.517037;13.397634,52.529407?exclude=motorway'
|
||||
|
||||
# Using polyline:
|
||||
curl 'http://router.project-osrm.org/route/v1/driving/polyline(ofp_Ik_vpAilAyu@te@g`E)?overview=false'
|
||||
```
|
||||
@@ -432,6 +437,8 @@ Vector tiles contain two layers:
|
||||
| `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 |
|
||||
| `type` | `string` | the type of this turn - values like `turn`, `continue`, etc. See the `StepManeuver` for a partial list, this field also exposes internal turn types that are never returned with an API response |
|
||||
| `modifier` | `string` | the direction modifier of the turn (`left`, `sharp left`, etc) |
|
||||
|
||||
|
||||
## Result objects
|
||||
@@ -661,6 +668,8 @@ step.
|
||||
| `rotary` | a traffic circle. While very similar to a larger version of a roundabout, it does not necessarily follow roundabout rules for right of way. It can offer `rotary_name` and/or `rotary_pronunciation` parameters (located in the RouteStep object) in addition to the `exit` parameter (located on the StepManeuver object). |
|
||||
| `roundabout turn`| Describes a turn at a small roundabout that should be treated as normal turn. The `modifier` indicates the turn direciton. Example instruction: `At the roundabout turn left`. |
|
||||
| `notification` | not an actual turn but a change in the driving conditions. For example the travel mode or classes. If the road takes a turn itself, the `modifier` describes the direction |
|
||||
| `exit roundabout`| Describes a maneuver exiting a roundabout (usually preceeded by a `roundabout` instruction) |
|
||||
| `exit rotary` | Describes the maneuver exiting a rotary (large named roundabout) |
|
||||
|
||||
Please note that even though there are `new name` and `notification` instructions, the `mode` and `name` can change
|
||||
between all instructions. They only offer a fallback in case nothing else is to report.
|
||||
|
||||
+11
-1
@@ -104,6 +104,16 @@ max_speed_for_map_matching | Float | Maximum vehicle speed to be as
|
||||
max_turn_weight | Float | Maximum turn penalty weight
|
||||
force_split_edges | Boolean | True value forces a split of forward and backward edges of extracted ways and guarantees that `process_segment` will be called for all segments (default `false`)
|
||||
|
||||
The following additional global properties can be set in the hash you return in the `setup` function:
|
||||
|
||||
Attribute | Type | Notes
|
||||
-------------------------------------|------------------|----------------------------------------------------------------------------
|
||||
excludable | Sequence of Sets | Determines which class-combinations are supported by the `exclude` option at query time.
|
||||
| | E.g. `Sequence{Set{"ferry", "motorway"}, Set{"motorway"}}` will allow you to exclude ferries and motorways, or only motorways.
|
||||
classes | Sequence | Determines the allowed classes that can be referenced using `{forward,backward}_classes` on the way in the `process_way` function.
|
||||
restrictions | Sequence | Determines which turn restrictions will be used for this profile.
|
||||
suffix_list | Set | List of name suffixes needed for determining if "Highway 101 NW" the same road as "Highway 101 ES".
|
||||
|
||||
### process_node(profile, node, result)
|
||||
Process an OSM node to determine whether this node is a barrier or can be passed and whether passing it incurs a delay.
|
||||
|
||||
@@ -272,4 +282,4 @@ durationIsValid
|
||||
parseDuration
|
||||
trimLaneString
|
||||
applyAccessTokens
|
||||
canonicalizeStringList
|
||||
canonicalizeStringList
|
||||
|
||||
@@ -24,6 +24,25 @@ Feature: End Of Road Instructions
|
||||
| a,c | aeb,cbd,cbd | depart,end of road left,arrive |
|
||||
| a,d | aeb,cbd,cbd | depart,end of road right,arrive |
|
||||
|
||||
# http://map.project-osrm.org/?z=18¢er=38.906632%2C-77.008265&loc=38.906463%2C-77.007621&loc=38.906822%2C-77.008860&hl=en&alt=0
|
||||
Scenario: End of Road, unnamed oneway
|
||||
Given the node map
|
||||
"""
|
||||
c
|
||||
a e b
|
||||
f d
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | highway | name | oneway |
|
||||
| aeb | primary | road | yes |
|
||||
| cbd | primary | | yes |
|
||||
| ef | primary | turn | yes |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| a,d | road,, | depart,end of road right,arrive |
|
||||
|
||||
@3605
|
||||
Scenario: End of Road with oneway through street
|
||||
Given the node map
|
||||
|
||||
@@ -158,23 +158,23 @@ Feature: Rotary
|
||||
| bkheb | roundabout | yes |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| a,c | abc,abc,abc | depart,exit rotary right,arrive |
|
||||
| a,l | abc,jkl,jkl,jkl | depart,bkheb-exit-2,exit rotary straight,arrive |
|
||||
| a,i | abc,ghi,ghi,ghi | depart,bkheb-exit-3,exit rotary straight,arrive |
|
||||
| a,f | abc,def,def,def | depart,bkheb-exit-4,exit rotary straight,arrive |
|
||||
| d,f | def,def,def | depart,exit rotary right,arrive |
|
||||
| d,c | def,abc,abc,abc | depart,bkheb-exit-2,exit rotary straight,arrive |
|
||||
| d,l | def,jkl,jkl,jkl | depart,bkheb-exit-3,exit rotary straight,arrive |
|
||||
| d,i | def,ghi,ghi,ghi | depart,bkheb-exit-4,exit rotary straight,arrive |
|
||||
| g,i | ghi,ghi,ghi | depart,exit rotary right,arrive |
|
||||
| g,f | ghi,def,def,def | depart,bkheb-exit-2,exit rotary straight,arrive |
|
||||
| g,c | ghi,abc,abc,abc | depart,bkheb-exit-3,exit rotary straight,arrive |
|
||||
| g,l | ghi,jkl,jkl,jkl | depart,bkheb-exit-4,exit rotary straight,arrive |
|
||||
| j,l | jkl,jkl,jkl | depart,exit rotary right,arrive |
|
||||
| j,i | jkl,ghi,ghi,ghi | depart,bkheb-exit-2,exit rotary straight,arrive |
|
||||
| j,f | jkl,def,def,def | depart,bkheb-exit-3,exit rotary straight,arrive |
|
||||
| j,c | jkl,abc,abc,abc | depart,bkheb-exit-4,exit rotary straight,arrive |
|
||||
| waypoints | route | turns |
|
||||
| a,c | abc,abc,abc | depart,exit rotary right,arrive |
|
||||
| a,l | abc,jkl,jkl,jkl | depart,bkheb-exit-2,exit rotary straight,arrive |
|
||||
| a,i | abc,ghi,ghi,ghi | depart,bkheb-exit-3,exit rotary straight,arrive |
|
||||
| a,f | abc,def,def,def | depart,bkheb-exit-4,exit rotary straight,arrive |
|
||||
| d,f | def,def,def | depart,exit rotary right,arrive |
|
||||
| d,c | def,abc,abc,abc | depart,bkheb-exit-2,exit rotary straight,arrive |
|
||||
| d,l | def,jkl,jkl,jkl | depart,bkheb-exit-3,exit rotary straight,arrive |
|
||||
| d,i | def,ghi,ghi,ghi | depart,bkheb-exit-4,exit rotary straight,arrive |
|
||||
| g,i | ghi,ghi,ghi | depart,exit rotary right,arrive |
|
||||
| g,f | ghi,def,def,def | depart,bkheb-exit-2,exit rotary straight,arrive |
|
||||
| g,c | ghi,abc,abc,abc | depart,bkheb-exit-3,exit rotary straight,arrive |
|
||||
| g,l | ghi,jkl,jkl,jkl | depart,bkheb-exit-4,exit rotary straight,arrive |
|
||||
| j,l | jkl,jkl,jkl | depart,exit rotary right,arrive |
|
||||
| j,i | jkl,ghi,ghi,ghi | depart,bkheb-exit-2,exit rotary straight,arrive |
|
||||
| j,f | jkl,def,def,def | depart,bkheb-exit-3,exit rotary straight,arrive |
|
||||
| j,c | jkl,abc,abc,abc | depart,bkheb-exit-4,exit rotary straight,arrive |
|
||||
|
||||
Scenario: Collinear in X,Y
|
||||
Given the node map
|
||||
@@ -193,7 +193,7 @@ Feature: Rotary
|
||||
| df | |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| waypoints | route | turns |
|
||||
| a,e | ab,ce,ce,ce | depart,bcdb-exit-1,exit rotary straight,arrive |
|
||||
| a,f | ab,df,df,df | depart,bcdb-exit-2,exit rotary straight,arrive |
|
||||
|
||||
@@ -214,7 +214,7 @@ Feature: Rotary
|
||||
| cf | |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| waypoints | route | turns |
|
||||
| a,e | ad,be,be,be | depart,bcdb-exit-1,exit rotary straight,arrive |
|
||||
| a,f | ad,cf,cf,cf | depart,bcdb-exit-2,exit rotary straight,arrive |
|
||||
|
||||
@@ -235,7 +235,7 @@ Feature: Rotary
|
||||
| bf | |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| waypoints | route | turns |
|
||||
| a,e | ac,de,de,de | depart,bcdb-exit-1,exit rotary straight,arrive |
|
||||
| a,f | ac,bf,bf,bf | depart,bcdb-exit-2,exit rotary straight,arrive |
|
||||
|
||||
@@ -256,7 +256,7 @@ Feature: Rotary
|
||||
| df | |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| waypoints | route | turns |
|
||||
| a,e | ab,ce,ce,ce | depart,bcdb-exit-1,exit rotary right,arrive |
|
||||
| a,f | ab,df,df,df | depart,bcdb-exit-2,exit rotary right,arrive |
|
||||
|
||||
@@ -277,6 +277,6 @@ Feature: Rotary
|
||||
| df | |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| waypoints | route | turns |
|
||||
| a,e | ab,ce,ce,ce | depart,bcdb-exit-1,exit rotary right,arrive |
|
||||
| a,f | ab,df,df,df | depart,bcdb-exit-2,exit rotary right,arrive |
|
||||
|
||||
@@ -26,15 +26,15 @@ Feature: Basic Roundabout
|
||||
|
||||
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 straight exit-1,exit roundabout right,arrive |
|
||||
| a,h | ab,gh,gh,gh | depart,roundabout turn right exit-1,exit roundabout right,arrive |
|
||||
| d,f | cd,ef,ef,ef | depart,roundabout turn left exit-2,exit roundabout right,arrive |
|
||||
| d,h | cd,gh,gh,gh | depart,roundabout turn straight exit-2,exit roundabout right,arrive |
|
||||
| d,a | cd,ab,ab | depart,roundabout turn right exit-1,arrive |
|
||||
| f,h | ef,gh,gh,gh | depart,roundabout turn left exit-3,exit roundabout 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,ef,ef,ef | depart,roundabout turn right exit-1,exit roundabout right,arrive |
|
||||
| a,d | ab,cd,cd | depart,roundabout turn left exit-1,arrive |
|
||||
| a,f | ab,ef,ef,ef | depart,roundabout turn straight exit-1,exit roundabout right,arrive |
|
||||
| a,h | ab,gh,gh,gh | depart,roundabout turn right exit-1,exit roundabout right,arrive |
|
||||
| d,f | cd,ef,ef,ef | depart,roundabout turn left exit-2,exit roundabout right,arrive |
|
||||
| d,h | cd,gh,gh,gh | depart,roundabout turn straight exit-2,exit roundabout right,arrive |
|
||||
| d,a | cd,ab,ab | depart,roundabout turn right exit-1,arrive |
|
||||
| f,h | ef,gh,gh,gh | depart,roundabout turn left exit-3,exit roundabout 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,ef,ef,ef | depart,roundabout turn right exit-1,exit roundabout right,arrive |
|
||||
|
||||
@@ -25,18 +25,18 @@ Feature: Basic Roundabout
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| a,d | ab,cd,cd | depart,roundabout turn left exit-3,arrive |
|
||||
| a,f | ab,ef,ef | depart,roundabout turn straight exit-2,arrive |
|
||||
| a,h | ab,gh,gh | depart,roundabout turn right exit-1,arrive |
|
||||
| d,f | cd,ef,ef | depart,roundabout turn left exit-3,arrive |
|
||||
| d,h | cd,gh,gh | depart,roundabout turn straight exit-2,arrive |
|
||||
| d,a | cd,ab,ab | depart,roundabout turn right exit-1,arrive |
|
||||
| f,h | ef,gh,gh | depart,roundabout turn left exit-3,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-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 |
|
||||
| a,d | ab,cd,cd | depart,roundabout turn left exit-3,arrive |
|
||||
| a,f | ab,ef,ef | depart,roundabout turn straight exit-2,arrive |
|
||||
| a,h | ab,gh,gh | depart,roundabout turn right exit-1,arrive |
|
||||
| d,f | cd,ef,ef | depart,roundabout turn left exit-3,arrive |
|
||||
| d,h | cd,gh,gh | depart,roundabout turn straight exit-2,arrive |
|
||||
| d,a | cd,ab,ab | depart,roundabout turn right exit-1,arrive |
|
||||
| f,h | ef,gh,gh | depart,roundabout turn left exit-3,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-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 |
|
||||
|
||||
Scenario: Enter and Exit - Rotated
|
||||
Given the node map
|
||||
@@ -56,21 +56,21 @@ Feature: Basic Roundabout
|
||||
| bgecb | roundabout |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| a,d | ab,cd,cd | depart,roundabout turn left exit-3,arrive |
|
||||
| a,f | ab,ef,ef | depart,roundabout turn straight exit-2,arrive |
|
||||
| a,h | ab,gh,gh | depart,roundabout turn right exit-1,arrive |
|
||||
| d,f | cd,ef,ef | depart,roundabout turn left exit-3,arrive |
|
||||
| d,h | cd,gh,gh | depart,roundabout turn straight exit-2,arrive |
|
||||
| d,a | cd,ab,ab | depart,roundabout turn right exit-1,arrive |
|
||||
| f,h | ef,gh,gh | depart,roundabout turn left exit-3,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-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 |
|
||||
| waypoints | route | turns |
|
||||
| a,d | ab,cd,cd | depart,roundabout turn left exit-3,arrive |
|
||||
| a,f | ab,ef,ef | depart,roundabout turn straight exit-2,arrive |
|
||||
| a,h | ab,gh,gh | depart,roundabout turn right exit-1,arrive |
|
||||
| d,f | cd,ef,ef | depart,roundabout turn left exit-3,arrive |
|
||||
| d,h | cd,gh,gh | depart,roundabout turn straight exit-2,arrive |
|
||||
| d,a | cd,ab,ab | depart,roundabout turn right exit-1,arrive |
|
||||
| f,h | ef,gh,gh | depart,roundabout turn left exit-3,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-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 |
|
||||
|
||||
Scenario: Only Enter
|
||||
Scenario: Only Enter
|
||||
Given the node map
|
||||
"""
|
||||
a
|
||||
@@ -122,19 +122,19 @@ Feature: Basic Roundabout
|
||||
| bcegb | roundabout |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| b,d | bcegb,cd,cd | depart,exit roundabout right,arrive |
|
||||
| b,f | bcegb,ef,ef | depart,exit roundabout right,arrive |
|
||||
| b,h | bcegb,gh,gh | depart,exit roundabout right,arrive |
|
||||
| c,f | bcegb,ef,ef | depart,exit roundabout right,arrive |
|
||||
| c,h | bcegb,gh,gh | depart,exit roundabout right,arrive |
|
||||
| c,a | bcegb,ab,ab | depart,exit roundabout right,arrive |
|
||||
| e,h | bcegb,gh,gh | depart,exit roundabout right,arrive |
|
||||
| e,a | bcegb,ab,ab | depart,exit roundabout right,arrive |
|
||||
| e,d | bcegb,cd,cd | depart,exit roundabout right,arrive |
|
||||
| g,a | bcegb,ab,ab | depart,exit roundabout right,arrive |
|
||||
| g,d | bcegb,cd,cd | depart,exit roundabout right,arrive |
|
||||
| g,f | bcegb,ef,ef | depart,exit roundabout right,arrive |
|
||||
| waypoints | route | turns |
|
||||
| b,d | bcegb,cd,cd | depart,exit roundabout right,arrive |
|
||||
| b,f | bcegb,ef,ef | depart,exit roundabout right,arrive |
|
||||
| b,h | bcegb,gh,gh | depart,exit roundabout right,arrive |
|
||||
| c,f | bcegb,ef,ef | depart,exit roundabout right,arrive |
|
||||
| c,h | bcegb,gh,gh | depart,exit roundabout right,arrive |
|
||||
| c,a | bcegb,ab,ab | depart,exit roundabout right,arrive |
|
||||
| e,h | bcegb,gh,gh | depart,exit roundabout right,arrive |
|
||||
| e,a | bcegb,ab,ab | depart,exit roundabout right,arrive |
|
||||
| e,d | bcegb,cd,cd | depart,exit roundabout right,arrive |
|
||||
| g,a | bcegb,ab,ab | depart,exit roundabout right,arrive |
|
||||
| g,d | bcegb,cd,cd | depart,exit roundabout right,arrive |
|
||||
| g,f | bcegb,ef,ef | depart,exit roundabout right,arrive |
|
||||
#phantom node snapping can result in a full round-trip here, therefore we cannot test b->a and the other direct exits
|
||||
|
||||
Scenario: Drive Around
|
||||
@@ -189,23 +189,23 @@ Feature: Basic Roundabout
|
||||
| bkheb | roundabout | yes |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| a,c | abc,abc,abc | depart,exit roundabout right,arrive |
|
||||
| a,l | abc,jkl,jkl,jkl | depart,roundabout-exit-2,exit roundabout straight,arrive |
|
||||
| a,i | abc,ghi,ghi,ghi | depart,roundabout-exit-3,exit roundabout straight,arrive |
|
||||
| a,f | abc,def,def,def | depart,roundabout-exit-4,exit roundabout straight,arrive |
|
||||
| d,f | def,def,def | depart,exit roundabout right,arrive |
|
||||
| d,c | def,abc,abc,abc | depart,roundabout-exit-2,exit roundabout straight,arrive |
|
||||
| d,l | def,jkl,jkl,jkl | depart,roundabout-exit-3,exit roundabout straight,arrive |
|
||||
| d,i | def,ghi,ghi,ghi | depart,roundabout-exit-4,exit roundabout straight,arrive |
|
||||
| g,i | ghi,ghi,ghi | depart,exit roundabout right,arrive |
|
||||
| g,f | ghi,def,def,def | depart,roundabout-exit-2,exit roundabout straight,arrive |
|
||||
| g,c | ghi,abc,abc,abc | depart,roundabout-exit-3,exit roundabout straight,arrive |
|
||||
| g,l | ghi,jkl,jkl,jkl | depart,roundabout-exit-4,exit roundabout straight,arrive |
|
||||
| j,l | jkl,jkl,jkl | depart,exit roundabout right,arrive |
|
||||
| j,i | jkl,ghi,ghi,ghi | depart,roundabout-exit-2,exit roundabout straight,arrive |
|
||||
| j,f | jkl,def,def,def | depart,roundabout-exit-3,exit roundabout straight,arrive |
|
||||
| j,c | jkl,abc,abc,abc | depart,roundabout-exit-4,exit roundabout straight,arrive |
|
||||
| waypoints | route | turns |
|
||||
| a,c | abc,abc,abc | depart,exit roundabout right,arrive |
|
||||
| a,l | abc,jkl,jkl,jkl | depart,roundabout-exit-2,exit roundabout straight,arrive |
|
||||
| a,i | abc,ghi,ghi,ghi | depart,roundabout-exit-3,exit roundabout straight,arrive |
|
||||
| a,f | abc,def,def,def | depart,roundabout-exit-4,exit roundabout straight,arrive |
|
||||
| d,f | def,def,def | depart,exit roundabout right,arrive |
|
||||
| d,c | def,abc,abc,abc | depart,roundabout-exit-2,exit roundabout straight,arrive |
|
||||
| d,l | def,jkl,jkl,jkl | depart,roundabout-exit-3,exit roundabout straight,arrive |
|
||||
| d,i | def,ghi,ghi,ghi | depart,roundabout-exit-4,exit roundabout straight,arrive |
|
||||
| g,i | ghi,ghi,ghi | depart,exit roundabout right,arrive |
|
||||
| g,f | ghi,def,def,def | depart,roundabout-exit-2,exit roundabout straight,arrive |
|
||||
| g,c | ghi,abc,abc,abc | depart,roundabout-exit-3,exit roundabout straight,arrive |
|
||||
| g,l | ghi,jkl,jkl,jkl | depart,roundabout-exit-4,exit roundabout straight,arrive |
|
||||
| j,l | jkl,jkl,jkl | depart,exit roundabout right,arrive |
|
||||
| j,i | jkl,ghi,ghi,ghi | depart,roundabout-exit-2,exit roundabout straight,arrive |
|
||||
| j,f | jkl,def,def,def | depart,roundabout-exit-3,exit roundabout straight,arrive |
|
||||
| j,c | jkl,abc,abc,abc | depart,roundabout-exit-4,exit roundabout straight,arrive |
|
||||
|
||||
Scenario: Segregated roads - Not an intersection
|
||||
Given the node map
|
||||
@@ -226,23 +226,23 @@ Feature: Basic Roundabout
|
||||
| bkheb | roundabout | yes |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| a,c | abc,abc,abc,abc | depart,roundabout-exit-4,exit roundabout right,arrive |
|
||||
| a,l | abc,jkl,jkl,jkl | depart,roundabout-exit-1,exit roundabout right,arrive |
|
||||
| a,i | abc,ghi,ghi,ghi | depart,roundabout-exit-2,exit roundabout right,arrive |
|
||||
| a,f | abc,def,def,def | depart,roundabout-exit-3,exit roundabout right,arrive |
|
||||
| d,f | def,def,def,def | depart,roundabout-exit-4,exit roundabout right,arrive |
|
||||
| d,c | def,abc,abc,abc | depart,roundabout-exit-1,exit roundabout right,arrive |
|
||||
| d,l | def,jkl,jkl,jkl | depart,roundabout-exit-2,exit roundabout right,arrive |
|
||||
| d,i | def,ghi,ghi,ghi | depart,roundabout-exit-3,exit roundabout right,arrive |
|
||||
| g,i | ghi,ghi,ghi,ghi | depart,roundabout-exit-4,exit roundabout right,arrive |
|
||||
| g,f | ghi,def,def,def | depart,roundabout-exit-1,exit roundabout right,arrive |
|
||||
| g,c | ghi,abc,abc,abc | depart,roundabout-exit-2,exit roundabout right,arrive |
|
||||
| g,l | ghi,jkl,jkl,jkl | depart,roundabout-exit-3,exit roundabout right,arrive |
|
||||
| j,l | jkl,jkl,jkl,jkl | depart,roundabout-exit-4,exit roundabout right,arrive |
|
||||
| j,i | jkl,ghi,ghi,ghi | depart,roundabout-exit-1,exit roundabout right,arrive |
|
||||
| j,f | jkl,def,def,def | depart,roundabout-exit-2,exit roundabout right,arrive |
|
||||
| j,c | jkl,abc,abc,abc | depart,roundabout-exit-3,exit roundabout right,arrive |
|
||||
| waypoints | route | turns |
|
||||
| a,c | abc,abc,abc,abc | depart,roundabout-exit-4,exit roundabout right,arrive |
|
||||
| a,l | abc,jkl,jkl,jkl | depart,roundabout-exit-1,exit roundabout right,arrive |
|
||||
| a,i | abc,ghi,ghi,ghi | depart,roundabout-exit-2,exit roundabout right,arrive |
|
||||
| a,f | abc,def,def,def | depart,roundabout-exit-3,exit roundabout right,arrive |
|
||||
| d,f | def,def,def,def | depart,roundabout-exit-4,exit roundabout right,arrive |
|
||||
| d,c | def,abc,abc,abc | depart,roundabout-exit-1,exit roundabout right,arrive |
|
||||
| d,l | def,jkl,jkl,jkl | depart,roundabout-exit-2,exit roundabout right,arrive |
|
||||
| d,i | def,ghi,ghi,ghi | depart,roundabout-exit-3,exit roundabout right,arrive |
|
||||
| g,i | ghi,ghi,ghi,ghi | depart,roundabout-exit-4,exit roundabout right,arrive |
|
||||
| g,f | ghi,def,def,def | depart,roundabout-exit-1,exit roundabout right,arrive |
|
||||
| g,c | ghi,abc,abc,abc | depart,roundabout-exit-2,exit roundabout right,arrive |
|
||||
| g,l | ghi,jkl,jkl,jkl | depart,roundabout-exit-3,exit roundabout right,arrive |
|
||||
| j,l | jkl,jkl,jkl,jkl | depart,roundabout-exit-4,exit roundabout right,arrive |
|
||||
| j,i | jkl,ghi,ghi,ghi | depart,roundabout-exit-1,exit roundabout right,arrive |
|
||||
| j,f | jkl,def,def,def | depart,roundabout-exit-2,exit roundabout right,arrive |
|
||||
| j,c | jkl,abc,abc,abc | depart,roundabout-exit-3,exit roundabout right,arrive |
|
||||
|
||||
Scenario: Collinear in X
|
||||
Given the node map
|
||||
@@ -259,9 +259,9 @@ Feature: Basic Roundabout
|
||||
| df | |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| a,e | ab,ce,ce | depart,roundabout turn right exit-1,arrive |
|
||||
| a,f | ab,df,df | depart,roundabout turn straight exit-2,arrive |
|
||||
| waypoints | route | turns |
|
||||
| a,e | ab,ce,ce | depart,roundabout turn right exit-1,arrive |
|
||||
| a,f | ab,df,df | depart,roundabout turn straight exit-2,arrive |
|
||||
|
||||
Scenario: Collinear in Y
|
||||
Given the node map
|
||||
@@ -281,7 +281,7 @@ Feature: Basic Roundabout
|
||||
| df | |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| waypoints | route | turns |
|
||||
| a,e | ab,ce,ce | depart,roundabout turn right exit-1,arrive |
|
||||
| a,f | ab,df,df | depart,roundabout turn straight exit-2,arrive |
|
||||
|
||||
@@ -302,7 +302,7 @@ Feature: Basic Roundabout
|
||||
| df | |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| waypoints | route | turns |
|
||||
| a,e | ab,ce,ce | depart,roundabout turn straight exit-1,arrive |
|
||||
| a,f | ab,df,df | depart,roundabout turn left exit-2,arrive |
|
||||
|
||||
@@ -323,7 +323,7 @@ Feature: Basic Roundabout
|
||||
| cf | |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| waypoints | route | turns |
|
||||
| a,e | ad,be,be | depart,roundabout turn straight exit-1,arrive |
|
||||
| a,f | ad,cf,cf | depart,roundabout turn left exit-2,arrive |
|
||||
|
||||
@@ -344,7 +344,7 @@ Feature: Basic Roundabout
|
||||
| bf | |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| waypoints | route | turns |
|
||||
| a,e | ac,de,de | depart,roundabout turn straight exit-1,arrive |
|
||||
| a,f | ac,bf,bf | depart,roundabout turn left exit-2,arrive |
|
||||
|
||||
@@ -368,19 +368,19 @@ Feature: Basic Roundabout
|
||||
| bigecb | roundabout |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| a,d | ab,cd,cd,cd | depart,roundabout-exit-4,exit roundabout right,arrive |
|
||||
| a,f | ab,ef,ef,ef | depart,roundabout-exit-3,exit roundabout right,arrive |
|
||||
| a,h | ab,gh,gh,gh | depart,roundabout-exit-2,exit roundabout right,arrive |
|
||||
| d,f | cd,ef,ef,ef | depart,roundabout-exit-4,exit roundabout right,arrive |
|
||||
| d,h | cd,gh,gh,gh | depart,roundabout-exit-3,exit roundabout right,arrive |
|
||||
| d,a | cd,ab,ab,ab | depart,roundabout-exit-1,exit roundabout right,arrive |
|
||||
| f,h | ef,gh,gh,gh | depart,roundabout-exit-4,exit roundabout right,arrive |
|
||||
| f,a | ef,ab,ab,ab | depart,roundabout-exit-2,exit roundabout right,arrive |
|
||||
| f,d | ef,cd,cd,cd | depart,roundabout-exit-1,exit roundabout right,arrive |
|
||||
| h,a | gh,ab,ab,ab | depart,roundabout-exit-3,exit roundabout right,arrive |
|
||||
| h,d | gh,cd,cd,cd | depart,roundabout-exit-2,exit roundabout right,arrive |
|
||||
| h,f | gh,ef,ef,ef | depart,roundabout-exit-1,exit roundabout right,arrive |
|
||||
| waypoints | route | turns |
|
||||
| a,d | ab,cd,cd,cd | depart,roundabout-exit-4,exit roundabout right,arrive |
|
||||
| a,f | ab,ef,ef,ef | depart,roundabout-exit-3,exit roundabout right,arrive |
|
||||
| a,h | ab,gh,gh,gh | depart,roundabout-exit-2,exit roundabout right,arrive |
|
||||
| d,f | cd,ef,ef,ef | depart,roundabout-exit-4,exit roundabout right,arrive |
|
||||
| d,h | cd,gh,gh,gh | depart,roundabout-exit-3,exit roundabout right,arrive |
|
||||
| d,a | cd,ab,ab,ab | depart,roundabout-exit-1,exit roundabout right,arrive |
|
||||
| f,h | ef,gh,gh,gh | depart,roundabout-exit-4,exit roundabout right,arrive |
|
||||
| f,a | ef,ab,ab,ab | depart,roundabout-exit-2,exit roundabout right,arrive |
|
||||
| f,d | ef,cd,cd,cd | depart,roundabout-exit-1,exit roundabout right,arrive |
|
||||
| h,a | gh,ab,ab,ab | depart,roundabout-exit-3,exit roundabout right,arrive |
|
||||
| h,d | gh,cd,cd,cd | depart,roundabout-exit-2,exit roundabout right,arrive |
|
||||
| h,f | gh,ef,ef,ef | depart,roundabout-exit-1,exit roundabout right,arrive |
|
||||
|
||||
Scenario: Enter and Exit -- Non-Distinct
|
||||
Given the node map
|
||||
@@ -401,19 +401,19 @@ Feature: Basic Roundabout
|
||||
| bgecb | roundabout |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| a,d | ab,cd,cd,cd | depart,roundabout-exit-3,exit roundabout right,arrive |
|
||||
| a,f | ab,ef,ef,ef | depart,roundabout-exit-2,exit roundabout right,arrive |
|
||||
| a,h | ab,gh,gh,gh | depart,roundabout-exit-1,exit roundabout straight,arrive |
|
||||
| d,f | cd,ef,ef,ef | depart,roundabout-exit-3,exit roundabout right,arrive |
|
||||
| d,h | cd,gh,gh,gh | depart,roundabout-exit-2,exit roundabout straight,arrive |
|
||||
| d,a | cd,ab,ab,ab | depart,roundabout-exit-1,exit roundabout right,arrive |
|
||||
| f,h | ef,gh,gh,gh | depart,roundabout-exit-3,exit roundabout straight,arrive |
|
||||
| f,a | ef,ab,ab,ab | depart,roundabout-exit-2,exit roundabout right,arrive |
|
||||
| f,d | ef,cd,cd,cd | depart,roundabout-exit-1,exit roundabout right,arrive |
|
||||
| h,a | gh,ab,ab,ab | depart,roundabout-exit-3,exit roundabout right,arrive |
|
||||
| h,d | gh,cd,cd,cd | depart,roundabout-exit-2,exit roundabout right,arrive |
|
||||
| h,f | gh,ef,ef,ef | depart,roundabout-exit-1,exit roundabout right,arrive |
|
||||
| waypoints | route | turns |
|
||||
| a,d | ab,cd,cd,cd | depart,roundabout-exit-3,exit roundabout right,arrive |
|
||||
| a,f | ab,ef,ef,ef | depart,roundabout-exit-2,exit roundabout right,arrive |
|
||||
| a,h | ab,gh,gh,gh | depart,roundabout-exit-1,exit roundabout straight,arrive |
|
||||
| d,f | cd,ef,ef,ef | depart,roundabout-exit-3,exit roundabout right,arrive |
|
||||
| d,h | cd,gh,gh,gh | depart,roundabout-exit-2,exit roundabout straight,arrive |
|
||||
| d,a | cd,ab,ab,ab | depart,roundabout-exit-1,exit roundabout right,arrive |
|
||||
| f,h | ef,gh,gh,gh | depart,roundabout-exit-3,exit roundabout straight,arrive |
|
||||
| f,a | ef,ab,ab,ab | depart,roundabout-exit-2,exit roundabout right,arrive |
|
||||
| f,d | ef,cd,cd,cd | depart,roundabout-exit-1,exit roundabout right,arrive |
|
||||
| h,a | gh,ab,ab,ab | depart,roundabout-exit-3,exit roundabout right,arrive |
|
||||
| h,d | gh,cd,cd,cd | depart,roundabout-exit-2,exit roundabout right,arrive |
|
||||
| h,f | gh,ef,ef,ef | depart,roundabout-exit-1,exit roundabout right,arrive |
|
||||
|
||||
Scenario: Enter and Exit -- Bearing
|
||||
Given the node map
|
||||
@@ -435,9 +435,9 @@ Feature: Basic Roundabout
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns | bearing |
|
||||
| a,d | ab,cd,cd | depart,roundabout turn left exit-3,arrive | 0->180,180->225,90->0 |
|
||||
| a,f | ab,ef,ef | depart,roundabout turn straight exit-2,arrive | 0->180,180->225,180->0 |
|
||||
| a,h | ab,gh,gh | depart,roundabout turn right exit-1,arrive | 0->180,180->225,270->0 |
|
||||
| a,d | ab,cd,cd | depart,roundabout turn left exit-3,arrive | 0->180,180->225,90->0 |
|
||||
| a,f | ab,ef,ef | depart,roundabout turn straight exit-2,arrive | 0->180,180->225,180->0 |
|
||||
| a,h | ab,gh,gh | depart,roundabout turn right exit-1,arrive | 0->180,180->225,270->0 |
|
||||
|
||||
Scenario: Enter and Exit - Bearings
|
||||
Given the node map
|
||||
@@ -461,9 +461,9 @@ Feature: Basic Roundabout
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns | bearing |
|
||||
| a,d | ab,cd,cd | depart,roundabout turn left exit-3,arrive | 0->180,180->270,90->0 |
|
||||
| a,f | ab,ef,ef | depart,roundabout turn straight exit-2,arrive | 0->180,180->270,180->0 |
|
||||
| a,h | ab,gh,gh | depart,roundabout turn right exit-1,arrive | 0->180,180->270,270->0 |
|
||||
| a,d | ab,cd,cd | depart,roundabout turn left exit-3,arrive | 0->180,180->270,90->0 |
|
||||
| a,f | ab,ef,ef | depart,roundabout turn straight exit-2,arrive | 0->180,180->270,180->0 |
|
||||
| a,h | ab,gh,gh | depart,roundabout turn right exit-1,arrive | 0->180,180->270,270->0 |
|
||||
|
||||
Scenario: Large radius Roundabout Intersection and ways modelled out: East Mission St, North 7th St
|
||||
# http://www.openstreetmap.org/way/348812150
|
||||
@@ -495,18 +495,18 @@ Feature: Basic Roundabout
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| a,e | North 7th St,East Mission St,East Mission St | depart,roundabout turn right exit-1,arrive |
|
||||
| a,h | North 7th St,North 7th St,North 7th St | depart,roundabout turn straight exit-2,arrive |
|
||||
| a,l | North 7th St,East Mission St,East Mission St | depart,roundabout turn left exit-3,arrive |
|
||||
| h,l | North 7th St,East Mission St,East Mission St | depart,roundabout turn right exit-1,arrive |
|
||||
| h,a | North 7th St,North 7th St,North 7th St | depart,roundabout turn straight exit-2,arrive |
|
||||
| h,e | North 7th St,East Mission St,East Mission St | depart,roundabout turn left exit-3,arrive |
|
||||
| e,h | East Mission St,North 7th St,North 7th St | depart,roundabout turn right exit-1,arrive |
|
||||
| e,l | East Mission St,East Mission St,East Mission St | depart,roundabout turn straight exit-2,arrive |
|
||||
| e,a | East Mission St,North 7th St,North 7th St | depart,roundabout turn left exit-3,arrive |
|
||||
| l,a | East Mission St,North 7th St,North 7th St | depart,roundabout turn right exit-1,arrive |
|
||||
| l,e | East Mission St,East Mission St,East Mission St | depart,roundabout turn straight exit-2,arrive |
|
||||
| l,h | East Mission St,North 7th St,North 7th St | depart,roundabout turn left exit-3,arrive |
|
||||
| a,e | North 7th St,East Mission St,East Mission St | depart,roundabout turn right exit-1,arrive |
|
||||
| a,h | North 7th St,North 7th St,North 7th St | depart,roundabout turn straight exit-2,arrive |
|
||||
| a,l | North 7th St,East Mission St,East Mission St | depart,roundabout turn left exit-3,arrive |
|
||||
| h,l | North 7th St,East Mission St,East Mission St | depart,roundabout turn right exit-1,arrive |
|
||||
| h,a | North 7th St,North 7th St,North 7th St | depart,roundabout turn straight exit-2,arrive |
|
||||
| h,e | North 7th St,East Mission St,East Mission St | depart,roundabout turn left exit-3,arrive |
|
||||
| e,h | East Mission St,North 7th St,North 7th St | depart,roundabout turn right exit-1,arrive |
|
||||
| e,l | East Mission St,East Mission St,East Mission St | depart,roundabout turn straight exit-2,arrive |
|
||||
| e,a | East Mission St,North 7th St,North 7th St | depart,roundabout turn left exit-3,arrive |
|
||||
| l,a | East Mission St,North 7th St,North 7th St | depart,roundabout turn right exit-1,arrive |
|
||||
| l,e | East Mission St,East Mission St,East Mission St | depart,roundabout turn straight exit-2,arrive |
|
||||
| l,h | East Mission St,North 7th St,North 7th St | depart,roundabout turn left exit-3,arrive |
|
||||
|
||||
Scenario: Enter and Exit - Traffic Signals
|
||||
Given the node map
|
||||
@@ -534,19 +534,19 @@ Feature: Basic Roundabout
|
||||
| bigjekclb | roundabout |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| a,d | ab,cd,cd | depart,roundabout turn left exit-3,arrive |
|
||||
| a,f | ab,ef,ef | depart,roundabout turn straight exit-2,arrive |
|
||||
| a,h | ab,gh,gh | depart,roundabout turn right exit-1,arrive |
|
||||
| d,f | cd,ef,ef | depart,roundabout turn left exit-3,arrive |
|
||||
| d,h | cd,gh,gh | depart,roundabout turn straight exit-2,arrive |
|
||||
| d,a | cd,ab,ab | depart,roundabout turn right exit-1,arrive |
|
||||
| f,h | ef,gh,gh | depart,roundabout turn left exit-3,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-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 |
|
||||
| waypoints | route | turns |
|
||||
| a,d | ab,cd,cd | depart,roundabout turn left exit-3,arrive |
|
||||
| a,f | ab,ef,ef | depart,roundabout turn straight exit-2,arrive |
|
||||
| a,h | ab,gh,gh | depart,roundabout turn right exit-1,arrive |
|
||||
| d,f | cd,ef,ef | depart,roundabout turn left exit-3,arrive |
|
||||
| d,h | cd,gh,gh | depart,roundabout turn straight exit-2,arrive |
|
||||
| d,a | cd,ab,ab | depart,roundabout turn right exit-1,arrive |
|
||||
| f,h | ef,gh,gh | depart,roundabout turn left exit-3,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-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 |
|
||||
|
||||
#http://www.openstreetmap.org/#map=19/41.03275/-2.18990
|
||||
#at some point we probably want to recognise these situations and don't mention the roundabout at all here
|
||||
|
||||
@@ -18,5 +18,5 @@ Feature: osrm-contract command line options: files
|
||||
Scenario: osrm-contract - Missing input file
|
||||
When I try to run "osrm-contract over-the-rainbow.osrm"
|
||||
And stderr should contain "over-the-rainbow.osrm"
|
||||
And stderr should contain "not found"
|
||||
And stderr should contain "Missing/Broken"
|
||||
And it should exit with an error
|
||||
|
||||
@@ -8,6 +8,7 @@ Feature: osrm-contract command line options: help
|
||||
And stdout should contain "Options:"
|
||||
And stdout should contain "--version"
|
||||
And stdout should contain "--help"
|
||||
And stdout should contain "--verbosity"
|
||||
And stdout should contain "Configuration:"
|
||||
And stdout should contain "--threads"
|
||||
And stdout should contain "--core"
|
||||
@@ -22,6 +23,7 @@ Feature: osrm-contract command line options: help
|
||||
And stdout should contain "Options:"
|
||||
And stdout should contain "--version"
|
||||
And stdout should contain "--help"
|
||||
And stdout should contain "--verbosity"
|
||||
And stdout should contain "Configuration:"
|
||||
And stdout should contain "--threads"
|
||||
And stdout should contain "--core"
|
||||
@@ -36,6 +38,7 @@ Feature: osrm-contract command line options: help
|
||||
And stdout should contain "Options:"
|
||||
And stdout should contain "--version"
|
||||
And stdout should contain "--help"
|
||||
And stdout should contain "--verbosity"
|
||||
And stdout should contain "Configuration:"
|
||||
And stdout should contain "--threads"
|
||||
And stdout should contain "--core"
|
||||
|
||||
@@ -8,7 +8,7 @@ Feature: osrm-contract command line options: version
|
||||
Given the profile "testbot"
|
||||
|
||||
Scenario: osrm-contract - Version, short
|
||||
When I run "osrm-contract --v"
|
||||
When I run "osrm-contract -v"
|
||||
Then stderr should be empty
|
||||
And stdout should contain 1 line
|
||||
And stdout should contain /(v\d{1,2}\.\d{1,2}\.\d{1,2}|\w*-\d+-\w+)/
|
||||
|
||||
@@ -19,5 +19,5 @@ Feature: osrm-contract command line options: files
|
||||
Scenario: osrm-customize - Missing input file
|
||||
When I try to run "osrm-customize over-the-rainbow.osrm"
|
||||
And stderr should contain "over-the-rainbow.osrm"
|
||||
And stderr should contain "not found"
|
||||
And stderr should contain "Missing/Broken"
|
||||
And it should exit with an error
|
||||
|
||||
@@ -8,6 +8,7 @@ Feature: osrm-customize command line options: help
|
||||
And stdout should contain "Options:"
|
||||
And stdout should contain "--version"
|
||||
And stdout should contain "--help"
|
||||
And stdout should contain "--verbosity"
|
||||
And stdout should contain "Configuration:"
|
||||
And stdout should contain "--threads"
|
||||
And it should exit with an error
|
||||
@@ -19,6 +20,7 @@ Feature: osrm-customize command line options: help
|
||||
And stdout should contain "Options:"
|
||||
And stdout should contain "--version"
|
||||
And stdout should contain "--help"
|
||||
And stdout should contain "--verbosity"
|
||||
And stdout should contain "Configuration:"
|
||||
And stdout should contain "--threads"
|
||||
And it should exit successfully
|
||||
@@ -30,6 +32,7 @@ Feature: osrm-customize command line options: help
|
||||
And stdout should contain "Options:"
|
||||
And stdout should contain "--version"
|
||||
And stdout should contain "--help"
|
||||
And stdout should contain "--verbosity"
|
||||
And stdout should contain "Configuration:"
|
||||
And stdout should contain "--threads"
|
||||
And it should exit successfully
|
||||
|
||||
@@ -5,7 +5,7 @@ Feature: osrm-customize command line options: version
|
||||
Given the profile "testbot"
|
||||
|
||||
Scenario: osrm-customize - Version, short
|
||||
When I run "osrm-customize --v"
|
||||
When I run "osrm-customize -v"
|
||||
Then stderr should be empty
|
||||
And stdout should contain 1 line
|
||||
And stdout should contain /(v\d{1,2}\.\d{1,2}\.\d{1,2}|\w*-\d+-\w+)/
|
||||
|
||||
@@ -11,6 +11,7 @@ Feature: osrm-extract command line options: help
|
||||
And stdout should contain "Options:"
|
||||
And stdout should contain "--version"
|
||||
And stdout should contain "--help"
|
||||
And stdout should contain "--verbosity"
|
||||
And stdout should contain "Configuration:"
|
||||
And stdout should contain "--profile"
|
||||
And stdout should contain "--threads"
|
||||
@@ -24,6 +25,7 @@ Feature: osrm-extract command line options: help
|
||||
And stdout should contain "Options:"
|
||||
And stdout should contain "--version"
|
||||
And stdout should contain "--help"
|
||||
And stdout should contain "--verbosity"
|
||||
And stdout should contain "Configuration:"
|
||||
And stdout should contain "--profile"
|
||||
And stdout should contain "--threads"
|
||||
@@ -37,6 +39,7 @@ Feature: osrm-extract command line options: help
|
||||
And stdout should contain "Options:"
|
||||
And stdout should contain "--version"
|
||||
And stdout should contain "--help"
|
||||
And stdout should contain "--verbosity"
|
||||
And stdout should contain "Configuration:"
|
||||
And stdout should contain "--profile"
|
||||
And stdout should contain "--threads"
|
||||
|
||||
@@ -8,7 +8,7 @@ Feature: osrm-extract command line options: version
|
||||
Given the profile "testbot"
|
||||
|
||||
Scenario: osrm-extract - Version, short
|
||||
When I run "osrm-extract --v"
|
||||
When I run "osrm-extract -v"
|
||||
Then stderr should be empty
|
||||
And stdout should contain 1 line
|
||||
And stdout should contain /(v\d{1,2}\.\d{1,2}\.\d{1,2}|\w*-\d+-\w+)/
|
||||
|
||||
@@ -8,6 +8,7 @@ Feature: osrm-partition command line options: help
|
||||
And stdout should contain "Options:"
|
||||
And stdout should contain "--version"
|
||||
And stdout should contain "--help"
|
||||
And stdout should contain "--verbosity"
|
||||
And stdout should contain "Configuration:"
|
||||
And stdout should contain "--threads"
|
||||
And stdout should contain "--balance"
|
||||
@@ -24,6 +25,7 @@ Feature: osrm-partition command line options: help
|
||||
And stdout should contain "Options:"
|
||||
And stdout should contain "--version"
|
||||
And stdout should contain "--help"
|
||||
And stdout should contain "--verbosity"
|
||||
And stdout should contain "Configuration:"
|
||||
And stdout should contain "--threads"
|
||||
And stdout should contain "--balance"
|
||||
@@ -40,6 +42,7 @@ Feature: osrm-partition command line options: help
|
||||
And stdout should contain "Options:"
|
||||
And stdout should contain "--version"
|
||||
And stdout should contain "--help"
|
||||
And stdout should contain "--verbosity"
|
||||
And stdout should contain "Configuration:"
|
||||
And stdout should contain "--threads"
|
||||
And stdout should contain "--balance"
|
||||
|
||||
@@ -5,7 +5,7 @@ Feature: osrm-partition command line options: version
|
||||
Given the profile "testbot"
|
||||
|
||||
Scenario: osrm-partition - Version, short
|
||||
When I run "osrm-partition --v"
|
||||
When I run "osrm-partition -v"
|
||||
Then stderr should be empty
|
||||
And stdout should contain 1 line
|
||||
And stdout should contain /(v\d{1,2}\.\d{1,2}\.\d{1,2}|\w*-\d+-\w+)/
|
||||
|
||||
@@ -11,6 +11,7 @@ Feature: osrm-routed command line options: help
|
||||
And stdout should contain "Options:"
|
||||
And stdout should contain "--version"
|
||||
And stdout should contain "--help"
|
||||
And stdout should contain "--verbosity"
|
||||
And stdout should contain "--trial"
|
||||
And stdout should contain "Configuration:"
|
||||
And stdout should contain "--ip"
|
||||
@@ -30,6 +31,7 @@ Feature: osrm-routed command line options: help
|
||||
And stdout should contain "Options:"
|
||||
And stdout should contain "--version"
|
||||
And stdout should contain "--help"
|
||||
And stdout should contain "--verbosity"
|
||||
And stdout should contain "--trial"
|
||||
And stdout should contain "Configuration:"
|
||||
And stdout should contain "--ip"
|
||||
@@ -49,6 +51,7 @@ Feature: osrm-routed command line options: help
|
||||
And stdout should contain "Options:"
|
||||
And stdout should contain "--version"
|
||||
And stdout should contain "--help"
|
||||
And stdout should contain "--verbosity"
|
||||
And stdout should contain "--trial"
|
||||
And stdout should contain "Configuration:"
|
||||
And stdout should contain "--ip"
|
||||
|
||||
@@ -8,7 +8,7 @@ Feature: osrm-routed command line options: version
|
||||
Given the profile "testbot"
|
||||
|
||||
Scenario: osrm-routed - Version, short
|
||||
When I run "osrm-routed --v"
|
||||
When I run "osrm-routed -v"
|
||||
Then stderr should be empty
|
||||
And stdout should contain 1 line
|
||||
And stdout should contain /(v\d{1,2}\.\d{1,2}\.\d{1,2}|\w*-\d+-\w+)/
|
||||
|
||||
@@ -31,7 +31,10 @@ Feature: Alternative route
|
||||
| 5 | 6 | dc,ca,ab,bd,dc,dc | |
|
||||
| 7 | 8 | ca,ab,bd,dc,ca,ca | |
|
||||
|
||||
@4111
|
||||
# This test case does not work in a platform independent way
|
||||
# since it depends on a specific CH structure that is only
|
||||
# present on linux it seems.
|
||||
@4111 @todo
|
||||
Scenario: Alternative Loop Paths with single node path
|
||||
Given the node map
|
||||
"""
|
||||
|
||||
@@ -67,8 +67,8 @@ Feature: Bearing parameter
|
||||
| from | to | bearings | route | bearing |
|
||||
| 0 | b | 10 10 | bc,bc | 0->0,0->0 |
|
||||
| 0 | b | 90 90 | ab,ab | 0->90,90->0 |
|
||||
| 0 | b | 170 170 | da,da | 0->0,0->0 |
|
||||
| 0 | b | 189 189 | da,da | 0->0,0->0 |
|
||||
| 0 | b | 170 170 | da,da | 0->180,180->0 |
|
||||
| 0 | b | 189 189 | da,da | 0->180,180->0 |
|
||||
| 0 | 1 | 90 270 | ab,cd,cd | 0->90,90->0,270->0 |
|
||||
| 1 | 2 | 10 10 | bc,bc | 0->0,0->0 |
|
||||
| 1 | 2 | 90 90 | ab,cd,ab,ab | 0->90,90->0,270->180,90->0 |
|
||||
@@ -108,12 +108,12 @@ Feature: Bearing parameter
|
||||
| ha | yes | ring |
|
||||
|
||||
When I route I should get
|
||||
| from | to | bearings | route | bearing |
|
||||
| 0 | q | 0 90 | ia,ring,ring | 0->0,0->90,90->0 |
|
||||
| 0 | a | 45 90 | jb,ring,ring | 0->45,45->180,90->0 |
|
||||
| 0 | q | 90 90 | kc,ring,ring | 0->90,90->180,90->0 |
|
||||
| 0 | a | 135 90 | ld,ring,ring | 0->135,135->270,90->0 |
|
||||
| 0 | a | 180 90 | me,ring,ring | 0->180,180->270,90->0 |
|
||||
| 0 | a | 225 90 | nf,ring,ring | 0->225,225->0,90->0 |
|
||||
| 0 | a | 270 90 | og,ring,ring | 0->270,270->0,90->0 |
|
||||
| 0 | a | 315 90 | ph,ring,ring | 0->315,315->90,90->0 |
|
||||
| from | to | bearings | route | bearing |
|
||||
| 0 | q | 0 90 | ia,ring,ring,ring,ring | 0->0,0->90,180->270,270->0,90->0 |
|
||||
| 0 | a | 45 90 | jb,ring,ring,ring,ring | 0->45,45->180,180->270,270->0,90->0 |
|
||||
| 0 | q | 90 90 | kc,ring,ring,ring | 0->90,90->180,270->0,90->0 |
|
||||
| 0 | a | 135 90 | ld,ring,ring,ring | 0->135,135->270,270->0,90->0 |
|
||||
| 0 | a | 180 90 | me,ring,ring,ring | 0->180,180->270,270->0,90->0 |
|
||||
| 0 | a | 225 90 | nf,ring,ring | 0->225,225->0,90->0 |
|
||||
| 0 | a | 270 90 | og,ring,ring | 0->270,270->0,90->0 |
|
||||
| 0 | a | 315 90 | ph,ring,ring | 0->315,315->90,90->0 |
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
@routing @testbot @exclude
|
||||
Feature: Testbot - Exclude flags
|
||||
Background:
|
||||
Given the profile "testbot"
|
||||
Given the node map
|
||||
"""
|
||||
a....b-----c-$-$-d
|
||||
$ $ :
|
||||
e.$.$.f.....g
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | highway | toll | # |
|
||||
| ab | primary | | always drivable |
|
||||
| bc | motorway | | not drivable for exclude=motorway and exclude=motorway,toll |
|
||||
| be | primary | yes | not drivable for exclude=toll and exclude=motorway,toll |
|
||||
| ef | primary | yes | not drivable for exclude=toll and exclude=motorway,toll |
|
||||
| fc | primary | yes | not drivable for exclude=toll and exclude=motorway,toll |
|
||||
| cd | motorway | yes | not drivable for exclude=motorway exclude=toll and exclude=motorway,toll |
|
||||
| fg | primary | | always drivable |
|
||||
| gd | primary | | always drivable |
|
||||
|
||||
Scenario: Testbot - exclude nothing
|
||||
When I route I should get
|
||||
| from | to | route |
|
||||
| a | d | ab,bc,cd,cd |
|
||||
| a | g | ab,be,ef,fg,fg |
|
||||
| a | c | ab,bc,bc |
|
||||
| a | f | ab,be,ef,ef |
|
||||
|
||||
When I match I should get
|
||||
| trace | matchings | duration |
|
||||
| ad | ad | 115 |
|
||||
|
||||
When I request a travel time matrix I should get
|
||||
| | a | d |
|
||||
| a | 0 | 115 |
|
||||
| d | 115 | 0 |
|
||||
|
||||
Scenario: Testbot - exclude motorway
|
||||
Given the query options
|
||||
| exclude | motorway |
|
||||
|
||||
When I route I should get
|
||||
| from | to | route |
|
||||
| a | d | ab,be,ef,fg,gd,gd |
|
||||
| a | g | ab,be,ef,fg,fg |
|
||||
| a | c | ab,be,ef,fc,fc |
|
||||
| a | f | ab,be,ef,ef |
|
||||
|
||||
When I match I should get
|
||||
| trace | matchings | duration |
|
||||
| ad | ad | 125 |
|
||||
|
||||
When I request a travel time matrix I should get
|
||||
| | a | d |
|
||||
| a | 0 | 125 |
|
||||
| d | 125 | 0 |
|
||||
|
||||
Scenario: Testbot - exclude toll
|
||||
Given the query options
|
||||
| exclude | toll |
|
||||
|
||||
When I route I should get
|
||||
| from | to | route |
|
||||
| a | d | |
|
||||
| a | g | |
|
||||
| a | c | ab,bc,bc |
|
||||
| a | f | |
|
||||
| f | d | fg,gd,gd |
|
||||
|
||||
Scenario: Testbot - exclude motorway and toll
|
||||
Given the query options
|
||||
| exclude | motorway,toll |
|
||||
|
||||
When I route I should get
|
||||
| from | to | route |
|
||||
| a | d | |
|
||||
| a | g | |
|
||||
| a | c | |
|
||||
| a | f | |
|
||||
| f | d | fg,gd,gd |
|
||||
|
||||
Scenario: Testbot - exclude with unsupported exclude combination
|
||||
Given the query options
|
||||
| exclude | TwoWords2 |
|
||||
|
||||
When I route I should get
|
||||
| from | to | status | message |
|
||||
| a | d | 400 | Exclude flag combination is not supported. |
|
||||
|
||||
Scenario: Testbot - exclude with invalid exclude class name
|
||||
Given the query options
|
||||
| exclude | foo |
|
||||
|
||||
When I route I should get
|
||||
| from | to | status | message |
|
||||
| a | d | 400 | Exclude flag combination is not supported. |
|
||||
|
||||
@@ -167,4 +167,3 @@ Feature: Traffic - speeds
|
||||
And the data has been extracted
|
||||
When I try to run "osrm-contract --segment-speed-file {speeds_file} {processed_file}"
|
||||
And it should exit successfully
|
||||
|
||||
|
||||
@@ -379,3 +379,36 @@ Feature: Weight tests
|
||||
| a,d | abcd,abcd | 59.9m | 6.996,0 | 7s,0s |
|
||||
| a,e | abcd,ce,ce | 60.1m | 6.005,2.002,0 | 6s,2s,0s |
|
||||
| d,e | abcd,ce,ce | 39.9m | 1.991,2.002,0 | 2s,2s,0s |
|
||||
|
||||
@traffic @speed
|
||||
Scenario: Updating speeds without affecting weights.
|
||||
Given the profile file "testbot" initialized with
|
||||
"""
|
||||
profile.properties.weight_precision = 3
|
||||
"""
|
||||
|
||||
And the node map
|
||||
"""
|
||||
a-----------b
|
||||
\ /
|
||||
c----d
|
||||
"""
|
||||
And the ways
|
||||
| nodes | highway | maxspeed |
|
||||
| ab | living_street | 5 |
|
||||
| acdb | motorway | 100 |
|
||||
|
||||
# Note the comma on the last column - this indicates 'keep existing weight value'
|
||||
And the speed file
|
||||
"""
|
||||
1,2,100,
|
||||
1,3,5,,junk
|
||||
3,4,5,,
|
||||
4,2,5,
|
||||
"""
|
||||
And the contract extra arguments "--segment-speed-file {speeds_file}"
|
||||
And the customize extra arguments "--segment-speed-file {speeds_file}"
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | distance | weights | times |
|
||||
| a,b | acdb,acdb | 78.3m | 11.744,0 | 56.4s,0s |
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
#ifndef OSRM_CONTRACTOR_CONTRACT_EXCLUDABLE_GRAPH_HPP
|
||||
#define OSRM_CONTRACTOR_CONTRACT_EXCLUDABLE_GRAPH_HPP
|
||||
|
||||
#include "contractor/contracted_edge_container.hpp"
|
||||
#include "contractor/contractor_graph.hpp"
|
||||
#include "contractor/graph_contractor.hpp"
|
||||
#include "contractor/graph_contractor_adaptors.hpp"
|
||||
#include "contractor/query_graph.hpp"
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace contractor
|
||||
{
|
||||
|
||||
using GraphFilterAndCore =
|
||||
std::tuple<QueryGraph, std::vector<std::vector<bool>>, std::vector<std::vector<bool>>>;
|
||||
|
||||
inline auto contractExcludableGraph(ContractorGraph contractor_graph_,
|
||||
std::vector<EdgeWeight> node_weights,
|
||||
const std::vector<std::vector<bool>> &filters,
|
||||
const float core_factor = 1.0)
|
||||
{
|
||||
auto num_nodes = contractor_graph_.GetNumberOfNodes();
|
||||
ContractedEdgeContainer edge_container;
|
||||
ContractorGraph shared_core_graph;
|
||||
std::vector<bool> is_shared_core;
|
||||
{
|
||||
ContractorGraph contractor_graph = std::move(contractor_graph_);
|
||||
std::vector<bool> always_allowed(num_nodes, true);
|
||||
for (const auto &filter : filters)
|
||||
{
|
||||
for (const auto node : util::irange<NodeID>(0, num_nodes))
|
||||
{
|
||||
always_allowed[node] = always_allowed[node] && filter[node];
|
||||
}
|
||||
}
|
||||
|
||||
// By not contracting all contractable nodes we avoid creating
|
||||
// a very dense core. This increases the overall graph sizes a little bit
|
||||
// but increases the final CH quality and contraction speed.
|
||||
constexpr float BASE_CORE = 0.9;
|
||||
is_shared_core = contractGraph(contractor_graph,
|
||||
std::move(always_allowed),
|
||||
node_weights,
|
||||
std::min<float>(BASE_CORE, core_factor));
|
||||
|
||||
// Add all non-core edges to container
|
||||
{
|
||||
auto non_core_edges = toEdges<QueryEdge>(contractor_graph);
|
||||
auto new_end =
|
||||
std::remove_if(non_core_edges.begin(), non_core_edges.end(), [&](const auto &edge) {
|
||||
return is_shared_core[edge.source] && is_shared_core[edge.target];
|
||||
});
|
||||
non_core_edges.resize(new_end - non_core_edges.begin());
|
||||
edge_container.Insert(std::move(non_core_edges));
|
||||
}
|
||||
|
||||
// Extract core graph for further contraction
|
||||
shared_core_graph = contractor_graph.Filter(
|
||||
[&is_shared_core](const NodeID node) { return is_shared_core[node]; });
|
||||
}
|
||||
|
||||
std::vector<std::vector<bool>> cores;
|
||||
for (const auto &filter : filters)
|
||||
{
|
||||
auto filtered_core_graph =
|
||||
shared_core_graph.Filter([&filter](const NodeID node) { return filter[node]; });
|
||||
|
||||
auto core = contractGraph(
|
||||
filtered_core_graph, is_shared_core, is_shared_core, node_weights, core_factor);
|
||||
if (core_factor == 1.0)
|
||||
{
|
||||
core.clear();
|
||||
}
|
||||
cores.push_back(std::move(core));
|
||||
|
||||
edge_container.Merge(toEdges<QueryEdge>(std::move(filtered_core_graph)));
|
||||
}
|
||||
|
||||
return GraphFilterAndCore{QueryGraph{num_nodes, std::move(edge_container.edges)},
|
||||
edge_container.MakeEdgeFilters(),
|
||||
std::move(cores)};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,153 @@
|
||||
#ifndef OSRM_CONTRACTOR_CONTRACTED_EDGE_CONTAINER_HPP
|
||||
#define OSRM_CONTRACTOR_CONTRACTED_EDGE_CONTAINER_HPP
|
||||
|
||||
#include "contractor/query_edge.hpp"
|
||||
#include "util/deallocating_vector.hpp"
|
||||
|
||||
#include <climits>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace contractor
|
||||
{
|
||||
|
||||
struct ContractedEdgeContainer
|
||||
{
|
||||
private:
|
||||
using MergedFlags = std::uint8_t;
|
||||
static constexpr auto ALL_FLAGS = 0xFF;
|
||||
|
||||
static bool mergeCompare(const QueryEdge &lhs, const QueryEdge &rhs)
|
||||
{
|
||||
return std::tie(lhs.source,
|
||||
lhs.target,
|
||||
lhs.data.shortcut,
|
||||
lhs.data.turn_id,
|
||||
lhs.data.weight,
|
||||
lhs.data.duration,
|
||||
lhs.data.forward,
|
||||
lhs.data.backward) < std::tie(rhs.source,
|
||||
rhs.target,
|
||||
rhs.data.shortcut,
|
||||
rhs.data.turn_id,
|
||||
rhs.data.weight,
|
||||
rhs.data.duration,
|
||||
rhs.data.forward,
|
||||
rhs.data.backward);
|
||||
}
|
||||
|
||||
static bool mergable(const QueryEdge &lhs, const QueryEdge &rhs)
|
||||
{
|
||||
// only true if both are equal
|
||||
return !mergeCompare(lhs, rhs) && !mergeCompare(rhs, lhs);
|
||||
}
|
||||
|
||||
public:
|
||||
void Insert(util::DeallocatingVector<QueryEdge> new_edges)
|
||||
{
|
||||
BOOST_ASSERT(edges.size() == 0);
|
||||
BOOST_ASSERT(flags.empty());
|
||||
|
||||
edges = std::move(new_edges);
|
||||
flags.resize(edges.size(), ALL_FLAGS);
|
||||
}
|
||||
|
||||
void Merge(util::DeallocatingVector<QueryEdge> new_edges)
|
||||
{
|
||||
BOOST_ASSERT(index < sizeof(MergedFlags) * CHAR_BIT);
|
||||
|
||||
const MergedFlags flag = 1 << index++;
|
||||
|
||||
std::vector<MergedFlags> merged_flags;
|
||||
merged_flags.reserve(flags.size() * 1.1);
|
||||
util::DeallocatingVector<QueryEdge> merged_edges;
|
||||
merged_edges.reserve(edges.size() * 1.1);
|
||||
|
||||
auto flags_iter = flags.begin();
|
||||
// destructive iterators, this is single-pass only
|
||||
// FIXME using dbegin() dend() will result in segfaults.
|
||||
auto edges_iter = edges.dbegin();
|
||||
auto edges_end = edges.dend();
|
||||
auto new_edges_iter = new_edges.dbegin();
|
||||
auto new_edges_end = new_edges.dend();
|
||||
|
||||
while (edges_iter != edges_end && new_edges_iter != new_edges_end)
|
||||
{
|
||||
while (edges_iter != edges_end && mergeCompare(*edges_iter, *new_edges_iter))
|
||||
{
|
||||
merged_edges.push_back(*edges_iter);
|
||||
merged_flags.push_back(*flags_iter);
|
||||
edges_iter++;
|
||||
flags_iter++;
|
||||
}
|
||||
|
||||
if (edges_iter == edges_end)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
while (new_edges_iter != new_edges_end && mergeCompare(*new_edges_iter, *edges_iter))
|
||||
{
|
||||
merged_edges.push_back(*new_edges_iter);
|
||||
merged_flags.push_back(flag);
|
||||
new_edges_iter++;
|
||||
}
|
||||
|
||||
if (new_edges_iter == new_edges_end)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
while (edges_iter != edges_end && new_edges_iter != new_edges_end &&
|
||||
mergable(*edges_iter, *new_edges_iter))
|
||||
{
|
||||
merged_edges.push_back(*edges_iter);
|
||||
merged_flags.push_back(*flags_iter | flag);
|
||||
|
||||
edges_iter++;
|
||||
flags_iter++;
|
||||
new_edges_iter++;
|
||||
}
|
||||
}
|
||||
|
||||
while (edges_iter != edges_end)
|
||||
{
|
||||
BOOST_ASSERT(new_edges_iter == new_edges_end);
|
||||
merged_edges.push_back(*edges_iter++);
|
||||
merged_flags.push_back(*flags_iter++);
|
||||
}
|
||||
|
||||
while (new_edges_iter != new_edges_end)
|
||||
{
|
||||
BOOST_ASSERT(edges_iter == edges_end);
|
||||
merged_edges.push_back(*new_edges_iter++);
|
||||
merged_flags.push_back(flag);
|
||||
}
|
||||
|
||||
flags = std::move(merged_flags);
|
||||
edges = std::move(merged_edges);
|
||||
}
|
||||
|
||||
auto MakeEdgeFilters() const
|
||||
{
|
||||
std::vector<std::vector<bool>> filters(index);
|
||||
for (const auto flag_index : util::irange<std::size_t>(0, index))
|
||||
{
|
||||
MergedFlags mask = 1 << flag_index;
|
||||
for (const auto flag : flags)
|
||||
{
|
||||
filters[flag_index].push_back(flag & mask);
|
||||
}
|
||||
}
|
||||
|
||||
return filters;
|
||||
}
|
||||
|
||||
std::size_t index = 0;
|
||||
std::vector<MergedFlags> flags;
|
||||
util::DeallocatingVector<QueryEdge> edges;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -43,12 +43,9 @@ namespace contractor
|
||||
struct ContractorConfig final : storage::IOConfig
|
||||
{
|
||||
ContractorConfig()
|
||||
: IOConfig(
|
||||
{
|
||||
".osrm",
|
||||
},
|
||||
{},
|
||||
{".osrm.level", ".osrm.core", ".osrm.hsgr", ".osrm.enw"}),
|
||||
: IOConfig({".osrm.ebg", ".osrm.ebg_nodes", ".osrm.properties"},
|
||||
{},
|
||||
{".osrm.level", ".osrm.core", ".osrm.hsgr", ".osrm.enw"}),
|
||||
requested_num_threads(0)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
#ifndef OSRM_CONTRACTOR_DIJKSTRA_HPP
|
||||
#define OSRM_CONTRACTOR_DIJKSTRA_HPP
|
||||
|
||||
#include "contractor/contractor_graph.hpp"
|
||||
#include "contractor/contractor_heap.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace contractor
|
||||
{
|
||||
|
||||
// allow access to the heap itself, add Dijkstra functionality on top
|
||||
class ContractorDijkstra
|
||||
{
|
||||
public:
|
||||
ContractorDijkstra(std::size_t heap_size);
|
||||
|
||||
// search the graph up
|
||||
void Run(const unsigned number_of_targets,
|
||||
const int node_limit,
|
||||
const int weight_limit,
|
||||
const NodeID forbidden_node,
|
||||
const ContractorGraph &graph);
|
||||
|
||||
// adaption of the heap interface
|
||||
void Clear();
|
||||
bool WasInserted(const NodeID node) const;
|
||||
void Insert(const NodeID node,
|
||||
const ContractorHeap::WeightType weight,
|
||||
const ContractorHeap::DataType &data);
|
||||
|
||||
// cannot be const due to node-hash access in the binary heap :(
|
||||
ContractorHeap::WeightType GetKey(const NodeID node);
|
||||
|
||||
private:
|
||||
void RelaxNode(const NodeID node,
|
||||
const int node_weight,
|
||||
const NodeID forbidden_node,
|
||||
const ContractorGraph &graph);
|
||||
|
||||
ContractorHeap heap;
|
||||
};
|
||||
|
||||
} // namespace contractor
|
||||
} // namespace osrm
|
||||
|
||||
#endif // OSRM_CONTRACTOR_DIJKSTRA_HPP
|
||||
@@ -12,8 +12,7 @@ namespace contractor
|
||||
struct ContractorEdgeData
|
||||
{
|
||||
ContractorEdgeData()
|
||||
: weight(0), duration(0), id(0), originalEdges(0), shortcut(0), forward(0), backward(0),
|
||||
is_original_via_node_ID(false)
|
||||
: weight(0), duration(0), id(0), originalEdges(0), shortcut(0), forward(0), backward(0)
|
||||
{
|
||||
}
|
||||
ContractorEdgeData(EdgeWeight weight,
|
||||
@@ -24,18 +23,17 @@ struct ContractorEdgeData
|
||||
bool forward,
|
||||
bool backward)
|
||||
: weight(weight), duration(duration), id(id),
|
||||
originalEdges(std::min((1u << 28) - 1u, original_edges)), shortcut(shortcut),
|
||||
forward(forward), backward(backward), is_original_via_node_ID(false)
|
||||
originalEdges(std::min((1u << 29) - 1u, original_edges)), shortcut(shortcut),
|
||||
forward(forward), backward(backward)
|
||||
{
|
||||
}
|
||||
EdgeWeight weight;
|
||||
EdgeWeight duration;
|
||||
unsigned id;
|
||||
unsigned originalEdges : 28;
|
||||
unsigned originalEdges : 29;
|
||||
bool shortcut : 1;
|
||||
bool forward : 1;
|
||||
bool backward : 1;
|
||||
bool is_original_via_node_ID : 1;
|
||||
};
|
||||
|
||||
using ContractorGraph = util::DynamicGraph<ContractorEdgeData>;
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef OSRM_CONTRACTOR_SEARCH_HPP
|
||||
#define OSRM_CONTRACTOR_SEARCH_HPP
|
||||
|
||||
#include "contractor/contractor_graph.hpp"
|
||||
#include "contractor/contractor_heap.hpp"
|
||||
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace contractor
|
||||
{
|
||||
|
||||
void search(ContractorHeap &heap,
|
||||
const ContractorGraph &graph,
|
||||
const unsigned number_of_targets,
|
||||
const int node_limit,
|
||||
const EdgeWeight weight_limit,
|
||||
const NodeID forbidden_node);
|
||||
|
||||
} // namespace contractor
|
||||
} // namespace osrm
|
||||
|
||||
#endif // OSRM_CONTRACTOR_DIJKSTRA_HPP
|
||||
@@ -16,54 +16,85 @@ namespace files
|
||||
{
|
||||
// reads .osrm.core
|
||||
template <typename CoreVectorT>
|
||||
void readCoreMarker(const boost::filesystem::path &path, CoreVectorT &is_core_node)
|
||||
void readCoreMarker(const boost::filesystem::path &path, std::vector<CoreVectorT> &cores)
|
||||
{
|
||||
static_assert(util::is_view_or_vector<bool, CoreVectorT>::value,
|
||||
"is_core_node must be a vector");
|
||||
"cores must be a vector of boolean vectors");
|
||||
storage::io::FileReader reader(path, storage::io::FileReader::VerifyFingerprint);
|
||||
|
||||
storage::serialization::read(reader, is_core_node);
|
||||
auto num_cores = reader.ReadElementCount64();
|
||||
cores.resize(num_cores);
|
||||
for (const auto index : util::irange<std::size_t>(0, num_cores))
|
||||
{
|
||||
storage::serialization::read(reader, cores[index]);
|
||||
}
|
||||
}
|
||||
|
||||
// writes .osrm.core
|
||||
template <typename CoreVectorT>
|
||||
void writeCoreMarker(const boost::filesystem::path &path, const CoreVectorT &is_core_node)
|
||||
void writeCoreMarker(const boost::filesystem::path &path, const std::vector<CoreVectorT> &cores)
|
||||
{
|
||||
static_assert(util::is_view_or_vector<bool, CoreVectorT>::value,
|
||||
"is_core_node must be a vector");
|
||||
"cores must be a vector of boolean vectors");
|
||||
storage::io::FileWriter writer(path, storage::io::FileWriter::GenerateFingerprint);
|
||||
|
||||
storage::serialization::write(writer, is_core_node);
|
||||
writer.WriteElementCount64(cores.size());
|
||||
for (const auto &core : cores)
|
||||
{
|
||||
storage::serialization::write(writer, core);
|
||||
}
|
||||
}
|
||||
|
||||
// reads .osrm.hsgr file
|
||||
template <typename QueryGraphT>
|
||||
inline void readGraph(const boost::filesystem::path &path, unsigned &checksum, QueryGraphT &graph)
|
||||
template <typename QueryGraphT, typename EdgeFilterT>
|
||||
inline void readGraph(const boost::filesystem::path &path,
|
||||
unsigned &checksum,
|
||||
QueryGraphT &graph,
|
||||
std::vector<EdgeFilterT> &edge_filter)
|
||||
{
|
||||
static_assert(std::is_same<QueryGraphView, QueryGraphT>::value ||
|
||||
std::is_same<QueryGraph, QueryGraphT>::value,
|
||||
"graph must be of type QueryGraph<>");
|
||||
static_assert(std::is_same<EdgeFilterT, std::vector<bool>>::value ||
|
||||
std::is_same<EdgeFilterT, util::vector_view<bool>>::value,
|
||||
"edge_filter must be a container of vector<bool> or vector_view<bool>");
|
||||
|
||||
const auto fingerprint = storage::io::FileReader::VerifyFingerprint;
|
||||
storage::io::FileReader reader{path, fingerprint};
|
||||
|
||||
reader.ReadInto(checksum);
|
||||
util::serialization::read(reader, graph);
|
||||
auto count = reader.ReadElementCount64();
|
||||
edge_filter.resize(count);
|
||||
for (const auto index : util::irange<std::size_t>(0, count))
|
||||
{
|
||||
storage::serialization::read(reader, edge_filter[index]);
|
||||
}
|
||||
}
|
||||
|
||||
// writes .osrm.hsgr file
|
||||
template <typename QueryGraphT>
|
||||
inline void
|
||||
writeGraph(const boost::filesystem::path &path, unsigned checksum, const QueryGraphT &graph)
|
||||
template <typename QueryGraphT, typename EdgeFilterT>
|
||||
inline void writeGraph(const boost::filesystem::path &path,
|
||||
unsigned checksum,
|
||||
const QueryGraphT &graph,
|
||||
const std::vector<EdgeFilterT> &edge_filter)
|
||||
{
|
||||
static_assert(std::is_same<QueryGraphView, QueryGraphT>::value ||
|
||||
std::is_same<QueryGraph, QueryGraphT>::value,
|
||||
"graph must be of type QueryGraph<>");
|
||||
static_assert(std::is_same<EdgeFilterT, std::vector<bool>>::value ||
|
||||
std::is_same<EdgeFilterT, util::vector_view<bool>>::value,
|
||||
"edge_filter must be a container of vector<bool> or vector_view<bool>");
|
||||
const auto fingerprint = storage::io::FileWriter::GenerateFingerprint;
|
||||
storage::io::FileWriter writer{path, fingerprint};
|
||||
|
||||
writer.WriteOne(checksum);
|
||||
util::serialization::write(writer, graph);
|
||||
writer.WriteElementCount64(edge_filter.size());
|
||||
for (const auto &filter : edge_filter)
|
||||
{
|
||||
storage::serialization::write(writer, filter);
|
||||
}
|
||||
}
|
||||
|
||||
// reads .levels file
|
||||
|
||||
@@ -1,427 +1,41 @@
|
||||
#ifndef OSRM_CONTRACTOR_GRAPH_CONTRACTOR_HPP
|
||||
#define OSRM_CONTRACTOR_GRAPH_CONTRACTOR_HPP
|
||||
|
||||
#include "contractor/contractor_dijkstra.hpp"
|
||||
#include "contractor/contractor_graph.hpp"
|
||||
#include "contractor/query_edge.hpp"
|
||||
#include "util/deallocating_vector.hpp"
|
||||
#include "util/integer_range.hpp"
|
||||
#include "util/log.hpp"
|
||||
#include "util/percent.hpp"
|
||||
#include "util/timing_util.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
#include "util/xor_fast_hash.hpp"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include "util/filtered_graph.hpp"
|
||||
|
||||
#include <tbb/enumerable_thread_specific.h>
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <tbb/parallel_sort.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
#if USE_STXXL_LIBRARY
|
||||
#include <stxxl/vector>
|
||||
#endif
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace contractor
|
||||
{
|
||||
|
||||
class GraphContractor
|
||||
std::vector<bool> contractGraph(ContractorGraph &graph,
|
||||
std::vector<bool> node_is_uncontracted,
|
||||
std::vector<bool> node_is_contractable,
|
||||
std::vector<EdgeWeight> node_weights,
|
||||
double core_factor = 1.0);
|
||||
|
||||
// Overload for contracting all nodes
|
||||
inline auto contractGraph(ContractorGraph &graph,
|
||||
std::vector<EdgeWeight> node_weights,
|
||||
double core_factor = 1.0)
|
||||
{
|
||||
private:
|
||||
#if USE_STXXL_LIBRARY
|
||||
template <typename T> using ExternalVector = stxxl::vector<T>;
|
||||
#else
|
||||
template <typename T> using ExternalVector = std::vector<T>;
|
||||
#endif
|
||||
return contractGraph(graph, {}, {}, std::move(node_weights), core_factor);
|
||||
}
|
||||
|
||||
struct ContractorThreadData
|
||||
{
|
||||
ContractorDijkstra dijkstra;
|
||||
std::vector<ContractorEdge> inserted_edges;
|
||||
std::vector<NodeID> neighbours;
|
||||
explicit ContractorThreadData(NodeID nodes) : dijkstra(nodes) {}
|
||||
};
|
||||
|
||||
using NodeDepth = int;
|
||||
|
||||
struct ContractionStats
|
||||
{
|
||||
int edges_deleted_count;
|
||||
int edges_added_count;
|
||||
int original_edges_deleted_count;
|
||||
int original_edges_added_count;
|
||||
ContractionStats()
|
||||
: edges_deleted_count(0), edges_added_count(0), original_edges_deleted_count(0),
|
||||
original_edges_added_count(0)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct RemainingNodeData
|
||||
{
|
||||
RemainingNodeData() : id(0), is_independent(false) {}
|
||||
NodeID id : 31;
|
||||
bool is_independent : 1;
|
||||
};
|
||||
|
||||
struct ThreadDataContainer
|
||||
{
|
||||
explicit ThreadDataContainer(int number_of_nodes) : number_of_nodes(number_of_nodes) {}
|
||||
|
||||
inline ContractorThreadData *GetThreadData()
|
||||
{
|
||||
bool exists = false;
|
||||
auto &ref = data.local(exists);
|
||||
if (!exists)
|
||||
{
|
||||
// ref = std::make_shared<ContractorThreadData>(number_of_nodes);
|
||||
ref = std::make_shared<ContractorThreadData>(4000);
|
||||
}
|
||||
|
||||
return ref.get();
|
||||
}
|
||||
|
||||
int number_of_nodes;
|
||||
using EnumerableThreadData =
|
||||
tbb::enumerable_thread_specific<std::shared_ptr<ContractorThreadData>>;
|
||||
EnumerableThreadData data;
|
||||
};
|
||||
|
||||
public:
|
||||
GraphContractor(int nodes, std::vector<ContractorEdge> input_edge_list);
|
||||
|
||||
GraphContractor(int nodes,
|
||||
std::vector<ContractorEdge> edges,
|
||||
std::vector<float> node_levels_,
|
||||
std::vector<EdgeWeight> node_weights_);
|
||||
|
||||
/* Flush all data from the contraction to disc and reorder stuff for better locality */
|
||||
void FlushDataAndRebuildContractorGraph(ThreadDataContainer &thread_data_list,
|
||||
std::vector<RemainingNodeData> &remaining_nodes,
|
||||
std::vector<float> &node_priorities);
|
||||
|
||||
void Run(double core_factor = 1.0);
|
||||
|
||||
std::vector<bool> GetCoreMarker();
|
||||
|
||||
std::vector<float> GetNodeLevels();
|
||||
|
||||
template <class Edge> inline util::DeallocatingVector<Edge> GetEdges()
|
||||
{
|
||||
util::DeallocatingVector<Edge> edges;
|
||||
|
||||
util::UnbufferedLog log;
|
||||
log << "Getting edges of minimized graph ";
|
||||
util::Percent p(log, contractor_graph->GetNumberOfNodes());
|
||||
const NodeID number_of_nodes = contractor_graph->GetNumberOfNodes();
|
||||
if (contractor_graph->GetNumberOfNodes())
|
||||
{
|
||||
Edge new_edge;
|
||||
for (const auto node : util::irange(0u, number_of_nodes))
|
||||
{
|
||||
p.PrintStatus(node);
|
||||
for (auto edge : contractor_graph->GetAdjacentEdgeRange(node))
|
||||
{
|
||||
const NodeID target = contractor_graph->GetTarget(edge);
|
||||
const ContractorGraph::EdgeData &data = contractor_graph->GetEdgeData(edge);
|
||||
if (!orig_node_id_from_new_node_id_map.empty())
|
||||
{
|
||||
new_edge.source = orig_node_id_from_new_node_id_map[node];
|
||||
new_edge.target = orig_node_id_from_new_node_id_map[target];
|
||||
}
|
||||
else
|
||||
{
|
||||
new_edge.source = node;
|
||||
new_edge.target = target;
|
||||
}
|
||||
BOOST_ASSERT_MSG(SPECIAL_NODEID != new_edge.source, "Source id invalid");
|
||||
BOOST_ASSERT_MSG(SPECIAL_NODEID != new_edge.target, "Target id invalid");
|
||||
new_edge.data.weight = data.weight;
|
||||
new_edge.data.duration = data.duration;
|
||||
new_edge.data.shortcut = data.shortcut;
|
||||
if (!data.is_original_via_node_ID && !orig_node_id_from_new_node_id_map.empty())
|
||||
{
|
||||
// tranlate the _node id_ of the shortcutted node
|
||||
new_edge.data.turn_id = orig_node_id_from_new_node_id_map[data.id];
|
||||
}
|
||||
else
|
||||
{
|
||||
new_edge.data.turn_id = data.id;
|
||||
}
|
||||
BOOST_ASSERT_MSG(new_edge.data.turn_id != INT_MAX, // 2^31
|
||||
"edge id invalid");
|
||||
new_edge.data.forward = data.forward;
|
||||
new_edge.data.backward = data.backward;
|
||||
edges.push_back(new_edge);
|
||||
}
|
||||
}
|
||||
}
|
||||
contractor_graph.reset();
|
||||
orig_node_id_from_new_node_id_map.clear();
|
||||
orig_node_id_from_new_node_id_map.shrink_to_fit();
|
||||
|
||||
BOOST_ASSERT(0 == orig_node_id_from_new_node_id_map.capacity());
|
||||
|
||||
edges.append(external_edge_list.begin(), external_edge_list.end());
|
||||
external_edge_list.clear();
|
||||
|
||||
// sort and remove duplicates
|
||||
tbb::parallel_sort(edges.begin(), edges.end());
|
||||
auto new_end = std::unique(edges.begin(), edges.end());
|
||||
edges.resize(new_end - edges.begin());
|
||||
|
||||
return edges;
|
||||
}
|
||||
|
||||
private:
|
||||
float EvaluateNodePriority(ContractorThreadData *const data,
|
||||
const NodeDepth node_depth,
|
||||
const NodeID node);
|
||||
|
||||
template <bool RUNSIMULATION>
|
||||
bool
|
||||
ContractNode(ContractorThreadData *data, const NodeID node, ContractionStats *stats = nullptr)
|
||||
{
|
||||
auto &dijkstra = data->dijkstra;
|
||||
std::size_t inserted_edges_size = data->inserted_edges.size();
|
||||
std::vector<ContractorEdge> &inserted_edges = data->inserted_edges;
|
||||
constexpr bool SHORTCUT_ARC = true;
|
||||
constexpr bool FORWARD_DIRECTION_ENABLED = true;
|
||||
constexpr bool FORWARD_DIRECTION_DISABLED = false;
|
||||
constexpr bool REVERSE_DIRECTION_ENABLED = true;
|
||||
constexpr bool REVERSE_DIRECTION_DISABLED = false;
|
||||
|
||||
for (auto in_edge : contractor_graph->GetAdjacentEdgeRange(node))
|
||||
{
|
||||
const ContractorEdgeData &in_data = contractor_graph->GetEdgeData(in_edge);
|
||||
const NodeID source = contractor_graph->GetTarget(in_edge);
|
||||
if (source == node)
|
||||
continue;
|
||||
|
||||
if (RUNSIMULATION)
|
||||
{
|
||||
BOOST_ASSERT(stats != nullptr);
|
||||
++stats->edges_deleted_count;
|
||||
stats->original_edges_deleted_count += in_data.originalEdges;
|
||||
}
|
||||
if (!in_data.backward)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
dijkstra.Clear();
|
||||
dijkstra.Insert(source, 0, ContractorHeapData{});
|
||||
EdgeWeight max_weight = 0;
|
||||
unsigned number_of_targets = 0;
|
||||
|
||||
for (auto out_edge : contractor_graph->GetAdjacentEdgeRange(node))
|
||||
{
|
||||
const ContractorEdgeData &out_data = contractor_graph->GetEdgeData(out_edge);
|
||||
if (!out_data.forward)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const NodeID target = contractor_graph->GetTarget(out_edge);
|
||||
if (node == target)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const EdgeWeight path_weight = in_data.weight + out_data.weight;
|
||||
if (target == source)
|
||||
{
|
||||
if (path_weight < node_weights[node])
|
||||
{
|
||||
if (RUNSIMULATION)
|
||||
{
|
||||
// make sure to prune better, but keep inserting this loop if it should
|
||||
// still be the best
|
||||
// CAREFUL: This only works due to the independent node-setting. This
|
||||
// guarantees that source is not connected to another node that is
|
||||
// contracted
|
||||
node_weights[source] = path_weight + 1;
|
||||
BOOST_ASSERT(stats != nullptr);
|
||||
stats->edges_added_count += 2;
|
||||
stats->original_edges_added_count +=
|
||||
2 * (out_data.originalEdges + in_data.originalEdges);
|
||||
}
|
||||
else
|
||||
{
|
||||
// CAREFUL: This only works due to the independent node-setting. This
|
||||
// guarantees that source is not connected to another node that is
|
||||
// contracted
|
||||
node_weights[source] = path_weight; // make sure to prune better
|
||||
inserted_edges.emplace_back(source,
|
||||
target,
|
||||
path_weight,
|
||||
in_data.duration + out_data.duration,
|
||||
out_data.originalEdges +
|
||||
in_data.originalEdges,
|
||||
node,
|
||||
SHORTCUT_ARC,
|
||||
FORWARD_DIRECTION_ENABLED,
|
||||
REVERSE_DIRECTION_DISABLED);
|
||||
|
||||
inserted_edges.emplace_back(target,
|
||||
source,
|
||||
path_weight,
|
||||
in_data.duration + out_data.duration,
|
||||
out_data.originalEdges +
|
||||
in_data.originalEdges,
|
||||
node,
|
||||
SHORTCUT_ARC,
|
||||
FORWARD_DIRECTION_DISABLED,
|
||||
REVERSE_DIRECTION_ENABLED);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
max_weight = std::max(max_weight, path_weight);
|
||||
if (!dijkstra.WasInserted(target))
|
||||
{
|
||||
dijkstra.Insert(target, INVALID_EDGE_WEIGHT, ContractorHeapData{0, true});
|
||||
++number_of_targets;
|
||||
}
|
||||
}
|
||||
|
||||
if (RUNSIMULATION)
|
||||
{
|
||||
const int constexpr SIMULATION_SEARCH_SPACE_SIZE = 1000;
|
||||
dijkstra.Run(number_of_targets,
|
||||
SIMULATION_SEARCH_SPACE_SIZE,
|
||||
max_weight,
|
||||
node,
|
||||
*contractor_graph);
|
||||
}
|
||||
else
|
||||
{
|
||||
const int constexpr FULL_SEARCH_SPACE_SIZE = 2000;
|
||||
dijkstra.Run(
|
||||
number_of_targets, FULL_SEARCH_SPACE_SIZE, max_weight, node, *contractor_graph);
|
||||
}
|
||||
for (auto out_edge : contractor_graph->GetAdjacentEdgeRange(node))
|
||||
{
|
||||
const ContractorEdgeData &out_data = contractor_graph->GetEdgeData(out_edge);
|
||||
if (!out_data.forward)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const NodeID target = contractor_graph->GetTarget(out_edge);
|
||||
if (target == node)
|
||||
continue;
|
||||
|
||||
const EdgeWeight path_weight = in_data.weight + out_data.weight;
|
||||
const EdgeWeight weight = dijkstra.GetKey(target);
|
||||
if (path_weight < weight)
|
||||
{
|
||||
if (RUNSIMULATION)
|
||||
{
|
||||
BOOST_ASSERT(stats != nullptr);
|
||||
stats->edges_added_count += 2;
|
||||
stats->original_edges_added_count +=
|
||||
2 * (out_data.originalEdges + in_data.originalEdges);
|
||||
}
|
||||
else
|
||||
{
|
||||
inserted_edges.emplace_back(source,
|
||||
target,
|
||||
path_weight,
|
||||
in_data.duration + out_data.duration,
|
||||
out_data.originalEdges + in_data.originalEdges,
|
||||
node,
|
||||
SHORTCUT_ARC,
|
||||
FORWARD_DIRECTION_ENABLED,
|
||||
REVERSE_DIRECTION_DISABLED);
|
||||
|
||||
inserted_edges.emplace_back(target,
|
||||
source,
|
||||
path_weight,
|
||||
in_data.duration + out_data.duration,
|
||||
out_data.originalEdges + in_data.originalEdges,
|
||||
node,
|
||||
SHORTCUT_ARC,
|
||||
FORWARD_DIRECTION_DISABLED,
|
||||
REVERSE_DIRECTION_ENABLED);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check For One-Way Streets to decide on the creation of self-loops
|
||||
|
||||
if (!RUNSIMULATION)
|
||||
{
|
||||
std::size_t iend = inserted_edges.size();
|
||||
for (std::size_t i = inserted_edges_size; i < iend; ++i)
|
||||
{
|
||||
bool found = false;
|
||||
for (std::size_t other = i + 1; other < iend; ++other)
|
||||
{
|
||||
if (inserted_edges[other].source != inserted_edges[i].source)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (inserted_edges[other].target != inserted_edges[i].target)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (inserted_edges[other].data.weight != inserted_edges[i].data.weight)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (inserted_edges[other].data.shortcut != inserted_edges[i].data.shortcut)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
inserted_edges[other].data.forward |= inserted_edges[i].data.forward;
|
||||
inserted_edges[other].data.backward |= inserted_edges[i].data.backward;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
inserted_edges[inserted_edges_size++] = inserted_edges[i];
|
||||
}
|
||||
}
|
||||
inserted_edges.resize(inserted_edges_size);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DeleteIncomingEdges(ContractorThreadData *data, const NodeID node);
|
||||
|
||||
bool UpdateNodeNeighbours(std::vector<float> &priorities,
|
||||
std::vector<NodeDepth> &node_depth,
|
||||
ContractorThreadData *const data,
|
||||
const NodeID node);
|
||||
|
||||
bool IsNodeIndependent(const std::vector<float> &priorities,
|
||||
ContractorThreadData *const data,
|
||||
NodeID node) const;
|
||||
|
||||
// This bias function takes up 22 assembly instructions in total on X86
|
||||
bool Bias(const NodeID a, const NodeID b) const;
|
||||
|
||||
std::shared_ptr<ContractorGraph> contractor_graph;
|
||||
ExternalVector<QueryEdge> external_edge_list;
|
||||
std::vector<NodeID> orig_node_id_from_new_node_id_map;
|
||||
std::vector<float> node_levels;
|
||||
|
||||
// A list of weights for every node in the graph.
|
||||
// The weight represents the cost for a u-turn on the segment in the base-graph in addition to
|
||||
// its traversal.
|
||||
// During contraction, self-loops are checked against this node weight to ensure that necessary
|
||||
// self-loops are added.
|
||||
std::vector<EdgeWeight> node_weights;
|
||||
std::vector<bool> is_core_node;
|
||||
util::XORFastHash<> fast_hash;
|
||||
};
|
||||
// Overload no contracted nodes
|
||||
inline auto contractGraph(ContractorGraph &graph,
|
||||
std::vector<bool> node_is_contractable,
|
||||
std::vector<EdgeWeight> node_weights,
|
||||
double core_factor = 1.0)
|
||||
{
|
||||
return contractGraph(
|
||||
graph, {}, std::move(node_is_contractable), std::move(node_weights), core_factor);
|
||||
}
|
||||
|
||||
} // namespace contractor
|
||||
} // namespace osrm
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
|
||||
#include "contractor/contractor_graph.hpp"
|
||||
#include "util/log.hpp"
|
||||
#include "util/percent.hpp"
|
||||
|
||||
#include <tbb/parallel_sort.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
@@ -13,7 +16,7 @@ namespace contractor
|
||||
|
||||
// Make sure to move in the input edge list!
|
||||
template <typename InputEdgeContainer>
|
||||
std::vector<ContractorEdge> adaptToContractorInput(InputEdgeContainer input_edge_list)
|
||||
ContractorGraph toContractorGraph(NodeID number_of_nodes, InputEdgeContainer input_edge_list)
|
||||
{
|
||||
std::vector<ContractorEdge> edges;
|
||||
edges.reserve(input_edge_list.size() * 2);
|
||||
@@ -52,9 +55,115 @@ std::vector<ContractorEdge> adaptToContractorInput(InputEdgeContainer input_edge
|
||||
false,
|
||||
input_edge.data.backward ? true : false,
|
||||
input_edge.data.forward ? true : false);
|
||||
};
|
||||
tbb::parallel_sort(edges.begin(), edges.end());
|
||||
|
||||
NodeID edge = 0;
|
||||
for (NodeID i = 0; i < edges.size();)
|
||||
{
|
||||
const NodeID source = edges[i].source;
|
||||
const NodeID target = edges[i].target;
|
||||
const NodeID id = edges[i].data.id;
|
||||
// remove eigenloops
|
||||
if (source == target)
|
||||
{
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
ContractorEdge forward_edge;
|
||||
ContractorEdge reverse_edge;
|
||||
forward_edge.source = reverse_edge.source = source;
|
||||
forward_edge.target = reverse_edge.target = target;
|
||||
forward_edge.data.forward = reverse_edge.data.backward = true;
|
||||
forward_edge.data.backward = reverse_edge.data.forward = false;
|
||||
forward_edge.data.shortcut = reverse_edge.data.shortcut = false;
|
||||
forward_edge.data.id = reverse_edge.data.id = id;
|
||||
forward_edge.data.originalEdges = reverse_edge.data.originalEdges = 1;
|
||||
forward_edge.data.weight = reverse_edge.data.weight = INVALID_EDGE_WEIGHT;
|
||||
forward_edge.data.duration = reverse_edge.data.duration = MAXIMAL_EDGE_DURATION;
|
||||
// remove parallel edges
|
||||
while (i < edges.size() && edges[i].source == source && edges[i].target == target)
|
||||
{
|
||||
if (edges[i].data.forward)
|
||||
{
|
||||
forward_edge.data.weight = std::min(edges[i].data.weight, forward_edge.data.weight);
|
||||
forward_edge.data.duration =
|
||||
std::min(edges[i].data.duration, forward_edge.data.duration);
|
||||
}
|
||||
if (edges[i].data.backward)
|
||||
{
|
||||
reverse_edge.data.weight = std::min(edges[i].data.weight, reverse_edge.data.weight);
|
||||
reverse_edge.data.duration =
|
||||
std::min(edges[i].data.duration, reverse_edge.data.duration);
|
||||
}
|
||||
++i;
|
||||
}
|
||||
// merge edges (s,t) and (t,s) into bidirectional edge
|
||||
if (forward_edge.data.weight == reverse_edge.data.weight)
|
||||
{
|
||||
if ((int)forward_edge.data.weight != INVALID_EDGE_WEIGHT)
|
||||
{
|
||||
forward_edge.data.backward = true;
|
||||
edges[edge++] = forward_edge;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // insert seperate edges
|
||||
if (((int)forward_edge.data.weight) != INVALID_EDGE_WEIGHT)
|
||||
{
|
||||
edges[edge++] = forward_edge;
|
||||
}
|
||||
if ((int)reverse_edge.data.weight != INVALID_EDGE_WEIGHT)
|
||||
{
|
||||
edges[edge++] = reverse_edge;
|
||||
}
|
||||
}
|
||||
}
|
||||
// FIXME not sure if we need this
|
||||
edges.shrink_to_fit();
|
||||
util::Log() << "merged " << edges.size() - edge << " edges out of " << edges.size();
|
||||
edges.resize(edge);
|
||||
|
||||
return ContractorGraph{number_of_nodes, edges};
|
||||
}
|
||||
|
||||
template <class Edge, typename GraphT> inline util::DeallocatingVector<Edge> toEdges(GraphT graph)
|
||||
{
|
||||
util::DeallocatingVector<Edge> edges;
|
||||
|
||||
util::UnbufferedLog log;
|
||||
log << "Getting edges of minimized graph ";
|
||||
util::Percent p(log, graph.GetNumberOfNodes());
|
||||
const NodeID number_of_nodes = graph.GetNumberOfNodes();
|
||||
if (graph.GetNumberOfNodes())
|
||||
{
|
||||
Edge new_edge;
|
||||
for (const auto node : util::irange(0u, number_of_nodes))
|
||||
{
|
||||
p.PrintStatus(node);
|
||||
for (auto edge : graph.GetAdjacentEdgeRange(node))
|
||||
{
|
||||
const NodeID target = graph.GetTarget(edge);
|
||||
const ContractorGraph::EdgeData &data = graph.GetEdgeData(edge);
|
||||
new_edge.source = node;
|
||||
new_edge.target = target;
|
||||
BOOST_ASSERT_MSG(SPECIAL_NODEID != new_edge.target, "Target id invalid");
|
||||
new_edge.data.weight = data.weight;
|
||||
new_edge.data.duration = data.duration;
|
||||
new_edge.data.shortcut = data.shortcut;
|
||||
new_edge.data.turn_id = data.id;
|
||||
BOOST_ASSERT_MSG(new_edge.data.turn_id != INT_MAX, // 2^31
|
||||
"edge id invalid");
|
||||
new_edge.data.forward = data.forward;
|
||||
new_edge.data.backward = data.backward;
|
||||
edges.push_back(new_edge);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sort and remove duplicates
|
||||
tbb::parallel_sort(edges.begin(), edges.end());
|
||||
auto new_end = std::unique(edges.begin(), edges.end());
|
||||
edges.resize(new_end - edges.begin());
|
||||
|
||||
return edges;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,17 @@ struct QueryEdge
|
||||
{
|
||||
}
|
||||
|
||||
EdgeData(const NodeID turn_id,
|
||||
const bool shortcut,
|
||||
const EdgeWeight weight,
|
||||
const EdgeWeight duration,
|
||||
const bool forward,
|
||||
const bool backward)
|
||||
: turn_id(turn_id), shortcut(shortcut), weight(weight), duration(duration),
|
||||
forward(forward), backward(backward)
|
||||
{
|
||||
}
|
||||
|
||||
template <class OtherT> EdgeData(const OtherT &other)
|
||||
{
|
||||
weight = other.weight;
|
||||
|
||||
@@ -31,16 +31,33 @@ class CellCustomizer
|
||||
CellCustomizer(const partition::MultiLevelPartition &partition) : partition(partition) {}
|
||||
|
||||
template <typename GraphT>
|
||||
void Customize(
|
||||
const GraphT &graph, Heap &heap, partition::CellStorage &cells, LevelID level, CellID id)
|
||||
void Customize(const GraphT &graph,
|
||||
Heap &heap,
|
||||
const partition::CellStorage &cells,
|
||||
const std::vector<bool> &allowed_nodes,
|
||||
CellMetric &metric,
|
||||
LevelID level,
|
||||
CellID id) const
|
||||
{
|
||||
auto cell = cells.GetCell(level, id);
|
||||
auto cell = cells.GetCell(metric, level, id);
|
||||
auto destinations = cell.GetDestinationNodes();
|
||||
|
||||
// for each source do forward search
|
||||
for (auto source : cell.GetSourceNodes())
|
||||
{
|
||||
std::unordered_set<NodeID> destinations_set(destinations.begin(), destinations.end());
|
||||
if (!allowed_nodes[source])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
std::unordered_set<NodeID> destinations_set;
|
||||
for (const auto destination : destinations)
|
||||
{
|
||||
if (allowed_nodes[destination])
|
||||
{
|
||||
destinations_set.insert(destination);
|
||||
}
|
||||
}
|
||||
heap.Clear();
|
||||
heap.Insert(source, 0, {false, 0});
|
||||
|
||||
@@ -51,10 +68,7 @@ class CellCustomizer
|
||||
const EdgeWeight weight = heap.GetKey(node);
|
||||
const EdgeDuration duration = heap.GetData(node).duration;
|
||||
|
||||
if (level == 1)
|
||||
RelaxNode<true>(graph, cells, heap, level, node, weight, duration);
|
||||
else
|
||||
RelaxNode<false>(graph, cells, heap, level, node, weight, duration);
|
||||
RelaxNode(graph, cells, allowed_nodes, metric, heap, level, node, weight, duration);
|
||||
|
||||
destinations_set.erase(node);
|
||||
}
|
||||
@@ -80,7 +94,11 @@ class CellCustomizer
|
||||
}
|
||||
}
|
||||
|
||||
template <typename GraphT> void Customize(const GraphT &graph, partition::CellStorage &cells)
|
||||
template <typename GraphT>
|
||||
void Customize(const GraphT &graph,
|
||||
const partition::CellStorage &cells,
|
||||
const std::vector<bool> &allowed_nodes,
|
||||
CellMetric &metric) const
|
||||
{
|
||||
Heap heap_exemplar(graph.GetNumberOfNodes());
|
||||
HeapPtr heaps(heap_exemplar);
|
||||
@@ -92,22 +110,26 @@ class CellCustomizer
|
||||
auto &heap = heaps.local();
|
||||
for (auto id = range.begin(), end = range.end(); id != end; ++id)
|
||||
{
|
||||
Customize(graph, heap, cells, level, id);
|
||||
Customize(
|
||||
graph, heap, cells, allowed_nodes, metric, level, id);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
template <bool first_level, typename GraphT>
|
||||
template <typename GraphT>
|
||||
void RelaxNode(const GraphT &graph,
|
||||
const partition::CellStorage &cells,
|
||||
const std::vector<bool> &allowed_nodes,
|
||||
const CellMetric &metric,
|
||||
Heap &heap,
|
||||
LevelID level,
|
||||
NodeID node,
|
||||
EdgeWeight weight,
|
||||
EdgeDuration duration) const
|
||||
{
|
||||
auto first_level = level == 1;
|
||||
BOOST_ASSERT(heap.WasInserted(node));
|
||||
|
||||
if (!first_level)
|
||||
@@ -123,7 +145,7 @@ class CellCustomizer
|
||||
{
|
||||
// Relax sub-cell nodes
|
||||
auto subcell_id = partition.GetCell(level - 1, node);
|
||||
auto subcell = cells.GetCell(level - 1, subcell_id);
|
||||
auto subcell = cells.GetCell(metric, level - 1, subcell_id);
|
||||
auto subcell_destination = subcell.GetDestinationNodes().begin();
|
||||
auto subcell_duration = subcell.GetOutDuration(node).begin();
|
||||
for (auto subcell_weight : subcell.GetOutWeight(node))
|
||||
@@ -131,6 +153,11 @@ class CellCustomizer
|
||||
if (subcell_weight != INVALID_EDGE_WEIGHT)
|
||||
{
|
||||
const NodeID to = *subcell_destination;
|
||||
if (!allowed_nodes[to])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const EdgeWeight to_weight = weight + subcell_weight;
|
||||
if (!heap.WasInserted(to))
|
||||
{
|
||||
@@ -153,6 +180,11 @@ class CellCustomizer
|
||||
for (auto edge : graph.GetInternalEdgeRange(level, node))
|
||||
{
|
||||
const NodeID to = graph.GetTarget(edge);
|
||||
if (!allowed_nodes[to])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto &data = graph.GetEdgeData(edge);
|
||||
if (data.forward &&
|
||||
(first_level ||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
#ifndef OSRM_CUSTOMIZER_CELL_METRIC_HPP
|
||||
#define OSRM_CUSTOMIZER_CELL_METRIC_HPP
|
||||
|
||||
#include "storage/io_fwd.hpp"
|
||||
#include "storage/shared_memory_ownership.hpp"
|
||||
|
||||
#include "util/typedefs.hpp"
|
||||
#include "util/vector_view.hpp"
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace customizer
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
// Encapsulated one metric to make it easily replacable in CelLStorage
|
||||
template <storage::Ownership Ownership> struct CellMetricImpl
|
||||
{
|
||||
template <typename T> using Vector = util::ViewOrVector<T, Ownership>;
|
||||
|
||||
Vector<EdgeWeight> weights;
|
||||
Vector<EdgeDuration> durations;
|
||||
};
|
||||
}
|
||||
|
||||
using CellMetric = detail::CellMetricImpl<storage::Ownership::Container>;
|
||||
using CellMetricView = detail::CellMetricImpl<storage::Ownership::View>;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -17,12 +17,13 @@ namespace customizer
|
||||
struct CustomizationConfig final : storage::IOConfig
|
||||
{
|
||||
CustomizationConfig()
|
||||
: IOConfig(
|
||||
{
|
||||
".osrm",
|
||||
},
|
||||
{},
|
||||
{".osrm.ebg", ".osrm.partition", ".osrm.cells", ".osrm.mldgr"}),
|
||||
: IOConfig({".osrm.ebg",
|
||||
".osrm.partition",
|
||||
".osrm.cells",
|
||||
".osrm.ebg_nodes",
|
||||
".osrm.properties"},
|
||||
{},
|
||||
{".osrm.cell_metrics", ".osrm.mldgr"}),
|
||||
requested_num_threads(0)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
#ifndef OSRM_CUSTOMIZER_FILES_HPP
|
||||
#define OSRM_CUSTOMIZER_FILES_HPP
|
||||
|
||||
#include "customizer/serialization.hpp"
|
||||
|
||||
#include "storage/io.hpp"
|
||||
|
||||
#include "util/integer_range.hpp"
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace customizer
|
||||
{
|
||||
namespace files
|
||||
{
|
||||
|
||||
// reads .osrm.cell_metrics file
|
||||
template <typename CellMetricT>
|
||||
inline void readCellMetrics(const boost::filesystem::path &path, std::vector<CellMetricT> &metrics)
|
||||
{
|
||||
static_assert(std::is_same<CellMetricView, CellMetricT>::value ||
|
||||
std::is_same<CellMetric, CellMetricT>::value,
|
||||
"");
|
||||
|
||||
const auto fingerprint = storage::io::FileReader::VerifyFingerprint;
|
||||
storage::io::FileReader reader{path, fingerprint};
|
||||
|
||||
auto num_metrics = reader.ReadElementCount64();
|
||||
metrics.resize(num_metrics);
|
||||
|
||||
for (auto &metric : metrics)
|
||||
{
|
||||
serialization::read(reader, metric);
|
||||
}
|
||||
}
|
||||
|
||||
// writes .osrm.cell_metrics file
|
||||
template <typename CellMetricT>
|
||||
inline void writeCellMetrics(const boost::filesystem::path &path,
|
||||
const std::vector<CellMetricT> &metrics)
|
||||
{
|
||||
static_assert(std::is_same<CellMetricView, CellMetricT>::value ||
|
||||
std::is_same<CellMetric, CellMetricT>::value,
|
||||
"");
|
||||
|
||||
const auto fingerprint = storage::io::FileWriter::GenerateFingerprint;
|
||||
storage::io::FileWriter writer{path, fingerprint};
|
||||
|
||||
writer.WriteElementCount64(metrics.size());
|
||||
for (const auto &metric : metrics)
|
||||
{
|
||||
serialization::write(writer, metric);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,34 @@
|
||||
#ifndef OSRM_CUSTOMIZER_SERIALIZATION_HPP
|
||||
#define OSRM_CUSTOMIZER_SERIALIZATION_HPP
|
||||
|
||||
#include "partition/cell_storage.hpp"
|
||||
|
||||
#include "storage/io.hpp"
|
||||
#include "storage/serialization.hpp"
|
||||
#include "storage/shared_memory_ownership.hpp"
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace customizer
|
||||
{
|
||||
namespace serialization
|
||||
{
|
||||
|
||||
template <storage::Ownership Ownership>
|
||||
inline void read(storage::io::FileReader &reader, detail::CellMetricImpl<Ownership> &metric)
|
||||
{
|
||||
storage::serialization::read(reader, metric.weights);
|
||||
storage::serialization::read(reader, metric.durations);
|
||||
}
|
||||
|
||||
template <storage::Ownership Ownership>
|
||||
inline void write(storage::io::FileWriter &writer, const detail::CellMetricImpl<Ownership> &metric)
|
||||
{
|
||||
storage::serialization::write(writer, metric.weights);
|
||||
storage::serialization::write(writer, metric.durations);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -56,6 +56,9 @@ template <typename AlgorithmT> struct HasManyToManySearch final : std::false_typ
|
||||
template <typename AlgorithmT> struct HasGetTileTurns final : std::false_type
|
||||
{
|
||||
};
|
||||
template <typename AlgorithmT> struct HasExcludeFlags final : std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
// Algorithms supported by Contraction Hierarchies
|
||||
template <> struct HasAlternativePathSearch<ch::Algorithm> final : std::true_type
|
||||
@@ -76,6 +79,9 @@ template <> struct HasManyToManySearch<ch::Algorithm> final : std::true_type
|
||||
template <> struct HasGetTileTurns<ch::Algorithm> final : std::true_type
|
||||
{
|
||||
};
|
||||
template <> struct HasExcludeFlags<ch::Algorithm> final : std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
// Algorithms supported by Contraction Hierarchies with core
|
||||
// the rest is disabled because of performance reasons
|
||||
@@ -91,6 +97,9 @@ template <> struct HasMapMatching<corech::Algorithm> final : std::true_type
|
||||
template <> struct HasGetTileTurns<corech::Algorithm> final : std::true_type
|
||||
{
|
||||
};
|
||||
template <> struct HasExcludeFlags<corech::Algorithm> final : std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
// Algorithms supported by Multi-Level Dijkstra
|
||||
template <> struct HasAlternativePathSearch<mld::Algorithm> final : std::true_type
|
||||
@@ -111,6 +120,9 @@ template <> struct HasManyToManySearch<mld::Algorithm> final : std::true_type
|
||||
template <> struct HasGetTileTurns<mld::Algorithm> final : std::true_type
|
||||
{
|
||||
};
|
||||
template <> struct HasExcludeFlags<mld::Algorithm> final : std::true_type
|
||||
{
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +68,7 @@ struct BaseParameters
|
||||
std::vector<boost::optional<double>> radiuses;
|
||||
std::vector<boost::optional<Bearing>> bearings;
|
||||
std::vector<boost::optional<Approach>> approaches;
|
||||
std::vector<std::string> exclude;
|
||||
|
||||
// Adds hints to response which can be included in subsequent requests, see `hints` above.
|
||||
bool generate_hints = true;
|
||||
@@ -77,9 +78,10 @@ struct BaseParameters
|
||||
std::vector<boost::optional<double>> radiuses_ = {},
|
||||
std::vector<boost::optional<Bearing>> bearings_ = {},
|
||||
std::vector<boost::optional<Approach>> approaches_ = {},
|
||||
bool generate_hints_ = true)
|
||||
bool generate_hints_ = true,
|
||||
std::vector<std::string> exclude = {})
|
||||
: coordinates(coordinates_), hints(hints_), radiuses(radiuses_), bearings(bearings_),
|
||||
approaches(approaches_), generate_hints(generate_hints_)
|
||||
approaches(approaches_), exclude(std::move(exclude)), generate_hints(generate_hints_)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,15 @@ namespace detail
|
||||
std::string instructionTypeToString(extractor::guidance::TurnType::Enum type);
|
||||
std::string instructionModifierToString(extractor::guidance::DirectionModifier::Enum modifier);
|
||||
|
||||
/**
|
||||
* Returns a string representing all instruction types (including internal types that
|
||||
* are normally not exposed in route responses)
|
||||
*
|
||||
* @param type the TurnType value to convert into a string
|
||||
* @return a string representing the turn type (e.g. `turn` or `continue`)
|
||||
*/
|
||||
std::string internalInstructionTypeToString(extractor::guidance::TurnType::Enum type);
|
||||
|
||||
util::json::Array coordinateToLonLat(const util::Coordinate coordinate);
|
||||
|
||||
std::string modeToString(const extractor::TravelMode mode);
|
||||
|
||||
@@ -88,11 +88,17 @@ struct RouteParameters : public BaseParameters
|
||||
const OverviewType overview_,
|
||||
const boost::optional<bool> continue_straight_,
|
||||
Args... args_)
|
||||
: BaseParameters{std::forward<Args>(args_)...}, steps{steps_}, alternatives{alternatives_},
|
||||
number_of_alternatives{alternatives_ ? 1u : 0u}, annotations{false},
|
||||
annotations_type{AnnotationsType::None}, geometries{geometries_}, overview{overview_},
|
||||
// Once we perfectly-forward `args` (see #2990) this constructor can delegate to the one
|
||||
// below.
|
||||
: BaseParameters{std::forward<Args>(args_)...},
|
||||
steps{steps_},
|
||||
alternatives{alternatives_},
|
||||
number_of_alternatives{alternatives_ ? 1u : 0u},
|
||||
annotations{false},
|
||||
annotations_type{AnnotationsType::None},
|
||||
geometries{geometries_},
|
||||
overview{overview_},
|
||||
continue_straight{continue_straight_}
|
||||
// Once we perfectly-forward `args` (see #2990) this constructor can delegate to the one below.
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "engine/datafacade/contiguous_internalmem_datafacade.hpp"
|
||||
#include "engine/datafacade/shared_memory_allocator.hpp"
|
||||
#include "engine/datafacade_factory.hpp"
|
||||
|
||||
#include "storage/shared_datatype.hpp"
|
||||
#include "storage/shared_memory.hpp"
|
||||
@@ -21,37 +22,50 @@ namespace osrm
|
||||
namespace engine
|
||||
{
|
||||
|
||||
// This class monitors the shared memory region that contains the pointers to
|
||||
// the data and layout regions that should be used. This region is updated
|
||||
// once a new dataset arrives.
|
||||
template <typename AlgorithmT> class DataWatchdog final
|
||||
namespace detail
|
||||
{
|
||||
// We need this wrapper type since template-template specilization of FacadeT is broken on clang
|
||||
// when it is combined with an templated alias (DataFacade in this case).
|
||||
// See https://godbolt.org/g/ZS6Xmt for an example.
|
||||
template <typename AlgorithmT, typename FacadeT> class DataWatchdogImpl;
|
||||
|
||||
template <typename AlgorithmT>
|
||||
class DataWatchdogImpl<AlgorithmT, datafacade::ContiguousInternalMemoryDataFacade<AlgorithmT>> final
|
||||
{
|
||||
using mutex_type = typename storage::SharedMonitor<storage::SharedDataTimestamp>::mutex_type;
|
||||
using FacadeT = datafacade::ContiguousInternalMemoryDataFacade<AlgorithmT>;
|
||||
using Facade = datafacade::ContiguousInternalMemoryDataFacade<AlgorithmT>;
|
||||
|
||||
public:
|
||||
DataWatchdog() : active(true), timestamp(0)
|
||||
DataWatchdogImpl() : active(true), timestamp(0)
|
||||
{
|
||||
// create the initial facade before launching the watchdog thread
|
||||
{
|
||||
boost::interprocess::scoped_lock<mutex_type> current_region_lock(barrier.get_mutex());
|
||||
|
||||
facade = std::make_shared<const FacadeT>(
|
||||
std::make_unique<datafacade::SharedMemoryAllocator>(barrier.data().region));
|
||||
facade_factory =
|
||||
DataFacadeFactory<datafacade::ContiguousInternalMemoryDataFacade, AlgorithmT>(
|
||||
std::make_shared<datafacade::SharedMemoryAllocator>(barrier.data().region));
|
||||
timestamp = barrier.data().timestamp;
|
||||
}
|
||||
|
||||
watcher = std::thread(&DataWatchdog::Run, this);
|
||||
watcher = std::thread(&DataWatchdogImpl::Run, this);
|
||||
}
|
||||
|
||||
~DataWatchdog()
|
||||
~DataWatchdogImpl()
|
||||
{
|
||||
active = false;
|
||||
barrier.notify_all();
|
||||
watcher.join();
|
||||
}
|
||||
|
||||
std::shared_ptr<const FacadeT> Get() const { return facade; }
|
||||
std::shared_ptr<const Facade> Get(const api::BaseParameters ¶ms) const
|
||||
{
|
||||
return facade_factory.Get(params);
|
||||
}
|
||||
std::shared_ptr<const Facade> Get(const api::TileParameters ¶ms) const
|
||||
{
|
||||
return facade_factory.Get(params);
|
||||
}
|
||||
|
||||
private:
|
||||
void Run()
|
||||
@@ -68,8 +82,9 @@ template <typename AlgorithmT> class DataWatchdog final
|
||||
if (timestamp != barrier.data().timestamp)
|
||||
{
|
||||
auto region = barrier.data().region;
|
||||
facade = std::make_shared<const FacadeT>(
|
||||
std::make_unique<datafacade::SharedMemoryAllocator>(region));
|
||||
facade_factory =
|
||||
DataFacadeFactory<datafacade::ContiguousInternalMemoryDataFacade, AlgorithmT>(
|
||||
std::make_shared<datafacade::SharedMemoryAllocator>(region));
|
||||
timestamp = barrier.data().timestamp;
|
||||
util::Log() << "updated facade to region " << region << " with timestamp "
|
||||
<< timestamp;
|
||||
@@ -83,9 +98,16 @@ template <typename AlgorithmT> class DataWatchdog final
|
||||
std::thread watcher;
|
||||
bool active;
|
||||
unsigned timestamp;
|
||||
std::shared_ptr<const FacadeT> facade;
|
||||
DataFacadeFactory<datafacade::ContiguousInternalMemoryDataFacade, AlgorithmT> facade_factory;
|
||||
};
|
||||
}
|
||||
|
||||
// This class monitors the shared memory region that contains the pointers to
|
||||
// the data and layout regions that should be used. This region is updated
|
||||
// once a new dataset arrives.
|
||||
template <typename AlgorithmT, template <typename A> class FacadeT>
|
||||
using DataWatchdog = detail::DataWatchdogImpl<AlgorithmT, FacadeT<AlgorithmT>>;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "partition/cell_storage.hpp"
|
||||
#include "partition/multi_level_partition.hpp"
|
||||
|
||||
#include "util/filtered_graph.hpp"
|
||||
#include "util/integer_range.hpp"
|
||||
|
||||
namespace osrm
|
||||
@@ -22,14 +23,13 @@ using CH = routing_algorithms::ch::Algorithm;
|
||||
using CoreCH = routing_algorithms::corech::Algorithm;
|
||||
using MLD = routing_algorithms::mld::Algorithm;
|
||||
|
||||
using EdgeRange = util::range<EdgeID>;
|
||||
|
||||
template <typename AlgorithmT> class AlgorithmDataFacade;
|
||||
|
||||
template <> class AlgorithmDataFacade<CH>
|
||||
{
|
||||
public:
|
||||
using EdgeData = contractor::QueryEdge::EdgeData;
|
||||
using EdgeRange = util::filtered_range<EdgeID, util::vector_view<bool>>;
|
||||
|
||||
// search graph access
|
||||
virtual unsigned GetNumberOfNodes() const = 0;
|
||||
@@ -42,10 +42,6 @@ template <> class AlgorithmDataFacade<CH>
|
||||
|
||||
virtual const EdgeData &GetEdgeData(const EdgeID e) const = 0;
|
||||
|
||||
virtual EdgeID BeginEdges(const NodeID n) const = 0;
|
||||
|
||||
virtual EdgeID EndEdges(const NodeID n) const = 0;
|
||||
|
||||
virtual EdgeRange GetAdjacentEdgeRange(const NodeID node) const = 0;
|
||||
|
||||
// searches for a specific edge
|
||||
@@ -73,6 +69,7 @@ template <> class AlgorithmDataFacade<MLD>
|
||||
{
|
||||
public:
|
||||
using EdgeData = extractor::EdgeBasedEdge::EdgeData;
|
||||
using EdgeRange = util::range<EdgeID>;
|
||||
|
||||
// search graph access
|
||||
virtual unsigned GetNumberOfNodes() const = 0;
|
||||
@@ -85,16 +82,14 @@ template <> class AlgorithmDataFacade<MLD>
|
||||
|
||||
virtual const EdgeData &GetEdgeData(const EdgeID e) const = 0;
|
||||
|
||||
virtual EdgeID BeginEdges(const NodeID n) const = 0;
|
||||
|
||||
virtual EdgeID EndEdges(const NodeID n) const = 0;
|
||||
|
||||
virtual EdgeRange GetAdjacentEdgeRange(const NodeID node) const = 0;
|
||||
|
||||
virtual const partition::MultiLevelPartitionView &GetMultiLevelPartition() const = 0;
|
||||
|
||||
virtual const partition::CellStorageView &GetCellStorage() const = 0;
|
||||
|
||||
virtual const customizer::CellMetricView &GetCellMetric() const = 0;
|
||||
|
||||
virtual EdgeRange GetBorderEdgeRange(const LevelID level, const NodeID node) const = 0;
|
||||
|
||||
// searches for a specific edge
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
|
||||
#include "util/exception.hpp"
|
||||
#include "util/exception_utils.hpp"
|
||||
#include "util/filtered_graph.hpp"
|
||||
#include "util/guidance/bearing_class.hpp"
|
||||
#include "util/guidance/entry_class.hpp"
|
||||
#include "util/guidance/turn_bearing.hpp"
|
||||
@@ -68,7 +69,7 @@ template <>
|
||||
class ContiguousInternalMemoryAlgorithmDataFacade<CH> : public datafacade::AlgorithmDataFacade<CH>
|
||||
{
|
||||
private:
|
||||
using QueryGraph = contractor::QueryGraphView;
|
||||
using QueryGraph = util::FilteredGraphView<contractor::QueryGraphView>;
|
||||
using GraphNode = QueryGraph::NodeArrayEntry;
|
||||
using GraphEdge = QueryGraph::EdgeArrayEntry;
|
||||
|
||||
@@ -77,7 +78,9 @@ class ContiguousInternalMemoryAlgorithmDataFacade<CH> : public datafacade::Algor
|
||||
// allocator that keeps the allocation data
|
||||
std::shared_ptr<ContiguousBlockAllocator> allocator;
|
||||
|
||||
void InitializeGraphPointer(storage::DataLayout &data_layout, char *memory_block)
|
||||
void InitializeGraphPointer(storage::DataLayout &data_layout,
|
||||
char *memory_block,
|
||||
const std::size_t exclude_index)
|
||||
{
|
||||
auto graph_nodes_ptr = data_layout.GetBlockPtr<GraphNode>(
|
||||
memory_block, storage::DataLayout::CH_GRAPH_NODE_LIST);
|
||||
@@ -85,24 +88,34 @@ class ContiguousInternalMemoryAlgorithmDataFacade<CH> : public datafacade::Algor
|
||||
auto graph_edges_ptr = data_layout.GetBlockPtr<GraphEdge>(
|
||||
memory_block, storage::DataLayout::CH_GRAPH_EDGE_LIST);
|
||||
|
||||
auto filter_block_id = static_cast<storage::DataLayout::BlockID>(
|
||||
storage::DataLayout::CH_EDGE_FILTER_0 + exclude_index);
|
||||
|
||||
auto edge_filter_ptr = data_layout.GetBlockPtr<unsigned>(memory_block, filter_block_id);
|
||||
|
||||
util::vector_view<GraphNode> node_list(
|
||||
graph_nodes_ptr, data_layout.num_entries[storage::DataLayout::CH_GRAPH_NODE_LIST]);
|
||||
util::vector_view<GraphEdge> edge_list(
|
||||
graph_edges_ptr, data_layout.num_entries[storage::DataLayout::CH_GRAPH_EDGE_LIST]);
|
||||
m_query_graph = QueryGraph(node_list, edge_list);
|
||||
|
||||
util::vector_view<bool> edge_filter(edge_filter_ptr,
|
||||
data_layout.num_entries[filter_block_id]);
|
||||
m_query_graph = QueryGraph({node_list, edge_list}, edge_filter);
|
||||
}
|
||||
|
||||
public:
|
||||
ContiguousInternalMemoryAlgorithmDataFacade(
|
||||
std::shared_ptr<ContiguousBlockAllocator> allocator_)
|
||||
std::shared_ptr<ContiguousBlockAllocator> allocator_, std::size_t exclude_index)
|
||||
: allocator(std::move(allocator_))
|
||||
{
|
||||
InitializeInternalPointers(allocator->GetLayout(), allocator->GetMemory());
|
||||
InitializeInternalPointers(allocator->GetLayout(), allocator->GetMemory(), exclude_index);
|
||||
}
|
||||
|
||||
void InitializeInternalPointers(storage::DataLayout &data_layout, char *memory_block)
|
||||
void InitializeInternalPointers(storage::DataLayout &data_layout,
|
||||
char *memory_block,
|
||||
const std::size_t exclude_index)
|
||||
{
|
||||
InitializeGraphPointer(data_layout, memory_block);
|
||||
InitializeGraphPointer(data_layout, memory_block, exclude_index);
|
||||
}
|
||||
|
||||
// search graph access
|
||||
@@ -122,10 +135,6 @@ class ContiguousInternalMemoryAlgorithmDataFacade<CH> : public datafacade::Algor
|
||||
return m_query_graph.GetEdgeData(e);
|
||||
}
|
||||
|
||||
EdgeID BeginEdges(const NodeID n) const override final { return m_query_graph.BeginEdges(n); }
|
||||
|
||||
EdgeID EndEdges(const NodeID n) const override final { return m_query_graph.EndEdges(n); }
|
||||
|
||||
EdgeRange GetAdjacentEdgeRange(const NodeID node) const override final
|
||||
{
|
||||
return m_query_graph.GetAdjacentEdgeRange(node);
|
||||
@@ -166,32 +175,37 @@ class ContiguousInternalMemoryAlgorithmDataFacade<CoreCH>
|
||||
// allocator that keeps the allocation data
|
||||
std::shared_ptr<ContiguousBlockAllocator> allocator;
|
||||
|
||||
void InitializeCoreInformationPointer(storage::DataLayout &data_layout, char *memory_block)
|
||||
void InitializeCoreInformationPointer(storage::DataLayout &data_layout,
|
||||
char *memory_block,
|
||||
const std::size_t exclude_index)
|
||||
{
|
||||
auto core_marker_ptr =
|
||||
data_layout.GetBlockPtr<unsigned>(memory_block, storage::DataLayout::CH_CORE_MARKER);
|
||||
util::vector_view<bool> is_core_node(
|
||||
core_marker_ptr, data_layout.num_entries[storage::DataLayout::CH_CORE_MARKER]);
|
||||
auto core_block_id = static_cast<storage::DataLayout::BlockID>(
|
||||
storage::DataLayout::CH_CORE_MARKER_0 + exclude_index);
|
||||
auto core_marker_ptr = data_layout.GetBlockPtr<unsigned>(memory_block, core_block_id);
|
||||
util::vector_view<bool> is_core_node(core_marker_ptr,
|
||||
data_layout.num_entries[core_block_id]);
|
||||
m_is_core_node = std::move(is_core_node);
|
||||
}
|
||||
|
||||
public:
|
||||
ContiguousInternalMemoryAlgorithmDataFacade(
|
||||
std::shared_ptr<ContiguousBlockAllocator> allocator_)
|
||||
std::shared_ptr<ContiguousBlockAllocator> allocator_, const std::size_t exclude_index)
|
||||
: allocator(std::move(allocator_))
|
||||
{
|
||||
InitializeInternalPointers(allocator->GetLayout(), allocator->GetMemory());
|
||||
InitializeInternalPointers(allocator->GetLayout(), allocator->GetMemory(), exclude_index);
|
||||
}
|
||||
|
||||
void InitializeInternalPointers(storage::DataLayout &data_layout, char *memory_block)
|
||||
void InitializeInternalPointers(storage::DataLayout &data_layout,
|
||||
char *memory_block,
|
||||
const std::size_t exclude_index)
|
||||
{
|
||||
InitializeCoreInformationPointer(data_layout, memory_block);
|
||||
InitializeCoreInformationPointer(data_layout, memory_block, exclude_index);
|
||||
}
|
||||
|
||||
bool IsCoreNode(const NodeID id) const override final
|
||||
{
|
||||
BOOST_ASSERT(id < m_is_core_node.size());
|
||||
return m_is_core_node[id];
|
||||
BOOST_ASSERT(m_is_core_node.empty() || id < m_is_core_node.size());
|
||||
return !m_is_core_node.empty() || m_is_core_node[id];
|
||||
}
|
||||
};
|
||||
|
||||
@@ -212,6 +226,7 @@ class ContiguousInternalMemoryDataFacadeBase : public BaseDataFacade
|
||||
using SharedGeospatialQuery = GeospatialQuery<SharedRTree, BaseDataFacade>;
|
||||
using RTreeNode = SharedRTree::TreeNode;
|
||||
|
||||
extractor::ClassData exclude_mask;
|
||||
std::string m_timestamp;
|
||||
extractor::ProfileProperties *m_profile_properties;
|
||||
extractor::Datasources *m_datasources;
|
||||
@@ -246,10 +261,14 @@ class ContiguousInternalMemoryDataFacadeBase : public BaseDataFacade
|
||||
// allocator that keeps the allocation data
|
||||
std::shared_ptr<ContiguousBlockAllocator> allocator;
|
||||
|
||||
void InitializeProfilePropertiesPointer(storage::DataLayout &data_layout, char *memory_block)
|
||||
void InitializeProfilePropertiesPointer(storage::DataLayout &data_layout,
|
||||
char *memory_block,
|
||||
const std::size_t exclude_index)
|
||||
{
|
||||
m_profile_properties = data_layout.GetBlockPtr<extractor::ProfileProperties>(
|
||||
memory_block, storage::DataLayout::PROPERTIES);
|
||||
|
||||
exclude_mask = m_profile_properties->excludable_classes[exclude_index];
|
||||
}
|
||||
|
||||
void InitializeTimestampPointer(storage::DataLayout &data_layout, char *memory_block)
|
||||
@@ -539,7 +558,9 @@ class ContiguousInternalMemoryDataFacadeBase : public BaseDataFacade
|
||||
m_entry_class_table = std::move(entry_class_table);
|
||||
}
|
||||
|
||||
void InitializeInternalPointers(storage::DataLayout &data_layout, char *memory_block)
|
||||
void InitializeInternalPointers(storage::DataLayout &data_layout,
|
||||
char *memory_block,
|
||||
const std::size_t exclude_index)
|
||||
{
|
||||
InitializeChecksumPointer(data_layout, memory_block);
|
||||
InitializeNodeInformationPointers(data_layout, memory_block);
|
||||
@@ -550,7 +571,7 @@ class ContiguousInternalMemoryDataFacadeBase : public BaseDataFacade
|
||||
InitializeTimestampPointer(data_layout, memory_block);
|
||||
InitializeNamePointers(data_layout, memory_block);
|
||||
InitializeTurnLaneDescriptionsPointers(data_layout, memory_block);
|
||||
InitializeProfilePropertiesPointer(data_layout, memory_block);
|
||||
InitializeProfilePropertiesPointer(data_layout, memory_block, exclude_index);
|
||||
InitializeRTreePointers(data_layout, memory_block);
|
||||
InitializeIntersectionClassPointers(data_layout, memory_block);
|
||||
}
|
||||
@@ -558,10 +579,11 @@ class ContiguousInternalMemoryDataFacadeBase : public BaseDataFacade
|
||||
public:
|
||||
// allows switching between process_memory/shared_memory datafacade, based on the type of
|
||||
// allocator
|
||||
ContiguousInternalMemoryDataFacadeBase(std::shared_ptr<ContiguousBlockAllocator> allocator_)
|
||||
ContiguousInternalMemoryDataFacadeBase(std::shared_ptr<ContiguousBlockAllocator> allocator_,
|
||||
const std::size_t exclude_index)
|
||||
: allocator(std::move(allocator_))
|
||||
{
|
||||
InitializeInternalPointers(allocator->GetLayout(), allocator->GetMemory());
|
||||
InitializeInternalPointers(allocator->GetLayout(), allocator->GetMemory(), exclude_index);
|
||||
}
|
||||
|
||||
// node and edge information access
|
||||
@@ -802,6 +824,11 @@ class ContiguousInternalMemoryDataFacadeBase : public BaseDataFacade
|
||||
return edge_based_node_data.GetClassData(id);
|
||||
}
|
||||
|
||||
bool ExcludeNode(const NodeID id) const override final
|
||||
{
|
||||
return (edge_based_node_data.GetClassData(id) & exclude_mask) > 0;
|
||||
}
|
||||
|
||||
std::vector<std::string> GetClasses(const extractor::ClassData class_data) const override final
|
||||
{
|
||||
auto indexes = extractor::getClassIndexes(class_data);
|
||||
@@ -926,9 +953,10 @@ class ContiguousInternalMemoryDataFacade<CH>
|
||||
public ContiguousInternalMemoryAlgorithmDataFacade<CH>
|
||||
{
|
||||
public:
|
||||
ContiguousInternalMemoryDataFacade(std::shared_ptr<ContiguousBlockAllocator> allocator)
|
||||
: ContiguousInternalMemoryDataFacadeBase(allocator),
|
||||
ContiguousInternalMemoryAlgorithmDataFacade<CH>(allocator)
|
||||
ContiguousInternalMemoryDataFacade(std::shared_ptr<ContiguousBlockAllocator> allocator,
|
||||
const std::size_t exclude_index)
|
||||
: ContiguousInternalMemoryDataFacadeBase(allocator, exclude_index),
|
||||
ContiguousInternalMemoryAlgorithmDataFacade<CH>(allocator, exclude_index)
|
||||
|
||||
{
|
||||
}
|
||||
@@ -940,9 +968,10 @@ class ContiguousInternalMemoryDataFacade<CoreCH> final
|
||||
public ContiguousInternalMemoryAlgorithmDataFacade<CoreCH>
|
||||
{
|
||||
public:
|
||||
ContiguousInternalMemoryDataFacade(std::shared_ptr<ContiguousBlockAllocator> allocator)
|
||||
: ContiguousInternalMemoryDataFacade<CH>(allocator),
|
||||
ContiguousInternalMemoryAlgorithmDataFacade<CoreCH>(allocator)
|
||||
ContiguousInternalMemoryDataFacade(std::shared_ptr<ContiguousBlockAllocator> allocator,
|
||||
const std::size_t exclude_index)
|
||||
: ContiguousInternalMemoryDataFacade<CH>(allocator, exclude_index),
|
||||
ContiguousInternalMemoryAlgorithmDataFacade<CoreCH>(allocator, exclude_index)
|
||||
|
||||
{
|
||||
}
|
||||
@@ -953,19 +982,24 @@ template <> class ContiguousInternalMemoryAlgorithmDataFacade<MLD> : public Algo
|
||||
// MLD data
|
||||
partition::MultiLevelPartitionView mld_partition;
|
||||
partition::CellStorageView mld_cell_storage;
|
||||
customizer::CellMetricView mld_cell_metric;
|
||||
using QueryGraph = customizer::MultiLevelEdgeBasedGraphView;
|
||||
using GraphNode = QueryGraph::NodeArrayEntry;
|
||||
using GraphEdge = QueryGraph::EdgeArrayEntry;
|
||||
|
||||
QueryGraph query_graph;
|
||||
|
||||
void InitializeInternalPointers(storage::DataLayout &data_layout, char *memory_block)
|
||||
void InitializeInternalPointers(storage::DataLayout &data_layout,
|
||||
char *memory_block,
|
||||
const std::size_t exclude_index)
|
||||
{
|
||||
InitializeMLDDataPointers(data_layout, memory_block);
|
||||
InitializeMLDDataPointers(data_layout, memory_block, exclude_index);
|
||||
InitializeGraphPointer(data_layout, memory_block);
|
||||
}
|
||||
|
||||
void InitializeMLDDataPointers(storage::DataLayout &data_layout, char *memory_block)
|
||||
void InitializeMLDDataPointers(storage::DataLayout &data_layout,
|
||||
char *memory_block,
|
||||
const std::size_t exclude_index)
|
||||
{
|
||||
if (data_layout.GetBlockSize(storage::DataLayout::MLD_PARTITION) > 0)
|
||||
{
|
||||
@@ -992,15 +1026,32 @@ template <> class ContiguousInternalMemoryAlgorithmDataFacade<MLD> : public Algo
|
||||
partition::MultiLevelPartitionView{level_data, partition, cell_to_children};
|
||||
}
|
||||
|
||||
if (data_layout.GetBlockSize(storage::DataLayout::MLD_CELL_WEIGHTS) > 0)
|
||||
{
|
||||
BOOST_ASSERT(data_layout.GetBlockSize(storage::DataLayout::MLD_CELLS) > 0);
|
||||
BOOST_ASSERT(data_layout.GetBlockSize(storage::DataLayout::MLD_CELL_LEVEL_OFFSETS) > 0);
|
||||
const auto weights_block_id = static_cast<storage::DataLayout::BlockID>(
|
||||
storage::DataLayout::MLD_CELL_WEIGHTS_0 + exclude_index);
|
||||
const auto durations_block_id = static_cast<storage::DataLayout::BlockID>(
|
||||
storage::DataLayout::MLD_CELL_DURATIONS_0 + exclude_index);
|
||||
|
||||
if (data_layout.GetBlockSize(weights_block_id) > 0)
|
||||
{
|
||||
auto mld_cell_weights_ptr =
|
||||
data_layout.GetBlockPtr<EdgeWeight>(memory_block, weights_block_id);
|
||||
auto mld_cell_durations_ptr =
|
||||
data_layout.GetBlockPtr<EdgeDuration>(memory_block, durations_block_id);
|
||||
auto weight_entries_count =
|
||||
data_layout.GetBlockEntries(storage::DataLayout::MLD_CELL_WEIGHTS_0);
|
||||
auto duration_entries_count =
|
||||
data_layout.GetBlockEntries(storage::DataLayout::MLD_CELL_DURATIONS_0);
|
||||
BOOST_ASSERT(weight_entries_count == duration_entries_count);
|
||||
util::vector_view<EdgeWeight> weights(mld_cell_weights_ptr, weight_entries_count);
|
||||
util::vector_view<EdgeDuration> durations(mld_cell_durations_ptr,
|
||||
duration_entries_count);
|
||||
|
||||
mld_cell_metric = customizer::CellMetricView{std::move(weights), std::move(durations)};
|
||||
}
|
||||
|
||||
if (data_layout.GetBlockSize(storage::DataLayout::MLD_CELLS) > 0)
|
||||
{
|
||||
|
||||
auto mld_cell_weights_ptr = data_layout.GetBlockPtr<EdgeWeight>(
|
||||
memory_block, storage::DataLayout::MLD_CELL_WEIGHTS);
|
||||
auto mld_cell_durations_ptr = data_layout.GetBlockPtr<EdgeDuration>(
|
||||
memory_block, storage::DataLayout::MLD_CELL_DURATIONS);
|
||||
auto mld_source_boundary_ptr = data_layout.GetBlockPtr<NodeID>(
|
||||
memory_block, storage::DataLayout::MLD_CELL_SOURCE_BOUNDARY);
|
||||
auto mld_destination_boundary_ptr = data_layout.GetBlockPtr<NodeID>(
|
||||
@@ -1010,10 +1061,6 @@ template <> class ContiguousInternalMemoryAlgorithmDataFacade<MLD> : public Algo
|
||||
auto mld_cell_level_offsets_ptr = data_layout.GetBlockPtr<std::uint64_t>(
|
||||
memory_block, storage::DataLayout::MLD_CELL_LEVEL_OFFSETS);
|
||||
|
||||
auto weight_entries_count =
|
||||
data_layout.GetBlockEntries(storage::DataLayout::MLD_CELL_WEIGHTS);
|
||||
auto duration_entries_count =
|
||||
data_layout.GetBlockEntries(storage::DataLayout::MLD_CELL_DURATIONS);
|
||||
auto source_boundary_entries_count =
|
||||
data_layout.GetBlockEntries(storage::DataLayout::MLD_CELL_SOURCE_BOUNDARY);
|
||||
auto destination_boundary_entries_count =
|
||||
@@ -1022,11 +1069,6 @@ template <> class ContiguousInternalMemoryAlgorithmDataFacade<MLD> : public Algo
|
||||
auto cell_level_offsets_entries_count =
|
||||
data_layout.GetBlockEntries(storage::DataLayout::MLD_CELL_LEVEL_OFFSETS);
|
||||
|
||||
BOOST_ASSERT(weight_entries_count == duration_entries_count);
|
||||
|
||||
util::vector_view<EdgeWeight> weights(mld_cell_weights_ptr, weight_entries_count);
|
||||
util::vector_view<EdgeDuration> durations(mld_cell_durations_ptr,
|
||||
duration_entries_count);
|
||||
util::vector_view<NodeID> source_boundary(mld_source_boundary_ptr,
|
||||
source_boundary_entries_count);
|
||||
util::vector_view<NodeID> destination_boundary(mld_destination_boundary_ptr,
|
||||
@@ -1036,9 +1078,7 @@ template <> class ContiguousInternalMemoryAlgorithmDataFacade<MLD> : public Algo
|
||||
util::vector_view<std::uint64_t> level_offsets(mld_cell_level_offsets_ptr,
|
||||
cell_level_offsets_entries_count);
|
||||
|
||||
mld_cell_storage = partition::CellStorageView{std::move(weights),
|
||||
std::move(durations),
|
||||
std::move(source_boundary),
|
||||
mld_cell_storage = partition::CellStorageView{std::move(source_boundary),
|
||||
std::move(destination_boundary),
|
||||
std::move(cells),
|
||||
std::move(level_offsets)};
|
||||
@@ -1072,10 +1112,10 @@ template <> class ContiguousInternalMemoryAlgorithmDataFacade<MLD> : public Algo
|
||||
|
||||
public:
|
||||
ContiguousInternalMemoryAlgorithmDataFacade(
|
||||
std::shared_ptr<ContiguousBlockAllocator> allocator_)
|
||||
std::shared_ptr<ContiguousBlockAllocator> allocator_, const std::size_t exclude_index)
|
||||
: allocator(std::move(allocator_))
|
||||
{
|
||||
InitializeInternalPointers(allocator->GetLayout(), allocator->GetMemory());
|
||||
InitializeInternalPointers(allocator->GetLayout(), allocator->GetMemory(), exclude_index);
|
||||
}
|
||||
|
||||
const partition::MultiLevelPartitionView &GetMultiLevelPartition() const override
|
||||
@@ -1085,6 +1125,8 @@ template <> class ContiguousInternalMemoryAlgorithmDataFacade<MLD> : public Algo
|
||||
|
||||
const partition::CellStorageView &GetCellStorage() const override { return mld_cell_storage; }
|
||||
|
||||
const customizer::CellMetricView &GetCellMetric() const override { return mld_cell_metric; }
|
||||
|
||||
// search graph access
|
||||
unsigned GetNumberOfNodes() const override final { return query_graph.GetNumberOfNodes(); }
|
||||
|
||||
@@ -1102,10 +1144,6 @@ template <> class ContiguousInternalMemoryAlgorithmDataFacade<MLD> : public Algo
|
||||
return query_graph.GetEdgeData(e);
|
||||
}
|
||||
|
||||
EdgeID BeginEdges(const NodeID n) const override final { return query_graph.BeginEdges(n); }
|
||||
|
||||
EdgeID EndEdges(const NodeID n) const override final { return query_graph.EndEdges(n); }
|
||||
|
||||
EdgeRange GetAdjacentEdgeRange(const NodeID node) const override final
|
||||
{
|
||||
return query_graph.GetAdjacentEdgeRange(node);
|
||||
@@ -1130,9 +1168,10 @@ class ContiguousInternalMemoryDataFacade<MLD> final
|
||||
{
|
||||
private:
|
||||
public:
|
||||
ContiguousInternalMemoryDataFacade(std::shared_ptr<ContiguousBlockAllocator> allocator)
|
||||
: ContiguousInternalMemoryDataFacadeBase(allocator),
|
||||
ContiguousInternalMemoryAlgorithmDataFacade<MLD>(allocator)
|
||||
ContiguousInternalMemoryDataFacade(std::shared_ptr<ContiguousBlockAllocator> allocator,
|
||||
const std::size_t exclude_index)
|
||||
: ContiguousInternalMemoryDataFacadeBase(allocator, exclude_index),
|
||||
ContiguousInternalMemoryAlgorithmDataFacade<MLD>(allocator, exclude_index)
|
||||
|
||||
{
|
||||
}
|
||||
|
||||
@@ -94,6 +94,8 @@ class BaseDataFacade
|
||||
|
||||
virtual extractor::ClassData GetClassData(const NodeID id) const = 0;
|
||||
|
||||
virtual bool ExcludeNode(const NodeID id) const = 0;
|
||||
|
||||
virtual std::vector<std::string> GetClasses(const extractor::ClassData class_data) const = 0;
|
||||
|
||||
virtual std::vector<RTreeLeaf> GetEdgesInBox(const util::Coordinate south_west,
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
#ifndef OSRM_ENGINE_DATAFACADE_FACTORY_HPP
|
||||
#define OSRM_ENGINE_DATAFACADE_FACTORY_HPP
|
||||
|
||||
#include "extractor/class_data.hpp"
|
||||
#include "extractor/profile_properties.hpp"
|
||||
|
||||
#include "engine/algorithm.hpp"
|
||||
#include "engine/api/base_parameters.hpp"
|
||||
#include "engine/api/tile_parameters.hpp"
|
||||
|
||||
#include "util/integer_range.hpp"
|
||||
|
||||
#include "storage/shared_datatype.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
// This class selects the right facade for
|
||||
template <template <typename A> class FacadeT, typename AlgorithmT> class DataFacadeFactory
|
||||
{
|
||||
static constexpr auto has_exclude_flags = routing_algorithms::HasExcludeFlags<AlgorithmT>{};
|
||||
|
||||
public:
|
||||
using Facade = FacadeT<AlgorithmT>;
|
||||
DataFacadeFactory() = default;
|
||||
|
||||
template <typename AllocatorT>
|
||||
DataFacadeFactory(std::shared_ptr<AllocatorT> allocator)
|
||||
: DataFacadeFactory(allocator, has_exclude_flags)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename ParameterT> std::shared_ptr<const Facade> Get(const ParameterT ¶ms) const
|
||||
{
|
||||
return Get(params, has_exclude_flags);
|
||||
}
|
||||
|
||||
private:
|
||||
// Algorithm with exclude flags
|
||||
template <typename AllocatorT>
|
||||
DataFacadeFactory(std::shared_ptr<AllocatorT> allocator, std::true_type)
|
||||
{
|
||||
for (const auto index : util::irange<std::size_t>(0, facades.size()))
|
||||
{
|
||||
facades[index] = std::make_shared<const Facade>(allocator, index);
|
||||
}
|
||||
|
||||
properties = allocator->GetLayout().template GetBlockPtr<extractor::ProfileProperties>(
|
||||
allocator->GetMemory(), storage::DataLayout::PROPERTIES);
|
||||
|
||||
for (const auto index : util::irange<std::size_t>(0, properties->class_names.size()))
|
||||
{
|
||||
const std::string name = properties->GetClassName(index);
|
||||
if (!name.empty())
|
||||
{
|
||||
name_to_class[name] = extractor::getClassData(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Algorithm without exclude flags
|
||||
template <typename AllocatorT>
|
||||
DataFacadeFactory(std::shared_ptr<AllocatorT> allocator, std::false_type)
|
||||
{
|
||||
facades[0] = std::make_shared<const Facade>(allocator, 0);
|
||||
}
|
||||
|
||||
std::shared_ptr<const Facade> Get(const api::TileParameters &, std::false_type) const
|
||||
{
|
||||
return facades[0];
|
||||
}
|
||||
|
||||
// Default for non-exclude flags: return only facade
|
||||
std::shared_ptr<const Facade> Get(const api::BaseParameters ¶ms, std::false_type) const
|
||||
{
|
||||
if (!params.exclude.empty())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
return facades[0];
|
||||
}
|
||||
|
||||
// TileParameters don't drive from BaseParameters and generally don't have use for exclude flags
|
||||
std::shared_ptr<const Facade> Get(const api::TileParameters &, std::true_type) const
|
||||
{
|
||||
return facades[0];
|
||||
}
|
||||
|
||||
// Selection logic for finding the corresponding datafacade for the given parameters
|
||||
std::shared_ptr<const Facade> Get(const api::BaseParameters ¶ms, std::true_type) const
|
||||
{
|
||||
if (params.exclude.empty())
|
||||
return facades[0];
|
||||
|
||||
extractor::ClassData mask = 0;
|
||||
for (const auto &name : params.exclude)
|
||||
{
|
||||
auto class_mask_iter = name_to_class.find(name);
|
||||
if (class_mask_iter == name_to_class.end())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
else
|
||||
{
|
||||
mask |= class_mask_iter->second;
|
||||
}
|
||||
}
|
||||
|
||||
auto exclude_iter = std::find(
|
||||
properties->excludable_classes.begin(), properties->excludable_classes.end(), mask);
|
||||
if (exclude_iter != properties->excludable_classes.end())
|
||||
{
|
||||
auto exclude_index =
|
||||
std::distance(properties->excludable_classes.begin(), exclude_iter);
|
||||
return facades[exclude_index];
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::array<std::shared_ptr<const Facade>, extractor::MAX_EXCLUDABLE_CLASSES> facades;
|
||||
std::unordered_map<std::string, extractor::ClassData> name_to_class;
|
||||
const extractor::ProfileProperties *properties = nullptr;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "engine/datafacade.hpp"
|
||||
#include "engine/datafacade/contiguous_internalmem_datafacade.hpp"
|
||||
#include "engine/datafacade/process_memory_allocator.hpp"
|
||||
#include "engine/datafacade_factory.hpp"
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
@@ -20,7 +21,8 @@ template <typename AlgorithmT, template <typename A> class FacadeT> class DataFa
|
||||
|
||||
virtual ~DataFacadeProvider() = default;
|
||||
|
||||
virtual std::shared_ptr<const Facade> Get() const = 0;
|
||||
virtual std::shared_ptr<const Facade> Get(const api::BaseParameters &) const = 0;
|
||||
virtual std::shared_ptr<const Facade> Get(const api::TileParameters &) const = 0;
|
||||
};
|
||||
|
||||
template <typename AlgorithmT, template <typename A> class FacadeT>
|
||||
@@ -30,26 +32,39 @@ class ImmutableProvider final : public DataFacadeProvider<AlgorithmT, FacadeT>
|
||||
using Facade = typename DataFacadeProvider<AlgorithmT, FacadeT>::Facade;
|
||||
|
||||
ImmutableProvider(const storage::StorageConfig &config)
|
||||
: immutable_data_facade(std::make_shared<Facade>(
|
||||
std::make_shared<datafacade::ProcessMemoryAllocator>(config)))
|
||||
: facade_factory(std::make_shared<datafacade::ProcessMemoryAllocator>(config))
|
||||
{
|
||||
}
|
||||
|
||||
std::shared_ptr<const Facade> Get() const override final { return immutable_data_facade; }
|
||||
std::shared_ptr<const Facade> Get(const api::TileParameters ¶ms) const override final
|
||||
{
|
||||
return facade_factory.Get(params);
|
||||
}
|
||||
std::shared_ptr<const Facade> Get(const api::BaseParameters ¶ms) const override final
|
||||
{
|
||||
return facade_factory.Get(params);
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<const Facade> immutable_data_facade;
|
||||
DataFacadeFactory<FacadeT, AlgorithmT> facade_factory;
|
||||
};
|
||||
|
||||
template <typename AlgorithmT, template <typename A> class FacadeT>
|
||||
class WatchingProvider : public DataFacadeProvider<AlgorithmT, FacadeT>
|
||||
{
|
||||
DataWatchdog<AlgorithmT> watchdog;
|
||||
DataWatchdog<AlgorithmT, FacadeT> watchdog;
|
||||
|
||||
public:
|
||||
using Facade = typename DataFacadeProvider<AlgorithmT, FacadeT>::Facade;
|
||||
|
||||
std::shared_ptr<const Facade> Get() const override final { return watchdog.Get(); }
|
||||
std::shared_ptr<const Facade> Get(const api::TileParameters ¶ms) const override final
|
||||
{
|
||||
return watchdog.Get(params);
|
||||
}
|
||||
std::shared_ptr<const Facade> Get(const api::BaseParameters ¶ms) const override final
|
||||
{
|
||||
return watchdog.Get(params);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+14
-15
@@ -85,46 +85,44 @@ template <typename Algorithm> class Engine final : public EngineInterface
|
||||
Status Route(const api::RouteParameters ¶ms,
|
||||
util::json::Object &result) const override final
|
||||
{
|
||||
auto algorithms = RoutingAlgorithms<Algorithm>{heaps, facade_provider->Get()};
|
||||
return route_plugin.HandleRequest(algorithms, params, result);
|
||||
return route_plugin.HandleRequest(GetAlgorithms(params), params, result);
|
||||
}
|
||||
|
||||
Status Table(const api::TableParameters ¶ms,
|
||||
util::json::Object &result) const override final
|
||||
{
|
||||
auto algorithms = RoutingAlgorithms<Algorithm>{heaps, facade_provider->Get()};
|
||||
return table_plugin.HandleRequest(algorithms, params, result);
|
||||
return table_plugin.HandleRequest(GetAlgorithms(params), params, result);
|
||||
}
|
||||
|
||||
Status Nearest(const api::NearestParameters ¶ms,
|
||||
util::json::Object &result) const override final
|
||||
{
|
||||
auto algorithms = RoutingAlgorithms<Algorithm>{heaps, facade_provider->Get()};
|
||||
return nearest_plugin.HandleRequest(algorithms, params, result);
|
||||
return nearest_plugin.HandleRequest(GetAlgorithms(params), params, result);
|
||||
}
|
||||
|
||||
Status Trip(const api::TripParameters ¶ms, util::json::Object &result) const override final
|
||||
{
|
||||
auto algorithms = RoutingAlgorithms<Algorithm>{heaps, facade_provider->Get()};
|
||||
return trip_plugin.HandleRequest(algorithms, params, result);
|
||||
return trip_plugin.HandleRequest(GetAlgorithms(params), params, result);
|
||||
}
|
||||
|
||||
Status Match(const api::MatchParameters ¶ms,
|
||||
util::json::Object &result) const override final
|
||||
{
|
||||
auto algorithms = RoutingAlgorithms<Algorithm>{heaps, facade_provider->Get()};
|
||||
return match_plugin.HandleRequest(algorithms, params, result);
|
||||
return match_plugin.HandleRequest(GetAlgorithms(params), params, result);
|
||||
}
|
||||
|
||||
Status Tile(const api::TileParameters ¶ms, std::string &result) const override final
|
||||
{
|
||||
auto algorithms = RoutingAlgorithms<Algorithm>{heaps, facade_provider->Get()};
|
||||
return tile_plugin.HandleRequest(algorithms, params, result);
|
||||
return tile_plugin.HandleRequest(GetAlgorithms(params), params, result);
|
||||
}
|
||||
|
||||
static bool CheckCompability(const EngineConfig &config);
|
||||
|
||||
private:
|
||||
template <typename ParametersT> auto GetAlgorithms(const ParametersT ¶ms) const
|
||||
{
|
||||
return RoutingAlgorithms<Algorithm>{heaps, facade_provider->Get(params)};
|
||||
}
|
||||
std::unique_ptr<DataFacadeProvider<Algorithm>> facade_provider;
|
||||
mutable SearchEngineData<Algorithm> heaps;
|
||||
|
||||
@@ -178,7 +176,7 @@ bool Engine<routing_algorithms::corech::Algorithm>::CheckCompability(const Engin
|
||||
|
||||
auto mem = storage::makeSharedMemory(barrier.data().region);
|
||||
auto layout = reinterpret_cast<storage::DataLayout *>(mem->Ptr());
|
||||
return layout->GetBlockSize(storage::DataLayout::CH_CORE_MARKER) >
|
||||
return layout->GetBlockSize(storage::DataLayout::CH_CORE_MARKER_0) >
|
||||
sizeof(std::uint64_t) + sizeof(util::FingerPrint);
|
||||
}
|
||||
else
|
||||
@@ -187,9 +185,10 @@ bool Engine<routing_algorithms::corech::Algorithm>::CheckCompability(const Engin
|
||||
return false;
|
||||
storage::io::FileReader in(config.storage_config.GetPath(".osrm.core"),
|
||||
storage::io::FileReader::VerifyFingerprint);
|
||||
in.ReadElementCount64(); // number of core markers
|
||||
const auto number_of_core_markers = in.ReadElementCount64();
|
||||
|
||||
auto size = in.GetSize();
|
||||
return size > sizeof(std::uint64_t);
|
||||
return number_of_core_markers > 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
|
||||
auto results = rtree.Nearest(
|
||||
input_coordinate,
|
||||
[this, approach, &input_coordinate](const CandidateSegment &segment) {
|
||||
return boolPairAnd(HasValidEdge(segment),
|
||||
return boolPairAnd(boolPairAnd(HasValidEdge(segment), CheckSegmentExclude(segment)),
|
||||
CheckApproach(input_coordinate, segment, approach));
|
||||
},
|
||||
[this, max_distance, input_coordinate](const std::size_t,
|
||||
@@ -81,8 +81,9 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
|
||||
input_coordinate,
|
||||
[this, approach, &input_coordinate, bearing, bearing_range, max_distance](
|
||||
const CandidateSegment &segment) {
|
||||
auto use_direction = boolPairAnd(
|
||||
CheckSegmentBearing(segment, bearing, bearing_range), HasValidEdge(segment));
|
||||
auto use_direction =
|
||||
boolPairAnd(CheckSegmentBearing(segment, bearing, bearing_range),
|
||||
boolPairAnd(HasValidEdge(segment), CheckSegmentExclude(segment)));
|
||||
use_direction =
|
||||
boolPairAnd(use_direction, CheckApproach(input_coordinate, segment, approach));
|
||||
return use_direction;
|
||||
@@ -108,8 +109,9 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
|
||||
input_coordinate,
|
||||
[this, approach, &input_coordinate, bearing, bearing_range](
|
||||
const CandidateSegment &segment) {
|
||||
auto use_direction = boolPairAnd(
|
||||
CheckSegmentBearing(segment, bearing, bearing_range), HasValidEdge(segment));
|
||||
auto use_direction =
|
||||
boolPairAnd(CheckSegmentBearing(segment, bearing, bearing_range),
|
||||
boolPairAnd(HasValidEdge(segment), CheckSegmentExclude(segment)));
|
||||
return boolPairAnd(use_direction,
|
||||
CheckApproach(input_coordinate, segment, approach));
|
||||
},
|
||||
@@ -135,8 +137,9 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
|
||||
input_coordinate,
|
||||
[this, approach, &input_coordinate, bearing, bearing_range](
|
||||
const CandidateSegment &segment) {
|
||||
auto use_direction = boolPairAnd(
|
||||
CheckSegmentBearing(segment, bearing, bearing_range), HasValidEdge(segment));
|
||||
auto use_direction =
|
||||
boolPairAnd(CheckSegmentBearing(segment, bearing, bearing_range),
|
||||
boolPairAnd(HasValidEdge(segment), CheckSegmentExclude(segment)));
|
||||
return boolPairAnd(use_direction,
|
||||
CheckApproach(input_coordinate, segment, approach));
|
||||
},
|
||||
@@ -159,7 +162,7 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
|
||||
auto results = rtree.Nearest(
|
||||
input_coordinate,
|
||||
[this, approach, &input_coordinate](const CandidateSegment &segment) {
|
||||
return boolPairAnd(HasValidEdge(segment),
|
||||
return boolPairAnd(boolPairAnd(HasValidEdge(segment), CheckSegmentExclude(segment)),
|
||||
CheckApproach(input_coordinate, segment, approach));
|
||||
},
|
||||
[max_results](const std::size_t num_results, const CandidateSegment &) {
|
||||
@@ -180,7 +183,7 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
|
||||
auto results = rtree.Nearest(
|
||||
input_coordinate,
|
||||
[this, approach, &input_coordinate](const CandidateSegment &segment) {
|
||||
return boolPairAnd(HasValidEdge(segment),
|
||||
return boolPairAnd(boolPairAnd(HasValidEdge(segment), CheckSegmentExclude(segment)),
|
||||
CheckApproach(input_coordinate, segment, approach));
|
||||
},
|
||||
[this, max_distance, max_results, input_coordinate](const std::size_t num_results,
|
||||
@@ -209,6 +212,8 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
|
||||
(!has_small_component || (!has_big_component && !IsTinyComponent(segment)));
|
||||
auto use_directions = std::make_pair(use_segment, use_segment);
|
||||
const auto valid_edges = HasValidEdge(segment);
|
||||
const auto admissible_segments = CheckSegmentExclude(segment);
|
||||
use_directions = boolPairAnd(use_directions, admissible_segments);
|
||||
use_directions = boolPairAnd(use_directions, valid_edges);
|
||||
use_directions =
|
||||
boolPairAnd(use_directions, CheckApproach(input_coordinate, segment, approach));
|
||||
@@ -254,6 +259,8 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
|
||||
auto use_directions = std::make_pair(use_segment, use_segment);
|
||||
|
||||
const auto valid_edges = HasValidEdge(segment);
|
||||
const auto admissible_segments = CheckSegmentExclude(segment);
|
||||
use_directions = boolPairAnd(use_directions, admissible_segments);
|
||||
use_directions = boolPairAnd(use_directions, valid_edges);
|
||||
use_directions =
|
||||
boolPairAnd(use_directions, CheckApproach(input_coordinate, segment, approach));
|
||||
@@ -302,6 +309,7 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
|
||||
auto use_segment =
|
||||
(!has_small_component || (!has_big_component && !IsTinyComponent(segment)));
|
||||
auto use_directions = std::make_pair(use_segment, use_segment);
|
||||
const auto admissible_segments = CheckSegmentExclude(segment);
|
||||
use_directions = boolPairAnd(use_directions, HasValidEdge(segment));
|
||||
|
||||
if (use_segment)
|
||||
@@ -309,6 +317,7 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
|
||||
use_directions =
|
||||
boolPairAnd(CheckSegmentBearing(segment, bearing, bearing_range),
|
||||
HasValidEdge(segment));
|
||||
use_directions = boolPairAnd(use_directions, admissible_segments);
|
||||
use_directions = boolPairAnd(
|
||||
use_directions, CheckApproach(input_coordinate, segment, approach));
|
||||
|
||||
@@ -358,6 +367,7 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
|
||||
auto use_segment =
|
||||
(!has_small_component || (!has_big_component && !IsTinyComponent(segment)));
|
||||
auto use_directions = std::make_pair(use_segment, use_segment);
|
||||
const auto admissible_segments = CheckSegmentExclude(segment);
|
||||
use_directions = boolPairAnd(use_directions, HasValidEdge(segment));
|
||||
|
||||
if (use_segment)
|
||||
@@ -365,6 +375,7 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
|
||||
use_directions =
|
||||
boolPairAnd(CheckSegmentBearing(segment, bearing, bearing_range),
|
||||
HasValidEdge(segment));
|
||||
use_directions = boolPairAnd(use_directions, admissible_segments);
|
||||
use_directions = boolPairAnd(
|
||||
use_directions, CheckApproach(input_coordinate, segment, approach));
|
||||
|
||||
@@ -491,23 +502,26 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
|
||||
bool is_reverse_valid_target = areSegmentsValid(
|
||||
reverse_weight_vector.begin(), reverse_weight_vector.end() - data.fwd_segment_position);
|
||||
|
||||
auto transformed = PhantomNodeWithDistance{PhantomNode{data,
|
||||
component_id,
|
||||
forward_weight,
|
||||
reverse_weight,
|
||||
forward_weight_offset,
|
||||
reverse_weight_offset,
|
||||
forward_duration,
|
||||
reverse_duration,
|
||||
forward_duration_offset,
|
||||
reverse_duration_offset,
|
||||
is_forward_valid_source,
|
||||
is_forward_valid_target,
|
||||
is_reverse_valid_source,
|
||||
is_reverse_valid_target,
|
||||
point_on_segment,
|
||||
input_coordinate},
|
||||
current_perpendicular_distance};
|
||||
auto transformed = PhantomNodeWithDistance{
|
||||
PhantomNode{data,
|
||||
component_id,
|
||||
forward_weight,
|
||||
reverse_weight,
|
||||
forward_weight_offset,
|
||||
reverse_weight_offset,
|
||||
forward_duration,
|
||||
reverse_duration,
|
||||
forward_duration_offset,
|
||||
reverse_duration_offset,
|
||||
is_forward_valid_source,
|
||||
is_forward_valid_target,
|
||||
is_reverse_valid_source,
|
||||
is_reverse_valid_target,
|
||||
point_on_segment,
|
||||
input_coordinate,
|
||||
static_cast<unsigned short>(util::coordinate_calculation::bearing(
|
||||
coordinates[data.u], coordinates[data.v]))},
|
||||
current_perpendicular_distance};
|
||||
|
||||
return transformed;
|
||||
}
|
||||
@@ -528,6 +542,25 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
|
||||
max_distance;
|
||||
}
|
||||
|
||||
std::pair<bool, bool> CheckSegmentExclude(const CandidateSegment &segment) const
|
||||
{
|
||||
std::pair<bool, bool> valid = {true, true};
|
||||
|
||||
if (segment.data.forward_segment_id.enabled &&
|
||||
datafacade.ExcludeNode(segment.data.forward_segment_id.id))
|
||||
{
|
||||
valid.first = false;
|
||||
}
|
||||
|
||||
if (segment.data.reverse_segment_id.enabled &&
|
||||
datafacade.ExcludeNode(segment.data.reverse_segment_id.id))
|
||||
{
|
||||
valid.second = false;
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
std::pair<bool, bool> CheckSegmentBearing(const CandidateSegment &segment,
|
||||
const int filter_bearing,
|
||||
const int filter_bearing_range) const
|
||||
|
||||
@@ -29,8 +29,12 @@ namespace guidance
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
std::pair<short, short> getDepartBearings(const LegGeometry &leg_geometry);
|
||||
std::pair<short, short> getArriveBearings(const LegGeometry &leg_geometry);
|
||||
std::pair<short, short> getDepartBearings(const LegGeometry &leg_geometry,
|
||||
const PhantomNode &source_node,
|
||||
const bool traversed_in_reverse);
|
||||
std::pair<short, short> getArriveBearings(const LegGeometry &leg_geometry,
|
||||
const PhantomNode &target_node,
|
||||
const bool traversed_in_reverse);
|
||||
} // ns detail
|
||||
|
||||
inline std::vector<RouteStep> assembleSteps(const datafacade::BaseDataFacade &facade,
|
||||
@@ -72,7 +76,8 @@ inline std::vector<RouteStep> assembleSteps(const datafacade::BaseDataFacade &fa
|
||||
std::size_t segment_index = 0;
|
||||
BOOST_ASSERT(leg_geometry.locations.size() >= 2);
|
||||
|
||||
auto bearings = detail::getDepartBearings(leg_geometry);
|
||||
auto bearings =
|
||||
detail::getDepartBearings(leg_geometry, source_node, source_traversed_in_reverse);
|
||||
|
||||
StepManeuver maneuver{source_node.location,
|
||||
bearings.first,
|
||||
@@ -260,7 +265,7 @@ inline std::vector<RouteStep> assembleSteps(const datafacade::BaseDataFacade &fa
|
||||
}
|
||||
|
||||
BOOST_ASSERT(segment_index == number_of_segments - 1);
|
||||
bearings = detail::getArriveBearings(leg_geometry);
|
||||
bearings = detail::getArriveBearings(leg_geometry, target_node, target_traversed_in_reverse);
|
||||
|
||||
intersection = {
|
||||
target_node.location,
|
||||
|
||||
@@ -31,6 +31,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "extractor/travel_mode.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include "util/bearing.hpp"
|
||||
#include "util/coordinate.hpp"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
@@ -53,7 +54,7 @@ struct PhantomNode
|
||||
forward_duration(MAXIMAL_EDGE_DURATION), reverse_duration(MAXIMAL_EDGE_DURATION),
|
||||
forward_duration_offset(0), reverse_duration_offset(0), fwd_segment_position(0),
|
||||
is_valid_forward_source{false}, is_valid_forward_target{false},
|
||||
is_valid_reverse_source{false}, is_valid_reverse_target{false}
|
||||
is_valid_reverse_source{false}, is_valid_reverse_target{false}, bearing(0)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -117,6 +118,12 @@ struct PhantomNode
|
||||
{
|
||||
return reverse_segment_id.enabled && is_valid_reverse_target;
|
||||
}
|
||||
short GetBearing(const bool traversed_in_reverse) const
|
||||
{
|
||||
if (traversed_in_reverse)
|
||||
return std::round(util::bearing::reverse(bearing));
|
||||
return std::round(bearing);
|
||||
}
|
||||
|
||||
bool operator==(const PhantomNode &other) const { return location == other.location; }
|
||||
|
||||
@@ -136,7 +143,8 @@ struct PhantomNode
|
||||
bool is_valid_reverse_source,
|
||||
bool is_valid_reverse_target,
|
||||
const util::Coordinate location,
|
||||
const util::Coordinate input_location)
|
||||
const util::Coordinate input_location,
|
||||
const unsigned short bearing)
|
||||
: forward_segment_id{other.forward_segment_id},
|
||||
reverse_segment_id{other.reverse_segment_id}, forward_weight{forward_weight},
|
||||
reverse_weight{reverse_weight}, forward_weight_offset{forward_weight_offset},
|
||||
@@ -148,7 +156,7 @@ struct PhantomNode
|
||||
is_valid_forward_source{is_valid_forward_source},
|
||||
is_valid_forward_target{is_valid_forward_target},
|
||||
is_valid_reverse_source{is_valid_reverse_source},
|
||||
is_valid_reverse_target{is_valid_reverse_target}
|
||||
is_valid_reverse_target{is_valid_reverse_target}, bearing{bearing}
|
||||
{
|
||||
}
|
||||
|
||||
@@ -173,7 +181,8 @@ struct PhantomNode
|
||||
unsigned short is_valid_forward_target : 1;
|
||||
unsigned short is_valid_reverse_source : 1;
|
||||
unsigned short is_valid_reverse_target : 1;
|
||||
unsigned short : 12; // Unused padding out to 16 bits (2 bytes)
|
||||
unsigned short bearing : 9;
|
||||
unsigned short : 3; // Unused padding out to 16 bits (2 bytes)
|
||||
};
|
||||
|
||||
static_assert(sizeof(PhantomNode) == 64, "PhantomNode has more padding then expected");
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "engine/api/base_parameters.hpp"
|
||||
#include "engine/datafacade/datafacade_base.hpp"
|
||||
#include "engine/phantom_node.hpp"
|
||||
#include "engine/routing_algorithms.hpp"
|
||||
#include "engine/status.hpp"
|
||||
|
||||
#include "util/coordinate.hpp"
|
||||
@@ -36,6 +37,31 @@ class BasePlugin
|
||||
});
|
||||
}
|
||||
|
||||
bool CheckAlgorithms(const api::BaseParameters ¶ms,
|
||||
const RoutingAlgorithmsInterface &algorithms,
|
||||
util::json::Object &result) const
|
||||
{
|
||||
if (algorithms.IsValid())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!algorithms.HasExcludeFlags() && !params.exclude.empty())
|
||||
{
|
||||
Error("NotImplemented", "This algorithm does not support exclude flags.", result);
|
||||
return false;
|
||||
}
|
||||
if (algorithms.HasExcludeFlags() && !params.exclude.empty())
|
||||
{
|
||||
Error("InvalidValue", "Exclude flag combination is not supported.", result);
|
||||
return false;
|
||||
}
|
||||
|
||||
BOOST_ASSERT_MSG(false,
|
||||
"There are only two reasons why the algorithm interface can be invalid.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Status Error(const std::string &code,
|
||||
const std::string &message,
|
||||
util::json::Object &json_result) const
|
||||
|
||||
@@ -54,6 +54,8 @@ class RoutingAlgorithmsInterface
|
||||
virtual bool HasMapMatching() const = 0;
|
||||
virtual bool HasManyToManySearch() const = 0;
|
||||
virtual bool HasGetTileTurns() const = 0;
|
||||
virtual bool HasExcludeFlags() const = 0;
|
||||
virtual bool IsValid() const = 0;
|
||||
};
|
||||
|
||||
// Short-lived object passed to each plugin in request to wrap routing algorithms
|
||||
@@ -127,6 +129,13 @@ template <typename Algorithm> class RoutingAlgorithms final : public RoutingAlgo
|
||||
return routing_algorithms::HasGetTileTurns<Algorithm>::value;
|
||||
}
|
||||
|
||||
bool HasExcludeFlags() const final override
|
||||
{
|
||||
return routing_algorithms::HasExcludeFlags<Algorithm>::value;
|
||||
}
|
||||
|
||||
bool IsValid() const final override { return static_cast<bool>(facade); }
|
||||
|
||||
private:
|
||||
SearchEngineData<Algorithm> &heaps;
|
||||
std::shared_ptr<const DataFacade<Algorithm>> facade;
|
||||
|
||||
@@ -143,10 +143,13 @@ void routingStep(const DataFacade<Algorithm> &facade,
|
||||
{
|
||||
const auto &partition = facade.GetMultiLevelPartition();
|
||||
const auto &cells = facade.GetCellStorage();
|
||||
const auto &metric = facade.GetCellMetric();
|
||||
|
||||
const auto node = forward_heap.DeleteMin();
|
||||
const auto weight = forward_heap.GetKey(node);
|
||||
|
||||
BOOST_ASSERT(!facade.ExcludeNode(node));
|
||||
|
||||
// Upper bound for the path source -> target with
|
||||
// weight(source -> node) = weight weight(to -> target) ≤ reverse_weight
|
||||
// is weight + reverse_weight
|
||||
@@ -174,12 +177,13 @@ void routingStep(const DataFacade<Algorithm> &facade,
|
||||
if (DIRECTION == FORWARD_DIRECTION)
|
||||
{
|
||||
// Shortcuts in forward direction
|
||||
const auto &cell = cells.GetCell(level, partition.GetCell(level, node));
|
||||
const auto &cell = cells.GetCell(metric, level, partition.GetCell(level, node));
|
||||
auto destination = cell.GetDestinationNodes().begin();
|
||||
for (auto shortcut_weight : cell.GetOutWeight(node))
|
||||
{
|
||||
BOOST_ASSERT(destination != cell.GetDestinationNodes().end());
|
||||
const NodeID to = *destination;
|
||||
|
||||
if (shortcut_weight != INVALID_EDGE_WEIGHT && node != to)
|
||||
{
|
||||
const EdgeWeight to_weight = weight + shortcut_weight;
|
||||
@@ -200,12 +204,13 @@ void routingStep(const DataFacade<Algorithm> &facade,
|
||||
else
|
||||
{
|
||||
// Shortcuts in backward direction
|
||||
const auto &cell = cells.GetCell(level, partition.GetCell(level, node));
|
||||
const auto &cell = cells.GetCell(metric, level, partition.GetCell(level, node));
|
||||
auto source = cell.GetSourceNodes().begin();
|
||||
for (auto shortcut_weight : cell.GetInWeight(node))
|
||||
{
|
||||
BOOST_ASSERT(source != cell.GetSourceNodes().end());
|
||||
const NodeID to = *source;
|
||||
|
||||
if (shortcut_weight != INVALID_EDGE_WEIGHT && node != to)
|
||||
{
|
||||
const EdgeWeight to_weight = weight + shortcut_weight;
|
||||
@@ -233,7 +238,8 @@ void routingStep(const DataFacade<Algorithm> &facade,
|
||||
{
|
||||
const NodeID to = facade.GetTarget(edge);
|
||||
|
||||
if (checkParentCellRestriction(partition.GetCell(level + 1, to), args...))
|
||||
if (!facade.ExcludeNode(to) &&
|
||||
checkParentCellRestriction(partition.GetCell(level + 1, to), args...))
|
||||
{
|
||||
BOOST_ASSERT_MSG(edge_data.weight > 0, "edge_weight invalid");
|
||||
const EdgeWeight to_weight = weight + edge_data.weight;
|
||||
|
||||
@@ -24,6 +24,7 @@ struct TurnData final
|
||||
const int turn_angle;
|
||||
const EdgeWeight weight;
|
||||
const EdgeWeight duration;
|
||||
const extractor::guidance::TurnInstruction turn_instruction;
|
||||
};
|
||||
|
||||
using RTreeLeaf = datafacade::BaseDataFacade::RTreeLeaf;
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
#include "util/bit_range.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstdint>
|
||||
|
||||
namespace osrm
|
||||
@@ -11,11 +13,26 @@ namespace extractor
|
||||
{
|
||||
|
||||
using ClassData = std::uint8_t;
|
||||
constexpr ClassData INAVLID_CLASS_DATA = std::numeric_limits<ClassData>::max();
|
||||
static const std::uint8_t MAX_CLASS_INDEX = 8 - 1;
|
||||
static const std::uint8_t MAX_EXCLUDABLE_CLASSES = 8;
|
||||
|
||||
inline bool isSubset(const ClassData lhs, const ClassData rhs) { return (lhs & rhs) == lhs; }
|
||||
|
||||
inline auto getClassIndexes(const ClassData data) { return util::makeBitRange<ClassData>(data); }
|
||||
|
||||
inline auto getClassData(const std::size_t index)
|
||||
{
|
||||
BOOST_ASSERT(index <= MAX_CLASS_INDEX);
|
||||
return uint8_t{1} << index;
|
||||
}
|
||||
|
||||
inline bool isValidClassName(const std::string &name)
|
||||
{
|
||||
return std::find_if_not(name.begin(), name.end(), [](const auto c) {
|
||||
return std::isalnum(c);
|
||||
}) == name.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -327,6 +327,42 @@ std::size_t IntersectionHandler::findObviousTurn(const EdgeID via_edge,
|
||||
return false;
|
||||
}();
|
||||
|
||||
// check whether we turn onto a oneway through street. These typically happen at the end of
|
||||
// roads and might not seem obvious, since it isn't always as visible that you cannot turn
|
||||
// left/right. To be on the safe side, we announce these as non-obvious
|
||||
const auto turns_onto_through_street = [&](const auto &road) {
|
||||
// find edge opposite to the one we are checking (in-road)
|
||||
const auto in_through_candidate =
|
||||
intersection.FindClosestBearing(util::bearing::reverse(road.bearing));
|
||||
|
||||
const auto &in_data = node_based_graph.GetEdgeData(in_through_candidate->eid);
|
||||
const auto &out_data = node_based_graph.GetEdgeData(road.eid);
|
||||
|
||||
// by asking for the same class, we ensure that we do not overrule obvious by road-class
|
||||
// decisions
|
||||
const auto same_class = in_data.road_classification == out_data.road_classification;
|
||||
|
||||
// only if the entry is allowed for one of the two, but not the other, we need to check.
|
||||
// Otherwise other handlers do it better
|
||||
const bool is_oneway = !in_through_candidate->entry_allowed && road.entry_allowed;
|
||||
|
||||
const bool not_roundabout =
|
||||
!(in_data.roundabout || in_data.circular || out_data.roundabout || out_data.circular);
|
||||
|
||||
// for the purpose of this check, we do not care about low-priority roads (parking lots,
|
||||
// mostly). Since we postulate both classes to be the same, checking one of the two is
|
||||
// enough
|
||||
const bool not_low_priority = !in_data.road_classification.IsLowPriorityRoadClass();
|
||||
|
||||
const auto in_deviation = angularDeviation(in_through_candidate->angle, STRAIGHT_ANGLE);
|
||||
const auto out_deviaiton = angularDeviation(road.angle, STRAIGHT_ANGLE);
|
||||
// in case the deviation isn't considerably lower for the road we are turning onto,
|
||||
// consider it non-obvious. The threshold here requires a slight (60) vs sharp (120)
|
||||
// degree variation, at lest (120/60 == 2)
|
||||
return is_oneway && same_class && not_roundabout && not_low_priority &&
|
||||
(in_deviation / (std::max(out_deviaiton, 0.5)) <= 2);
|
||||
};
|
||||
|
||||
if (best_over_best_continue)
|
||||
{
|
||||
// Find left/right deviation
|
||||
@@ -366,8 +402,7 @@ std::size_t IntersectionHandler::findObviousTurn(const EdgeID via_edge,
|
||||
angularDeviation(intersection[right_index].angle, STRAIGHT_ANGLE);
|
||||
|
||||
// return best_option candidate if it is nearly straight and distinct from the nearest other
|
||||
// out
|
||||
// way
|
||||
// out way
|
||||
if (best_option_deviation < MAXIMAL_ALLOWED_NO_TURN_DEVIATION &&
|
||||
std::min(left_deviation, right_deviation) > FUZZY_ANGLE_DIFFERENCE)
|
||||
return best_option;
|
||||
@@ -385,8 +420,7 @@ std::size_t IntersectionHandler::findObviousTurn(const EdgeID via_edge,
|
||||
right_data.road_classification);
|
||||
|
||||
// if the best_option turn isn't narrow, but there is a nearly straight turn, we don't
|
||||
// consider the
|
||||
// turn obvious
|
||||
// consider the turn obvious
|
||||
const auto check_narrow = [&intersection, best_option_deviation](const std::size_t index) {
|
||||
return angularDeviation(intersection[index].angle, STRAIGHT_ANGLE) <=
|
||||
FUZZY_ANGLE_DIFFERENCE &&
|
||||
@@ -400,6 +434,11 @@ std::size_t IntersectionHandler::findObviousTurn(const EdgeID via_edge,
|
||||
if (check_narrow(left_index) && !obvious_to_left)
|
||||
return 0;
|
||||
|
||||
// we are turning onto a through street (possibly at the end of the road). Ensure that we
|
||||
// announce a turn, if it isn't a slight merge
|
||||
if (turns_onto_through_street(intersection[best_option]))
|
||||
return 0;
|
||||
|
||||
// checks if a given way in the intersection is distinct enough from the best_option
|
||||
// candidate
|
||||
const auto isDistinct = [&](const std::size_t index, const double deviation) {
|
||||
@@ -437,6 +476,11 @@ std::size_t IntersectionHandler::findObviousTurn(const EdgeID via_edge,
|
||||
if (std::abs(best_continue_deviation) < 1)
|
||||
return best_continue;
|
||||
|
||||
// we are turning onto a through street (possibly at the end of the road). Ensure that we
|
||||
// announce a turn, if it isn't a slight merge
|
||||
if (turns_onto_through_street(intersection[best_continue]))
|
||||
return 0;
|
||||
|
||||
// check if any other similar best continues exist
|
||||
std::size_t i, last = intersection.size();
|
||||
for (i = 1; i < last; ++i)
|
||||
|
||||
@@ -5,9 +5,11 @@
|
||||
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/numeric/conversion/cast.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
|
||||
namespace osrm
|
||||
@@ -28,6 +30,7 @@ struct ProfileProperties
|
||||
use_turn_restrictions(false), left_hand_driving(false), fallback_to_duration(true),
|
||||
weight_name{"duration"}, call_tagless_node_function(true)
|
||||
{
|
||||
std::fill(excludable_classes.begin(), excludable_classes.end(), INAVLID_CLASS_DATA);
|
||||
BOOST_ASSERT(weight_name[MAX_WEIGHT_NAME_LENGTH] == '\0');
|
||||
}
|
||||
|
||||
@@ -70,6 +73,24 @@ struct ProfileProperties
|
||||
return std::string(weight_name);
|
||||
}
|
||||
|
||||
// Mark this combination of classes as excludable
|
||||
void SetExcludableClasses(std::size_t index, ClassData classes)
|
||||
{
|
||||
excludable_classes[index] = classes;
|
||||
}
|
||||
|
||||
// Check if this classes are excludable
|
||||
boost::optional<std::size_t> ClassesAreExcludable(ClassData classes) const
|
||||
{
|
||||
auto iter = std::find(excludable_classes.begin(), excludable_classes.end(), classes);
|
||||
if (iter != excludable_classes.end())
|
||||
{
|
||||
return std::distance(excludable_classes.begin(), iter);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void SetClassName(std::size_t index, const std::string &name)
|
||||
{
|
||||
char *name_ptr = class_names[index];
|
||||
@@ -109,6 +130,8 @@ struct ProfileProperties
|
||||
char weight_name[MAX_WEIGHT_NAME_LENGTH + 1];
|
||||
//! stores the names of each class
|
||||
std::array<char[MAX_CLASS_NAME_LENGTH + 1], MAX_CLASS_INDEX + 1> class_names;
|
||||
//! stores the masks of excludable class combinations
|
||||
std::array<ClassData, MAX_EXCLUDABLE_CLASSES> excludable_classes;
|
||||
unsigned weight_precision = 1;
|
||||
bool force_split_edges = false;
|
||||
bool call_tagless_node_function = true;
|
||||
|
||||
@@ -52,6 +52,8 @@ class ScriptingEnvironment
|
||||
|
||||
virtual const ProfileProperties &GetProfileProperties() = 0;
|
||||
|
||||
virtual std::vector<std::vector<std::string>> GetExcludableClasses() = 0;
|
||||
virtual std::vector<std::string> GetClassNames() = 0;
|
||||
virtual std::vector<std::string> GetNameSuffixList() = 0;
|
||||
virtual std::vector<std::string> GetRestrictions() = 0;
|
||||
virtual void ProcessTurn(ExtractionTurn &turn) = 0;
|
||||
|
||||
@@ -58,11 +58,9 @@ class Sol2ScriptingEnvironment final : public ScriptingEnvironment
|
||||
|
||||
const ProfileProperties &GetProfileProperties() override;
|
||||
|
||||
LuaScriptingContext &GetSol2Context();
|
||||
|
||||
std::vector<std::string> GetStringListFromTable(const std::string &table_name);
|
||||
std::vector<std::string> GetStringListFromFunction(const std::string &function_name);
|
||||
std::vector<std::vector<std::string>> GetExcludableClasses() override;
|
||||
std::vector<std::string> GetNameSuffixList() override;
|
||||
std::vector<std::string> GetClassNames() override;
|
||||
std::vector<std::string> GetRestrictions() override;
|
||||
void ProcessTurn(ExtractionTurn &turn) override;
|
||||
void ProcessSegment(ExtractionSegment &segment) override;
|
||||
@@ -75,6 +73,12 @@ class Sol2ScriptingEnvironment final : public ScriptingEnvironment
|
||||
std::vector<InputConditionalTurnRestriction> &resulting_restrictions) override;
|
||||
|
||||
private:
|
||||
LuaScriptingContext &GetSol2Context();
|
||||
|
||||
std::vector<std::string> GetStringListFromTable(const std::string &table_name);
|
||||
std::vector<std::vector<std::string>> GetStringListsFromTable(const std::string &table_name);
|
||||
std::vector<std::string> GetStringListFromFunction(const std::string &function_name);
|
||||
|
||||
void InitContext(LuaScriptingContext &context);
|
||||
std::mutex init_mutex;
|
||||
std::string file_name;
|
||||
|
||||
@@ -585,6 +585,39 @@ inline bool argumentsToParameter(const Nan::FunctionCallbackInfo<v8::Value> &arg
|
||||
params->generate_hints = generate_hints->BooleanValue();
|
||||
}
|
||||
|
||||
if (obj->Has(Nan::New("exclude").ToLocalChecked()))
|
||||
{
|
||||
v8::Local<v8::Value> exclude = obj->Get(Nan::New("exclude").ToLocalChecked());
|
||||
if (exclude.IsEmpty())
|
||||
return false;
|
||||
|
||||
if (!exclude->IsArray())
|
||||
{
|
||||
Nan::ThrowError("Exclude must be an array of strings or empty");
|
||||
return false;
|
||||
}
|
||||
|
||||
v8::Local<v8::Array> exclude_array = v8::Local<v8::Array>::Cast(exclude);
|
||||
|
||||
for (uint32_t i = 0; i < exclude_array->Length(); ++i)
|
||||
{
|
||||
v8::Local<v8::Value> class_name = exclude_array->Get(i);
|
||||
if (class_name.IsEmpty())
|
||||
return false;
|
||||
|
||||
if (class_name->IsString())
|
||||
{
|
||||
std::string class_name_str = *v8::String::Utf8Value(class_name);
|
||||
params->exclude.emplace_back(class_name_str);
|
||||
}
|
||||
else
|
||||
{
|
||||
Nan::ThrowError("Exclude must be an array of strings or empty");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef OSRM_PARTITION_GRAPHVIEW_HPP_
|
||||
#define OSRM_PARTITION_GRAPHVIEW_HPP_
|
||||
#ifndef OSRM_PARTITION_BISECTION_GRAPHVIEW_HPP_
|
||||
#define OSRM_PARTITION_BISECTION_GRAPHVIEW_HPP_
|
||||
|
||||
#include "partition/bisection_graph.hpp"
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace partition
|
||||
|
||||
// Non-owning immutable sub-graph view into a base graph.
|
||||
// The part of the graph to select is determined by the recursive bisection state.
|
||||
class GraphView
|
||||
class BisectionGraphView
|
||||
{
|
||||
public:
|
||||
using ConstNodeIterator = BisectionGraph::ConstNodeIterator;
|
||||
@@ -26,13 +26,15 @@ class GraphView
|
||||
using EdgeT = BisectionGraph::EdgeT;
|
||||
|
||||
// Construction either for a subrange, or for a full range
|
||||
GraphView(const BisectionGraph &graph);
|
||||
GraphView(const BisectionGraph &graph,
|
||||
const ConstNodeIterator begin,
|
||||
const ConstNodeIterator end);
|
||||
BisectionGraphView(const BisectionGraph &graph);
|
||||
BisectionGraphView(const BisectionGraph &graph,
|
||||
const ConstNodeIterator begin,
|
||||
const ConstNodeIterator end);
|
||||
|
||||
// construction from a different view, no need to keep the graph around
|
||||
GraphView(const GraphView &view, const ConstNodeIterator begin, const ConstNodeIterator end);
|
||||
BisectionGraphView(const BisectionGraphView &view,
|
||||
const ConstNodeIterator begin,
|
||||
const ConstNodeIterator end);
|
||||
|
||||
// Number of nodes _in this sub-graph.
|
||||
std::size_t NumberOfNodes() const;
|
||||
@@ -12,6 +12,8 @@
|
||||
#include "storage/io_fwd.hpp"
|
||||
#include "storage/shared_memory_ownership.hpp"
|
||||
|
||||
#include "customizer/cell_metric.hpp"
|
||||
|
||||
#include <boost/iterator/iterator_facade.hpp>
|
||||
#include <boost/range/iterator_range.hpp>
|
||||
#include <tbb/parallel_sort.h>
|
||||
@@ -323,26 +325,42 @@ template <storage::Ownership Ownership> class CellStorageImpl
|
||||
cell.value_offset = value_offset;
|
||||
value_offset += cell.num_source_nodes * cell.num_destination_nodes;
|
||||
}
|
||||
}
|
||||
|
||||
weights.resize(value_offset + 1, INVALID_EDGE_WEIGHT);
|
||||
durations.resize(value_offset + 1, MAXIMAL_EDGE_DURATION);
|
||||
// Returns a new metric that can be used with this container
|
||||
customizer::CellMetric MakeMetric() const
|
||||
{
|
||||
customizer::CellMetric metric;
|
||||
|
||||
if (cells.empty())
|
||||
{
|
||||
return metric;
|
||||
}
|
||||
|
||||
const auto &last_cell = cells.back();
|
||||
ValueOffset total_size =
|
||||
last_cell.value_offset + last_cell.num_source_nodes * last_cell.num_destination_nodes;
|
||||
|
||||
metric.weights.resize(total_size + 1, INVALID_EDGE_WEIGHT);
|
||||
metric.durations.resize(total_size + 1, MAXIMAL_EDGE_DURATION);
|
||||
|
||||
return metric;
|
||||
}
|
||||
|
||||
template <typename = std::enable_if<Ownership == storage::Ownership::View>>
|
||||
CellStorageImpl(Vector<EdgeWeight> weights_,
|
||||
Vector<EdgeDuration> durations_,
|
||||
Vector<NodeID> source_boundary_,
|
||||
CellStorageImpl(Vector<NodeID> source_boundary_,
|
||||
Vector<NodeID> destination_boundary_,
|
||||
Vector<CellData> cells_,
|
||||
Vector<std::uint64_t> level_to_cell_offset_)
|
||||
: weights(std::move(weights_)), durations(std::move(durations_)),
|
||||
source_boundary(std::move(source_boundary_)),
|
||||
: source_boundary(std::move(source_boundary_)),
|
||||
destination_boundary(std::move(destination_boundary_)), cells(std::move(cells_)),
|
||||
level_to_cell_offset(std::move(level_to_cell_offset_))
|
||||
{
|
||||
}
|
||||
|
||||
ConstCell GetCell(LevelID level, CellID id) const
|
||||
ConstCell GetCell(const customizer::detail::CellMetricImpl<Ownership> &metric,
|
||||
LevelID level,
|
||||
CellID id) const
|
||||
{
|
||||
const auto level_index = LevelIDToIndex(level);
|
||||
BOOST_ASSERT(level_index < level_to_cell_offset.size());
|
||||
@@ -350,14 +368,14 @@ template <storage::Ownership Ownership> class CellStorageImpl
|
||||
const auto cell_index = offset + id;
|
||||
BOOST_ASSERT(cell_index < cells.size());
|
||||
return ConstCell{cells[cell_index],
|
||||
weights.data(),
|
||||
durations.data(),
|
||||
metric.weights.data(),
|
||||
metric.durations.data(),
|
||||
source_boundary.empty() ? nullptr : source_boundary.data(),
|
||||
destination_boundary.empty() ? nullptr : destination_boundary.data()};
|
||||
}
|
||||
|
||||
template <typename = std::enable_if<Ownership == storage::Ownership::Container>>
|
||||
Cell GetCell(LevelID level, CellID id)
|
||||
Cell GetCell(customizer::CellMetric &metric, LevelID level, CellID id) const
|
||||
{
|
||||
const auto level_index = LevelIDToIndex(level);
|
||||
BOOST_ASSERT(level_index < level_to_cell_offset.size());
|
||||
@@ -365,8 +383,8 @@ template <storage::Ownership Ownership> class CellStorageImpl
|
||||
const auto cell_index = offset + id;
|
||||
BOOST_ASSERT(cell_index < cells.size());
|
||||
return Cell{cells[cell_index],
|
||||
weights.data(),
|
||||
durations.data(),
|
||||
metric.weights.data(),
|
||||
metric.durations.data(),
|
||||
source_boundary.data(),
|
||||
destination_boundary.data()};
|
||||
}
|
||||
@@ -377,8 +395,6 @@ template <storage::Ownership Ownership> class CellStorageImpl
|
||||
const detail::CellStorageImpl<Ownership> &storage);
|
||||
|
||||
private:
|
||||
Vector<EdgeWeight> weights;
|
||||
Vector<EdgeDuration> durations;
|
||||
Vector<NodeID> source_boundary;
|
||||
Vector<NodeID> destination_boundary;
|
||||
Vector<CellData> cells;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifndef OSRM_PARTITION_DINIC_MAX_FLOW_HPP_
|
||||
#define OSRM_PARTITION_DINIC_MAX_FLOW_HPP_
|
||||
|
||||
#include "partition/graph_view.hpp"
|
||||
#include "partition/bisection_graph_view.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
@@ -31,12 +31,12 @@ class DinicMaxFlow
|
||||
// input parameter storing the set o
|
||||
using SourceSinkNodes = std::unordered_set<NodeID>;
|
||||
|
||||
MinCut operator()(const GraphView &view,
|
||||
MinCut operator()(const BisectionGraphView &view,
|
||||
const SourceSinkNodes &source_nodes,
|
||||
const SourceSinkNodes &sink_nodes) const;
|
||||
|
||||
// validates the inpiut parameters to the flow algorithm (e.g. not intersecting)
|
||||
bool Validate(const GraphView &view,
|
||||
bool Validate(const BisectionGraphView &view,
|
||||
const SourceSinkNodes &source_nodes,
|
||||
const SourceSinkNodes &sink_nodes) const;
|
||||
|
||||
@@ -57,7 +57,7 @@ class DinicMaxFlow
|
||||
// \ /
|
||||
// b
|
||||
// would assign s = 0, a,b = 1, t=2
|
||||
LevelGraph ComputeLevelGraph(const GraphView &view,
|
||||
LevelGraph ComputeLevelGraph(const BisectionGraphView &view,
|
||||
const std::vector<NodeID> &border_source_nodes,
|
||||
const SourceSinkNodes &source_nodes,
|
||||
const SourceSinkNodes &sink_nodes,
|
||||
@@ -68,7 +68,7 @@ class DinicMaxFlow
|
||||
// with increasing level exists from `s` to `t`).
|
||||
std::size_t BlockingFlow(FlowEdges &flow,
|
||||
LevelGraph &levels,
|
||||
const GraphView &view,
|
||||
const BisectionGraphView &view,
|
||||
const SourceSinkNodes &source_nodes,
|
||||
const std::vector<NodeID> &border_sink_nodes) const;
|
||||
|
||||
@@ -78,13 +78,14 @@ class DinicMaxFlow
|
||||
// sink nodes, instead of the source, so we can save a few dfs runs
|
||||
std::vector<NodeID> GetAugmentingPath(LevelGraph &levels,
|
||||
const NodeID from,
|
||||
const GraphView &view,
|
||||
const BisectionGraphView &view,
|
||||
const FlowEdges &flow,
|
||||
const SourceSinkNodes &source_nodes) const;
|
||||
|
||||
// Builds an actual cut result from a level graph
|
||||
MinCut
|
||||
MakeCut(const GraphView &view, const LevelGraph &levels, const std::size_t flow_value) const;
|
||||
MinCut MakeCut(const BisectionGraphView &view,
|
||||
const LevelGraph &levels,
|
||||
const std::size_t flow_value) const;
|
||||
};
|
||||
|
||||
} // namespace partition
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace partition
|
||||
{
|
||||
|
||||
// Bidirectional (s,t) to (s,t) and (t,s)
|
||||
std::vector<extractor::EdgeBasedEdge>
|
||||
inline std::vector<extractor::EdgeBasedEdge>
|
||||
splitBidirectionalEdges(const std::vector<extractor::EdgeBasedEdge> &edges)
|
||||
{
|
||||
std::vector<extractor::EdgeBasedEdge> directed;
|
||||
@@ -136,7 +136,8 @@ std::vector<OutputEdgeT> prepareEdgesForUsageInGraph(std::vector<extractor::Edge
|
||||
return output_edges;
|
||||
}
|
||||
|
||||
std::vector<extractor::EdgeBasedEdge> graphToEdges(const DynamicEdgeBasedGraph &edge_based_graph)
|
||||
inline std::vector<extractor::EdgeBasedEdge>
|
||||
graphToEdges(const DynamicEdgeBasedGraph &edge_based_graph)
|
||||
{
|
||||
auto range = tbb::blocked_range<NodeID>(0, edge_based_graph.GetNumberOfNodes());
|
||||
auto max_turn_id =
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
#ifndef OSRM_PARTITION_INERTIAL_FLOW_HPP_
|
||||
#define OSRM_PARTITION_INERTIAL_FLOW_HPP_
|
||||
|
||||
#include "partition/bisection_graph_view.hpp"
|
||||
#include "partition/dinic_max_flow.hpp"
|
||||
#include "partition/graph_view.hpp"
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace partition
|
||||
{
|
||||
|
||||
DinicMaxFlow::MinCut computeInertialFlowCut(const GraphView &view,
|
||||
DinicMaxFlow::MinCut computeInertialFlowCut(const BisectionGraphView &view,
|
||||
const std::size_t num_slopes,
|
||||
const double balance,
|
||||
const double source_sink_rate);
|
||||
|
||||
@@ -43,6 +43,10 @@ class MultiLevelGraph : public util::StaticGraph<EdgeDataT, Ownership>
|
||||
using EdgeOffset = std::uint8_t;
|
||||
|
||||
MultiLevelGraph() = default;
|
||||
MultiLevelGraph(MultiLevelGraph &&) = default;
|
||||
MultiLevelGraph(const MultiLevelGraph &) = default;
|
||||
MultiLevelGraph &operator=(MultiLevelGraph &&) = default;
|
||||
MultiLevelGraph &operator=(const MultiLevelGraph &) = default;
|
||||
|
||||
MultiLevelGraph(Vector<typename SuperT::NodeArrayEntry> node_array_,
|
||||
Vector<typename SuperT::EdgeArrayEntry> edge_array_,
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#define OSRM_PARTITION_RECURSIVE_BISECTION_HPP_
|
||||
|
||||
#include "partition/bisection_graph.hpp"
|
||||
#include "partition/graph_view.hpp"
|
||||
#include "partition/recursive_bisection_state.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "partition/bisection_graph.hpp"
|
||||
#include "partition/graph_view.hpp"
|
||||
#include "partition/bisection_graph_view.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
namespace osrm
|
||||
@@ -37,7 +37,7 @@ class RecursiveBisectionState
|
||||
// perform an initial pre-partitioning into small components
|
||||
// on larger graphs, SCCs give perfect cuts (think Amerika vs Europe)
|
||||
// This function performs an initial pre-partitioning using these sccs.
|
||||
std::vector<GraphView> PrePartitionWithSCC(const std::size_t small_component_size);
|
||||
std::vector<BisectionGraphView> PrePartitionWithSCC(const std::size_t small_component_size);
|
||||
|
||||
const std::vector<BisectionID> &BisectionIDs() const;
|
||||
|
||||
|
||||
@@ -54,8 +54,6 @@ inline void write(storage::io::FileWriter &writer,
|
||||
template <storage::Ownership Ownership>
|
||||
inline void read(storage::io::FileReader &reader, detail::CellStorageImpl<Ownership> &storage)
|
||||
{
|
||||
storage::serialization::read(reader, storage.weights);
|
||||
storage::serialization::read(reader, storage.durations);
|
||||
storage::serialization::read(reader, storage.source_boundary);
|
||||
storage::serialization::read(reader, storage.destination_boundary);
|
||||
storage::serialization::read(reader, storage.cells);
|
||||
@@ -66,8 +64,6 @@ template <storage::Ownership Ownership>
|
||||
inline void write(storage::io::FileWriter &writer,
|
||||
const detail::CellStorageImpl<Ownership> &storage)
|
||||
{
|
||||
storage::serialization::write(writer, storage.weights);
|
||||
storage::serialization::write(writer, storage.durations);
|
||||
storage::serialization::write(writer, storage.source_boundary);
|
||||
storage::serialization::write(writer, storage.destination_boundary);
|
||||
storage::serialization::write(writer, storage.cells);
|
||||
|
||||
@@ -162,10 +162,16 @@ struct BaseParametersGrammar : boost::spirit::qi::grammar<Iterator, Signature>
|
||||
(-approach_type %
|
||||
';')[ph::bind(&engine::api::BaseParameters::approaches, qi::_r1) = qi::_1];
|
||||
|
||||
base_rule = radiuses_rule(qi::_r1) //
|
||||
| hints_rule(qi::_r1) //
|
||||
| bearings_rule(qi::_r1) //
|
||||
| generate_hints_rule(qi::_r1) | approach_rule(qi::_r1);
|
||||
exclude_rule = qi::lit("exclude=") >
|
||||
(qi::as_string[+qi::char_("a-zA-Z0-9")] %
|
||||
',')[ph::bind(&engine::api::BaseParameters::exclude, qi::_r1) = qi::_1];
|
||||
|
||||
base_rule = radiuses_rule(qi::_r1) //
|
||||
| hints_rule(qi::_r1) //
|
||||
| bearings_rule(qi::_r1) //
|
||||
| generate_hints_rule(qi::_r1) //
|
||||
| approach_rule(qi::_r1) //
|
||||
| exclude_rule(qi::_r1);
|
||||
}
|
||||
|
||||
protected:
|
||||
@@ -179,6 +185,7 @@ struct BaseParametersGrammar : boost::spirit::qi::grammar<Iterator, Signature>
|
||||
|
||||
qi::rule<Iterator, Signature> generate_hints_rule;
|
||||
qi::rule<Iterator, Signature> approach_rule;
|
||||
qi::rule<Iterator, Signature> exclude_rule;
|
||||
|
||||
qi::rule<Iterator, osrm::engine::Bearing()> bearing_rule;
|
||||
qi::rule<Iterator, osrm::util::Coordinate()> location_rule;
|
||||
|
||||
@@ -26,6 +26,14 @@ const constexpr char *block_id_to_name[] = {"NAME_CHAR_DATA",
|
||||
"CLASSES_LIST",
|
||||
"CH_GRAPH_NODE_LIST",
|
||||
"CH_GRAPH_EDGE_LIST",
|
||||
"CH_EDGE_FILTER_0",
|
||||
"CH_EDGE_FILTER_1",
|
||||
"CH_EDGE_FILTER_2",
|
||||
"CH_EDGE_FILTER_3",
|
||||
"CH_EDGE_FILTER_4",
|
||||
"CH_EDGE_FILTER_5",
|
||||
"CH_EDGE_FILTER_6",
|
||||
"CH_EDGE_FILTER_7",
|
||||
"COORDINATE_LIST",
|
||||
"OSM_NODE_ID_LIST",
|
||||
"TURN_INSTRUCTION",
|
||||
@@ -43,7 +51,14 @@ const constexpr char *block_id_to_name[] = {"NAME_CHAR_DATA",
|
||||
"HSGR_CHECKSUM",
|
||||
"TIMESTAMP",
|
||||
"FILE_INDEX_PATH",
|
||||
"CH_CORE_MARKER",
|
||||
"CH_CORE_MARKER_0",
|
||||
"CH_CORE_MARKER_1",
|
||||
"CH_CORE_MARKER_2",
|
||||
"CH_CORE_MARKER_3",
|
||||
"CH_CORE_MARKER_4",
|
||||
"CH_CORE_MARKER_5",
|
||||
"CH_CORE_MARKER_6",
|
||||
"CH_CORE_MARKER_7",
|
||||
"DATASOURCES_NAMES",
|
||||
"PROPERTIES",
|
||||
"BEARING_CLASSID",
|
||||
@@ -62,8 +77,22 @@ const constexpr char *block_id_to_name[] = {"NAME_CHAR_DATA",
|
||||
"MLD_LEVEL_DATA",
|
||||
"MLD_PARTITION",
|
||||
"MLD_CELL_TO_CHILDREN",
|
||||
"MLD_CELL_WEIGHTS",
|
||||
"MLD_CELL_DURATIONS",
|
||||
"MLD_CELL_WEIGHTS_0",
|
||||
"MLD_CELL_WEIGHTS_1",
|
||||
"MLD_CELL_WEIGHTS_2",
|
||||
"MLD_CELL_WEIGHTS_3",
|
||||
"MLD_CELL_WEIGHTS_4",
|
||||
"MLD_CELL_WEIGHTS_5",
|
||||
"MLD_CELL_WEIGHTS_6",
|
||||
"MLD_CELL_WEIGHTS_7",
|
||||
"MLD_CELL_DURATIONS_0",
|
||||
"MLD_CELL_DURATIONS_1",
|
||||
"MLD_CELL_DURATIONS_2",
|
||||
"MLD_CELL_DURATIONS_3",
|
||||
"MLD_CELL_DURATIONS_4",
|
||||
"MLD_CELL_DURATIONS_5",
|
||||
"MLD_CELL_DURATIONS_6",
|
||||
"MLD_CELL_DURATIONS_7",
|
||||
"MLD_CELL_SOURCE_BOUNDARY",
|
||||
"MLD_CELL_DESTINATION_BOUNDARY",
|
||||
"MLD_CELLS",
|
||||
@@ -84,6 +113,14 @@ struct DataLayout
|
||||
CLASSES_LIST,
|
||||
CH_GRAPH_NODE_LIST,
|
||||
CH_GRAPH_EDGE_LIST,
|
||||
CH_EDGE_FILTER_0,
|
||||
CH_EDGE_FILTER_1,
|
||||
CH_EDGE_FILTER_2,
|
||||
CH_EDGE_FILTER_3,
|
||||
CH_EDGE_FILTER_4,
|
||||
CH_EDGE_FILTER_5,
|
||||
CH_EDGE_FILTER_6,
|
||||
CH_EDGE_FILTER_7,
|
||||
COORDINATE_LIST,
|
||||
OSM_NODE_ID_LIST,
|
||||
TURN_INSTRUCTION,
|
||||
@@ -101,7 +138,14 @@ struct DataLayout
|
||||
HSGR_CHECKSUM,
|
||||
TIMESTAMP,
|
||||
FILE_INDEX_PATH,
|
||||
CH_CORE_MARKER,
|
||||
CH_CORE_MARKER_0,
|
||||
CH_CORE_MARKER_1,
|
||||
CH_CORE_MARKER_2,
|
||||
CH_CORE_MARKER_3,
|
||||
CH_CORE_MARKER_4,
|
||||
CH_CORE_MARKER_5,
|
||||
CH_CORE_MARKER_6,
|
||||
CH_CORE_MARKER_7,
|
||||
DATASOURCES_NAMES,
|
||||
PROPERTIES,
|
||||
BEARING_CLASSID,
|
||||
@@ -120,8 +164,22 @@ struct DataLayout
|
||||
MLD_LEVEL_DATA,
|
||||
MLD_PARTITION,
|
||||
MLD_CELL_TO_CHILDREN,
|
||||
MLD_CELL_WEIGHTS,
|
||||
MLD_CELL_DURATIONS,
|
||||
MLD_CELL_WEIGHTS_0,
|
||||
MLD_CELL_WEIGHTS_1,
|
||||
MLD_CELL_WEIGHTS_2,
|
||||
MLD_CELL_WEIGHTS_3,
|
||||
MLD_CELL_WEIGHTS_4,
|
||||
MLD_CELL_WEIGHTS_5,
|
||||
MLD_CELL_WEIGHTS_6,
|
||||
MLD_CELL_WEIGHTS_7,
|
||||
MLD_CELL_DURATIONS_0,
|
||||
MLD_CELL_DURATIONS_1,
|
||||
MLD_CELL_DURATIONS_2,
|
||||
MLD_CELL_DURATIONS_3,
|
||||
MLD_CELL_DURATIONS_4,
|
||||
MLD_CELL_DURATIONS_5,
|
||||
MLD_CELL_DURATIONS_6,
|
||||
MLD_CELL_DURATIONS_7,
|
||||
MLD_CELL_SOURCE_BOUNDARY,
|
||||
MLD_CELL_DESTINATION_BOUNDARY,
|
||||
MLD_CELLS,
|
||||
@@ -151,7 +209,7 @@ struct DataLayout
|
||||
inline uint64_t GetBlockSize(BlockID bid) const
|
||||
{
|
||||
// special bit encoding
|
||||
if (bid == CH_CORE_MARKER)
|
||||
if (bid >= CH_CORE_MARKER_0 && bid <= CH_CORE_MARKER_7)
|
||||
{
|
||||
return (num_entries[bid] / 32 + 1) * entry_size[bid];
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ struct StorageConfig final : IOConfig
|
||||
".osrm.ebg_nodes",
|
||||
".osrm.core",
|
||||
".osrm.cells",
|
||||
".osrm.cell_metrics",
|
||||
".osrm.mldgr",
|
||||
".osrm.tld",
|
||||
".osrm.tls",
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "util/log.hpp"
|
||||
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <tbb/parallel_sort.h>
|
||||
#include <tbb/spin_mutex.h>
|
||||
|
||||
#include <boost/exception/diagnostic_information.hpp>
|
||||
@@ -62,9 +63,9 @@ template <typename Key, typename Value> struct CSVFilesParser
|
||||
// and unique them on key to keep only the value with the largest file index
|
||||
// and the largest line number in a file.
|
||||
// The operands order is swapped to make descending ordering on (key, source)
|
||||
std::stable_sort(begin(lookup), end(lookup), [](const auto &lhs, const auto &rhs) {
|
||||
return rhs.first < lhs.first ||
|
||||
(rhs.first == lhs.first && rhs.second.source < lhs.second.source);
|
||||
tbb::parallel_sort(begin(lookup), end(lookup), [](const auto &lhs, const auto &rhs) {
|
||||
return std::tie(rhs.first, rhs.second.source) <
|
||||
std::tie(lhs.first, lhs.second.source);
|
||||
});
|
||||
|
||||
// Unique only on key to take the source precedence into account and remove duplicates.
|
||||
|
||||
@@ -49,9 +49,9 @@ struct Segment final
|
||||
|
||||
struct SpeedSource final
|
||||
{
|
||||
SpeedSource() : speed(0), rate(std::numeric_limits<double>::quiet_NaN()) {}
|
||||
SpeedSource() : speed(0), rate() {}
|
||||
unsigned speed;
|
||||
double rate;
|
||||
boost::optional<double> rate;
|
||||
std::uint8_t source;
|
||||
};
|
||||
|
||||
|
||||
@@ -55,12 +55,11 @@ struct UpdaterConfig final : storage::IOConfig
|
||||
".osrm.edges",
|
||||
".osrm.geometry",
|
||||
".osrm.fileIndex",
|
||||
".osrm.datasource_names",
|
||||
".osrm.properties",
|
||||
".osrm.restrictions",
|
||||
},
|
||||
{},
|
||||
{})
|
||||
{".osrm.datasource_names"})
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <boost/iterator/iterator_facade.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@@ -254,9 +255,25 @@ template <typename ElementT> class DeallocatingVector
|
||||
bucket_list.emplace_back(new ElementT[ELEMENTS_PER_BLOCK]);
|
||||
}
|
||||
|
||||
// copying is not safe since this would only do a shallow copy
|
||||
DeallocatingVector(DeallocatingVector &other) = delete;
|
||||
DeallocatingVector &operator=(DeallocatingVector &other) = delete;
|
||||
// Performs a deep copy of the buckets
|
||||
DeallocatingVector(const DeallocatingVector &other)
|
||||
{
|
||||
bucket_list.resize(other.bucket_list.size());
|
||||
for (const auto index : util::irange<std::size_t>(0, bucket_list.size()))
|
||||
{
|
||||
bucket_list[index] = new ElementT[ELEMENTS_PER_BLOCK];
|
||||
std::copy_n(other.bucket_list[index], ELEMENTS_PER_BLOCK, bucket_list[index]);
|
||||
}
|
||||
current_size = other.current_size;
|
||||
}
|
||||
// Note we capture other by value
|
||||
DeallocatingVector &operator=(const DeallocatingVector &other)
|
||||
{
|
||||
auto copy_other = other;
|
||||
swap(copy_other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// moving is fine
|
||||
DeallocatingVector(DeallocatingVector &&other) { swap(other); }
|
||||
DeallocatingVector &operator=(DeallocatingVector &&other)
|
||||
|
||||
@@ -33,6 +33,27 @@ template <typename EdgeDataT, bool UseSharedMemory>
|
||||
void write(storage::io::FileWriter &writer, const DynamicGraph<EdgeDataT> &graph);
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
// These types need to live outside of DynamicGraph
|
||||
// to be not dependable. We need this for transforming graphs
|
||||
// with different data.
|
||||
|
||||
template <typename EdgeIterator> struct DynamicNode
|
||||
{
|
||||
// index of the first edge
|
||||
EdgeIterator first_edge;
|
||||
// amount of edges
|
||||
unsigned edges;
|
||||
};
|
||||
|
||||
template <typename NodeIterator, typename EdgeDataT> struct DynamicEdge
|
||||
{
|
||||
NodeIterator target;
|
||||
EdgeDataT data;
|
||||
};
|
||||
}
|
||||
|
||||
template <typename EdgeDataT> class DynamicGraph
|
||||
{
|
||||
public:
|
||||
@@ -41,6 +62,11 @@ template <typename EdgeDataT> class DynamicGraph
|
||||
using EdgeIterator = std::uint32_t;
|
||||
using EdgeRange = range<EdgeIterator>;
|
||||
|
||||
using Node = detail::DynamicNode<EdgeIterator>;
|
||||
using Edge = detail::DynamicEdge<NodeIterator, EdgeDataT>;
|
||||
|
||||
template <typename E> friend class DynamicGraph;
|
||||
|
||||
class InputEdge
|
||||
{
|
||||
public:
|
||||
@@ -66,6 +92,8 @@ template <typename EdgeDataT> class DynamicGraph
|
||||
}
|
||||
};
|
||||
|
||||
DynamicGraph() : DynamicGraph(0) {}
|
||||
|
||||
// Constructs an empty graph with a given number of nodes.
|
||||
explicit DynamicGraph(NodeIterator nodes) : number_of_nodes(nodes), number_of_edges(0)
|
||||
{
|
||||
@@ -118,6 +146,26 @@ template <typename EdgeDataT> class DynamicGraph
|
||||
}
|
||||
}
|
||||
|
||||
// Copy&move for the same data
|
||||
//
|
||||
|
||||
DynamicGraph(const DynamicGraph &other)
|
||||
{
|
||||
number_of_nodes = other.number_of_nodes;
|
||||
// atomics can't be moved this is why we need an own constructor
|
||||
number_of_edges = static_cast<std::uint32_t>(other.number_of_edges);
|
||||
|
||||
node_array = other.node_array;
|
||||
edge_list = other.edge_list;
|
||||
}
|
||||
|
||||
DynamicGraph &operator=(const DynamicGraph &other)
|
||||
{
|
||||
auto copy_other = other;
|
||||
*this = std::move(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
DynamicGraph(DynamicGraph &&other)
|
||||
{
|
||||
number_of_nodes = other.number_of_nodes;
|
||||
@@ -140,6 +188,38 @@ template <typename EdgeDataT> class DynamicGraph
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Removes all edges to and from nodes for which filter(node_id) returns false
|
||||
template <typename Pred> auto Filter(Pred filter) const &
|
||||
{
|
||||
DynamicGraph other;
|
||||
|
||||
other.number_of_nodes = number_of_nodes;
|
||||
other.number_of_edges = static_cast<std::uint32_t>(number_of_edges);
|
||||
other.edge_list.reserve(edge_list.size());
|
||||
other.node_array.resize(node_array.size());
|
||||
|
||||
NodeID node_id = 0;
|
||||
std::transform(
|
||||
node_array.begin(), node_array.end(), other.node_array.begin(), [&](const Node &node) {
|
||||
const EdgeIterator first_edge = other.edge_list.size();
|
||||
if (filter(node_id++))
|
||||
{
|
||||
std::copy_if(edge_list.begin() + node.first_edge,
|
||||
edge_list.begin() + node.first_edge + node.edges,
|
||||
std::back_inserter(other.edge_list),
|
||||
[&](const auto &edge) { return filter(edge.target); });
|
||||
const unsigned num_edges = other.edge_list.size() - first_edge;
|
||||
return Node{first_edge, num_edges};
|
||||
}
|
||||
else
|
||||
{
|
||||
return Node{first_edge, 0};
|
||||
}
|
||||
});
|
||||
|
||||
return other;
|
||||
}
|
||||
|
||||
unsigned GetNumberOfNodes() const { return number_of_nodes; }
|
||||
|
||||
unsigned GetNumberOfEdges() const { return number_of_edges; }
|
||||
@@ -347,19 +427,28 @@ template <typename EdgeDataT> class DynamicGraph
|
||||
for (auto edge : GetAdjacentEdgeRange(node))
|
||||
{
|
||||
edge_list[edge].target = old_to_new_node[edge_list[edge].target];
|
||||
old_to_new_edge[edge] = new_edge_index++;
|
||||
}
|
||||
// and all adjacent empty edges
|
||||
for (auto edge = EndEdges(node); edge < number_of_edges && isDummy(edge); edge++)
|
||||
{
|
||||
BOOST_ASSERT(edge_list[edge].target != SPECIAL_NODEID);
|
||||
old_to_new_edge[edge] = new_edge_index++;
|
||||
}
|
||||
node_array[node].first_edge = new_first_edge;
|
||||
}
|
||||
auto number_of_valid_edges = new_edge_index;
|
||||
|
||||
// move all dummy edges to the end of the renumbered range
|
||||
for (auto edge : util::irange<NodeID>(0, edge_list.size()))
|
||||
{
|
||||
if (old_to_new_edge[edge] == SPECIAL_EDGEID)
|
||||
{
|
||||
BOOST_ASSERT(isDummy(edge));
|
||||
old_to_new_edge[edge] = new_edge_index++;
|
||||
}
|
||||
}
|
||||
BOOST_ASSERT(std::find(old_to_new_edge.begin(), old_to_new_edge.end(), SPECIAL_EDGEID) ==
|
||||
old_to_new_edge.end());
|
||||
|
||||
util::inplacePermutation(edge_list.begin(), edge_list.end(), old_to_new_edge);
|
||||
// Remove useless dummy nodes at the end
|
||||
edge_list.resize(number_of_valid_edges);
|
||||
number_of_edges = number_of_valid_edges;
|
||||
}
|
||||
|
||||
protected:
|
||||
@@ -373,20 +462,6 @@ template <typename EdgeDataT> class DynamicGraph
|
||||
edge_list[edge].target = (std::numeric_limits<NodeIterator>::max)();
|
||||
}
|
||||
|
||||
struct Node
|
||||
{
|
||||
// index of the first edge
|
||||
EdgeIterator first_edge;
|
||||
// amount of edges
|
||||
unsigned edges;
|
||||
};
|
||||
|
||||
struct Edge
|
||||
{
|
||||
NodeIterator target;
|
||||
EdgeDataT data;
|
||||
};
|
||||
|
||||
NodeIterator number_of_nodes;
|
||||
std::atomic_uint number_of_edges;
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
#ifndef OSRM_UTIL_EXCLUDE_FLAG_HPP
|
||||
#define OSRM_UTIL_EXCLUDE_FLAG_HPP
|
||||
|
||||
#include "extractor/node_data_container.hpp"
|
||||
#include "extractor/profile_properties.hpp"
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
|
||||
inline std::vector<std::vector<bool>>
|
||||
excludeFlagsToNodeFilter(const NodeID number_of_nodes,
|
||||
const extractor::EdgeBasedNodeDataContainer &node_data,
|
||||
const extractor::ProfileProperties &properties)
|
||||
{
|
||||
std::vector<std::vector<bool>> filters;
|
||||
for (auto mask : properties.excludable_classes)
|
||||
{
|
||||
if (mask != extractor::INAVLID_CLASS_DATA)
|
||||
{
|
||||
std::vector<bool> allowed_nodes(number_of_nodes);
|
||||
for (const auto node : util::irange<NodeID>(0, number_of_nodes))
|
||||
{
|
||||
allowed_nodes[node] = (node_data.GetClassData(node) & mask) == 0;
|
||||
}
|
||||
filters.push_back(std::move(allowed_nodes));
|
||||
}
|
||||
}
|
||||
return filters;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,165 @@
|
||||
#ifndef OSRM_UTIL_FILTERED_GRAPH_HPP
|
||||
#define OSRM_UTIL_FILTERED_GRAPH_HPP
|
||||
|
||||
#include "storage/shared_memory_ownership.hpp"
|
||||
|
||||
#include "util/dynamic_graph.hpp"
|
||||
#include "util/filtered_integer_range.hpp"
|
||||
#include "util/static_graph.hpp"
|
||||
#include "util/vector_view.hpp"
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
template <typename GraphT, storage::Ownership Ownership> class FilteredGraphImpl;
|
||||
|
||||
// For static graphs we can save the filters as a static vector since
|
||||
// we don't modify the structure of the graph. This also makes it easy to
|
||||
// swap out the filter.
|
||||
template <typename EdgeDataT, storage::Ownership Ownership>
|
||||
class FilteredGraphImpl<util::StaticGraph<EdgeDataT, Ownership>, Ownership>
|
||||
{
|
||||
template <typename T> using Vector = util::ViewOrVector<T, Ownership>;
|
||||
|
||||
public:
|
||||
using Graph = util::StaticGraph<EdgeDataT, Ownership>;
|
||||
using EdgeIterator = typename Graph::EdgeIterator;
|
||||
using NodeIterator = typename Graph::NodeIterator;
|
||||
using NodeArrayEntry = typename Graph::NodeArrayEntry;
|
||||
using EdgeArrayEntry = typename Graph::EdgeArrayEntry;
|
||||
using EdgeRange = util::filtered_range<EdgeIterator, Vector<bool>>;
|
||||
|
||||
unsigned GetNumberOfNodes() const { return graph.GetNumberOfNodes(); }
|
||||
|
||||
unsigned GetNumberOfEdges() const { return graph.GetNumberOfEdges(); }
|
||||
|
||||
unsigned GetOutDegree(const NodeIterator n) const
|
||||
{
|
||||
auto range = graph.GetAdjacentEdgeRange(n);
|
||||
return std::count_if(range.begin(), range.end(), [this](const EdgeIterator edge) {
|
||||
return edge_filter[edge];
|
||||
});
|
||||
}
|
||||
|
||||
inline NodeIterator GetTarget(const EdgeIterator e) const
|
||||
{
|
||||
BOOST_ASSERT(edge_filter[e]);
|
||||
return graph.GetTarget(e);
|
||||
}
|
||||
|
||||
auto &GetEdgeData(const EdgeIterator e)
|
||||
{
|
||||
BOOST_ASSERT(edge_filter[e]);
|
||||
return graph.GetEdgeData(e);
|
||||
}
|
||||
|
||||
const auto &GetEdgeData(const EdgeIterator e) const
|
||||
{
|
||||
BOOST_ASSERT(edge_filter[e]);
|
||||
return graph.GetEdgeData(e);
|
||||
}
|
||||
|
||||
auto GetAdjacentEdgeRange(const NodeIterator n) const
|
||||
{
|
||||
return EdgeRange{graph.BeginEdges(n), graph.EndEdges(n), edge_filter};
|
||||
}
|
||||
|
||||
// searches for a specific edge
|
||||
EdgeIterator FindEdge(const NodeIterator from, const NodeIterator to) const
|
||||
{
|
||||
for (const auto edge : GetAdjacentEdgeRange(from))
|
||||
{
|
||||
if (to == GetTarget(edge))
|
||||
{
|
||||
return edge;
|
||||
}
|
||||
}
|
||||
return SPECIAL_EDGEID;
|
||||
}
|
||||
|
||||
template <typename FilterFunction>
|
||||
EdgeIterator
|
||||
FindSmallestEdge(const NodeIterator from, const NodeIterator to, FilterFunction &&filter) const
|
||||
{
|
||||
static_assert(traits::HasDataMember<typename Graph::EdgeArrayEntry>::value,
|
||||
"Filtering on .data not possible without .data member attribute");
|
||||
|
||||
EdgeIterator smallest_edge = SPECIAL_EDGEID;
|
||||
EdgeWeight smallest_weight = INVALID_EDGE_WEIGHT;
|
||||
for (auto edge : GetAdjacentEdgeRange(from))
|
||||
{
|
||||
const NodeID target = GetTarget(edge);
|
||||
const auto &data = GetEdgeData(edge);
|
||||
if (target == to && data.weight < smallest_weight &&
|
||||
std::forward<FilterFunction>(filter)(data))
|
||||
{
|
||||
smallest_edge = edge;
|
||||
smallest_weight = data.weight;
|
||||
}
|
||||
}
|
||||
return smallest_edge;
|
||||
}
|
||||
|
||||
EdgeIterator FindEdgeInEitherDirection(const NodeIterator from, const NodeIterator to) const
|
||||
{
|
||||
EdgeIterator tmp = FindEdge(from, to);
|
||||
return (SPECIAL_NODEID != tmp ? tmp : FindEdge(to, from));
|
||||
}
|
||||
|
||||
EdgeIterator
|
||||
FindEdgeIndicateIfReverse(const NodeIterator from, const NodeIterator to, bool &result) const
|
||||
{
|
||||
EdgeIterator current_iterator = FindEdge(from, to);
|
||||
if (SPECIAL_NODEID == current_iterator)
|
||||
{
|
||||
current_iterator = FindEdge(to, from);
|
||||
if (SPECIAL_NODEID != current_iterator)
|
||||
{
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
return current_iterator;
|
||||
}
|
||||
|
||||
FilteredGraphImpl() = default;
|
||||
|
||||
FilteredGraphImpl(Graph graph, Vector<bool> edge_filter_)
|
||||
: graph(std::move(graph)), edge_filter(std::move(edge_filter_))
|
||||
{
|
||||
BOOST_ASSERT(edge_filter.empty() || edge_filter.size() == graph.GetNumberOfEdges());
|
||||
}
|
||||
|
||||
// Takes a graph and a function that maps EdgeID to true
|
||||
// if the edge should be included in the graph.
|
||||
template <typename Pred>
|
||||
FilteredGraphImpl(Graph graph, Pred filter)
|
||||
: graph(std::move(graph)), edge_filter(graph.GetNumberOfEdges())
|
||||
{
|
||||
auto edge_ids = util::irange<EdgeID>(0, graph.GetNumberOfEdges());
|
||||
std::transform(edge_ids.begin(), edge_ids.end(), edge_filter.begin(), filter);
|
||||
}
|
||||
|
||||
void Renumber(const std::vector<NodeID> &old_to_new_node)
|
||||
{
|
||||
graph.Renumber(old_to_new_node);
|
||||
// FIXME the edge filter needs to be renumbered with a different permutation
|
||||
// util::inplacePermutation(edge_filter.begin(), edge_filter.end(), old_to_new_node);
|
||||
}
|
||||
|
||||
private:
|
||||
Graph graph;
|
||||
Vector<bool> edge_filter;
|
||||
};
|
||||
}
|
||||
|
||||
template <typename GraphT>
|
||||
using FilteredGraphContainer = detail::FilteredGraphImpl<GraphT, storage::Ownership::Container>;
|
||||
template <typename GraphT>
|
||||
using FilteredGraphView = detail::FilteredGraphImpl<GraphT, storage::Ownership::View>;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,101 @@
|
||||
#ifndef FILTERED_INTEGER_RANGE_HPP
|
||||
#define FILTERED_INTEGER_RANGE_HPP
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/iterator/iterator_facade.hpp>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
|
||||
// This implements a single-pass integer range.
|
||||
// We need our own implementation here because using boost::adaptor::filtered() has
|
||||
// the problem that the return-type depends on the lambda-type you pass into the function.
|
||||
// That makes it unsuitable to use in interface where we would expect all filtered ranges
|
||||
// to be off the same type.
|
||||
|
||||
template <typename Integer, typename Filter>
|
||||
class filtered_integer_iterator
|
||||
: public boost::iterator_facade<filtered_integer_iterator<Integer, Filter>,
|
||||
Integer,
|
||||
boost::single_pass_traversal_tag,
|
||||
Integer>
|
||||
{
|
||||
typedef boost::iterator_facade<filtered_integer_iterator<Integer, Filter>,
|
||||
Integer,
|
||||
boost::single_pass_traversal_tag,
|
||||
Integer>
|
||||
base_t;
|
||||
|
||||
public:
|
||||
typedef typename base_t::value_type value_type;
|
||||
typedef typename base_t::difference_type difference_type;
|
||||
typedef typename base_t::reference reference;
|
||||
typedef std::random_access_iterator_tag iterator_category;
|
||||
|
||||
filtered_integer_iterator() : value(), filter(nullptr) {}
|
||||
explicit filtered_integer_iterator(value_type x, value_type end_value, const Filter *filter)
|
||||
: value(x), end_value(end_value), filter(filter)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
void increment()
|
||||
{
|
||||
do
|
||||
{
|
||||
++value;
|
||||
} while (value < end_value && !(*filter)[value]);
|
||||
}
|
||||
bool equal(const filtered_integer_iterator &other) const { return value == other.value; }
|
||||
reference dereference() const { return value; }
|
||||
|
||||
friend class ::boost::iterator_core_access;
|
||||
value_type value;
|
||||
value_type end_value;
|
||||
const Filter *filter;
|
||||
};
|
||||
|
||||
template <typename Integer, typename Filter> class filtered_range
|
||||
{
|
||||
public:
|
||||
typedef filtered_integer_iterator<Integer, Filter> const_iterator;
|
||||
typedef filtered_integer_iterator<Integer, Filter> iterator;
|
||||
|
||||
filtered_range(Integer begin, Integer end, const Filter &filter) : last(end, end, &filter)
|
||||
{
|
||||
while (begin < end && !filter[begin])
|
||||
{
|
||||
begin++;
|
||||
}
|
||||
|
||||
iter = iterator(begin, end, &filter);
|
||||
}
|
||||
|
||||
iterator begin() const noexcept { return iter; }
|
||||
iterator end() const noexcept { return last; }
|
||||
|
||||
private:
|
||||
iterator iter;
|
||||
iterator last;
|
||||
};
|
||||
|
||||
// convenience function to construct an integer range with type deduction
|
||||
template <typename Integer, typename Filter>
|
||||
filtered_range<Integer, Filter>
|
||||
filtered_irange(const Integer first,
|
||||
const Integer last,
|
||||
const Filter &filter,
|
||||
typename std::enable_if<std::is_integral<Integer>::value>::type * = 0) noexcept
|
||||
{
|
||||
return filtered_range<Integer, Filter>(first, last, filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // INTEGER_RANGE_HPP
|
||||
+10
-3
@@ -7,9 +7,10 @@
|
||||
|
||||
enum LogLevel
|
||||
{
|
||||
logINFO,
|
||||
logWARNING,
|
||||
logNONE,
|
||||
logERROR,
|
||||
logWARNING,
|
||||
logINFO,
|
||||
logDEBUG
|
||||
};
|
||||
|
||||
@@ -27,14 +28,20 @@ class LogPolicy
|
||||
|
||||
bool IsMute() const;
|
||||
|
||||
LogLevel GetLevel() const;
|
||||
void SetLevel(LogLevel level);
|
||||
void SetLevel(std::string const &level);
|
||||
|
||||
static LogPolicy &GetInstance();
|
||||
static std::string GetLevels();
|
||||
|
||||
LogPolicy(const LogPolicy &) = delete;
|
||||
LogPolicy &operator=(const LogPolicy &) = delete;
|
||||
|
||||
private:
|
||||
LogPolicy() : m_is_mute(true) {}
|
||||
LogPolicy() : m_is_mute(true), m_level(logINFO) {}
|
||||
std::atomic<bool> m_is_mute;
|
||||
LogLevel m_level;
|
||||
};
|
||||
|
||||
class Log
|
||||
|
||||
@@ -152,6 +152,7 @@ class QueryHeap
|
||||
|
||||
void Insert(NodeID node, Weight weight, const Data &data)
|
||||
{
|
||||
BOOST_ASSERT(node < std::numeric_limits<NodeID>::max());
|
||||
const auto index = static_cast<Key>(inserted_nodes.size());
|
||||
const auto handle = heap.push(std::make_pair(weight, index));
|
||||
inserted_nodes.emplace_back(HeapNode{handle, node, weight, data});
|
||||
@@ -180,7 +181,16 @@ class QueryHeap
|
||||
{
|
||||
BOOST_ASSERT(WasInserted(node));
|
||||
const Key index = node_index.peek_index(node);
|
||||
return inserted_nodes[index].handle == HeapHandle{};
|
||||
|
||||
// Use end iterator as a reliable "non-existent" handle.
|
||||
// Default-constructed handles are singular and
|
||||
// can only be checked-compared to another singular instance.
|
||||
// Behaviour investigated at https://lists.boost.org/boost-users/2017/08/87787.php,
|
||||
// eventually confirmation at https://stackoverflow.com/a/45622940/151641.
|
||||
// Corrected in https://github.com/Project-OSRM/osrm-backend/pull/4396
|
||||
auto const end_it = const_cast<HeapContainer &>(heap).end(); // non-const iterator
|
||||
auto const none_handle = heap.s_handle_from_iterator(end_it); // from non-const iterator
|
||||
return inserted_nodes[index].handle == none_handle;
|
||||
}
|
||||
|
||||
bool WasInserted(const NodeID node) const
|
||||
@@ -210,14 +220,15 @@ class QueryHeap
|
||||
BOOST_ASSERT(!heap.empty());
|
||||
const Key removedIndex = heap.top().second;
|
||||
heap.pop();
|
||||
inserted_nodes[removedIndex].handle = HeapHandle{};
|
||||
inserted_nodes[removedIndex].handle = heap.s_handle_from_iterator(heap.end());
|
||||
return inserted_nodes[removedIndex].node;
|
||||
}
|
||||
|
||||
void DeleteAll()
|
||||
{
|
||||
std::for_each(inserted_nodes.begin(), inserted_nodes.end(), [](auto &node) {
|
||||
node.handle = HeapHandle();
|
||||
auto const none_handle = heap.s_handle_from_iterator(heap.end());
|
||||
std::for_each(inserted_nodes.begin(), inserted_nodes.end(), [&none_handle](auto &node) {
|
||||
node.handle = none_handle;
|
||||
});
|
||||
heap.clear();
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "osrm",
|
||||
"version": "5.12.0-roundaboutexits.1",
|
||||
"version": "5.12.0-rc.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": {
|
||||
|
||||
@@ -100,6 +100,17 @@ function setup()
|
||||
'vehicle'
|
||||
},
|
||||
|
||||
classes = Sequence {
|
||||
'toll', 'motorway', 'ferry', 'restricted'
|
||||
},
|
||||
|
||||
-- classes to support for exclude flags
|
||||
excludable = Sequence {
|
||||
Set {'toll'},
|
||||
Set {'motorway'},
|
||||
Set {'ferry'}
|
||||
},
|
||||
|
||||
avoid = Set {
|
||||
'area',
|
||||
-- 'toll', -- uncomment this to avoid tolls
|
||||
|
||||
@@ -19,6 +19,14 @@ function setup()
|
||||
use_turn_restrictions = true
|
||||
},
|
||||
|
||||
classes = {"motorway", "toll", "TooWords2"},
|
||||
|
||||
excludable = {
|
||||
{["motorway"] = true},
|
||||
{["toll"] = true},
|
||||
{["motorway"] = true, ["toll"] = true}
|
||||
},
|
||||
|
||||
default_speed = 24,
|
||||
speeds = {
|
||||
primary = 36,
|
||||
@@ -40,6 +48,7 @@ end
|
||||
|
||||
function process_way (profile, way, result)
|
||||
local highway = way:get_value_by_key("highway")
|
||||
local toll = way:get_value_by_key("toll")
|
||||
local name = way:get_value_by_key("name")
|
||||
local oneway = way:get_value_by_key("oneway")
|
||||
local route = way:get_value_by_key("route")
|
||||
@@ -103,6 +112,16 @@ function process_way (profile, way, result)
|
||||
result.backward_mode = mode.inaccessible
|
||||
end
|
||||
|
||||
if highway == 'motorway' then
|
||||
result.forward_classes["motorway"] = true
|
||||
result.backward_classes["motorway"] = true
|
||||
end
|
||||
|
||||
if toll == "yes" then
|
||||
result.forward_classes["toll"] = true
|
||||
result.backward_classes["toll"] = true
|
||||
end
|
||||
|
||||
if junction == 'roundabout' then
|
||||
result.roundabout = true
|
||||
end
|
||||
|
||||
@@ -10,7 +10,7 @@ set -o nounset
|
||||
# http://git.661346.n2.nabble.com/subtree-merges-lose-prefix-after-rebase-td7332850.html
|
||||
|
||||
OSMIUM_REPO="https://github.com/osmcode/libosmium.git"
|
||||
OSMIUM_TAG=v2.11.3
|
||||
OSMIUM_TAG=v2.13.1
|
||||
|
||||
VARIANT_REPO="https://github.com/mapbox/variant.git"
|
||||
VARIANT_TAG=v1.1.3
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#include "contractor/contractor.hpp"
|
||||
#include "contractor/contract_excludable_graph.hpp"
|
||||
#include "contractor/contracted_edge_container.hpp"
|
||||
#include "contractor/crc32_processor.hpp"
|
||||
#include "contractor/files.hpp"
|
||||
#include "contractor/graph_contractor.hpp"
|
||||
@@ -6,6 +8,7 @@
|
||||
|
||||
#include "extractor/compressed_edge_container.hpp"
|
||||
#include "extractor/edge_based_graph_factory.hpp"
|
||||
#include "extractor/files.hpp"
|
||||
#include "extractor/node_based_edge.hpp"
|
||||
|
||||
#include "storage/io.hpp"
|
||||
@@ -14,6 +17,8 @@
|
||||
|
||||
#include "util/exception.hpp"
|
||||
#include "util/exception_utils.hpp"
|
||||
#include "util/exclude_flag.hpp"
|
||||
#include "util/filtered_graph.hpp"
|
||||
#include "util/graph_loader.hpp"
|
||||
#include "util/integer_range.hpp"
|
||||
#include "util/log.hpp"
|
||||
@@ -42,6 +47,11 @@ int Contractor::Run()
|
||||
throw util::exception("Core factor must be between 0.0 to 1.0 (inclusive)" + SOURCE_REF);
|
||||
}
|
||||
|
||||
if (config.use_cached_priority)
|
||||
{
|
||||
util::Log(logWARNING) << "Using cached priorities is deprecated and they will be ignored.";
|
||||
}
|
||||
|
||||
TIMER_START(preparing);
|
||||
|
||||
util::Log() << "Reading node weights.";
|
||||
@@ -63,43 +73,36 @@ int Contractor::Run()
|
||||
// Contracting the edge-expanded graph
|
||||
|
||||
TIMER_START(contraction);
|
||||
std::vector<bool> is_core_node;
|
||||
std::vector<float> node_levels;
|
||||
if (config.use_cached_priority)
|
||||
|
||||
std::vector<std::vector<bool>> node_filters;
|
||||
{
|
||||
files::readLevels(config.GetPath(".osrm.level"), node_levels);
|
||||
extractor::EdgeBasedNodeDataContainer node_data;
|
||||
extractor::files::readNodeData(config.GetPath(".osrm.ebg_nodes"), node_data);
|
||||
|
||||
extractor::ProfileProperties properties;
|
||||
extractor::files::readProfileProperties(config.GetPath(".osrm.properties"), properties);
|
||||
|
||||
node_filters = util::excludeFlagsToNodeFilter(max_edge_id + 1, node_data, properties);
|
||||
}
|
||||
|
||||
util::DeallocatingVector<QueryEdge> contracted_edge_list;
|
||||
{ // own scope to not keep the contractor around
|
||||
GraphContractor graph_contractor(max_edge_id + 1,
|
||||
adaptToContractorInput(std::move(edge_based_edge_list)),
|
||||
std::move(node_levels),
|
||||
std::move(node_weights));
|
||||
graph_contractor.Run(config.core_factor);
|
||||
RangebasedCRC32 crc32_calculator;
|
||||
const unsigned checksum = crc32_calculator(edge_based_edge_list);
|
||||
|
||||
contracted_edge_list = graph_contractor.GetEdges<QueryEdge>();
|
||||
is_core_node = graph_contractor.GetCoreMarker();
|
||||
node_levels = graph_contractor.GetNodeLevels();
|
||||
}
|
||||
QueryGraph query_graph;
|
||||
std::vector<std::vector<bool>> edge_filters;
|
||||
std::vector<std::vector<bool>> cores;
|
||||
std::tie(query_graph, edge_filters, cores) =
|
||||
contractExcludableGraph(toContractorGraph(max_edge_id + 1, std::move(edge_based_edge_list)),
|
||||
std::move(node_weights),
|
||||
std::move(node_filters),
|
||||
config.core_factor);
|
||||
TIMER_STOP(contraction);
|
||||
|
||||
util::Log() << "Contracted graph has " << query_graph.GetNumberOfEdges() << " edges.";
|
||||
util::Log() << "Contraction took " << TIMER_SEC(contraction) << " sec";
|
||||
|
||||
{
|
||||
RangebasedCRC32 crc32_calculator;
|
||||
const unsigned checksum = crc32_calculator(contracted_edge_list);
|
||||
files::writeGraph(config.GetPath(".osrm.hsgr"), checksum, query_graph, edge_filters);
|
||||
|
||||
files::writeGraph(config.GetPath(".osrm.hsgr"),
|
||||
checksum,
|
||||
QueryGraph{max_edge_id + 1, std::move(contracted_edge_list)});
|
||||
}
|
||||
|
||||
files::writeCoreMarker(config.GetPath(".osrm.core"), is_core_node);
|
||||
if (!config.use_cached_priority)
|
||||
{
|
||||
files::writeLevels(config.GetPath(".osrm.level"), node_levels);
|
||||
}
|
||||
files::writeCoreMarker(config.GetPath(".osrm.core"), cores);
|
||||
|
||||
TIMER_STOP(preparing);
|
||||
|
||||
|
||||
@@ -1,23 +1,65 @@
|
||||
#include "contractor/contractor_dijkstra.hpp"
|
||||
#include "contractor/contractor_search.hpp"
|
||||
|
||||
#include "contractor/contractor_graph.hpp"
|
||||
#include "contractor/contractor_heap.hpp"
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace contractor
|
||||
{
|
||||
|
||||
ContractorDijkstra::ContractorDijkstra(const std::size_t heap_size) : heap(heap_size) {}
|
||||
namespace
|
||||
{
|
||||
void relaxNode(ContractorHeap &heap,
|
||||
const ContractorGraph &graph,
|
||||
const NodeID node,
|
||||
const EdgeWeight node_weight,
|
||||
const NodeID forbidden_node)
|
||||
{
|
||||
const short current_hop = heap.GetData(node).hop + 1;
|
||||
for (auto edge : graph.GetAdjacentEdgeRange(node))
|
||||
{
|
||||
const auto &data = graph.GetEdgeData(edge);
|
||||
if (!data.forward)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const NodeID to = graph.GetTarget(edge);
|
||||
BOOST_ASSERT(to != SPECIAL_NODEID);
|
||||
if (forbidden_node == to)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const EdgeWeight to_weight = node_weight + data.weight;
|
||||
|
||||
void ContractorDijkstra::Run(const unsigned number_of_targets,
|
||||
const int node_limit,
|
||||
const EdgeWeight weight_limit,
|
||||
const NodeID forbidden_node,
|
||||
const ContractorGraph &graph)
|
||||
// New Node discovered -> Add to Heap + Node Info Storage
|
||||
if (!heap.WasInserted(to))
|
||||
{
|
||||
heap.Insert(to, to_weight, ContractorHeapData{current_hop, false});
|
||||
}
|
||||
// Found a shorter Path -> Update weight
|
||||
else if (to_weight < heap.GetKey(to))
|
||||
{
|
||||
heap.DecreaseKey(to, to_weight);
|
||||
heap.GetData(to).hop = current_hop;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void search(ContractorHeap &heap,
|
||||
const ContractorGraph &graph,
|
||||
const unsigned number_of_targets,
|
||||
const int node_limit,
|
||||
const EdgeWeight weight_limit,
|
||||
const NodeID forbidden_node)
|
||||
{
|
||||
int nodes = 0;
|
||||
unsigned number_of_targets_found = 0;
|
||||
while (!heap.Empty())
|
||||
{
|
||||
const NodeID node = heap.DeleteMin();
|
||||
BOOST_ASSERT(node != SPECIAL_NODEID);
|
||||
const auto node_weight = heap.GetKey(node);
|
||||
if (++nodes > node_limit)
|
||||
{
|
||||
@@ -38,59 +80,8 @@ void ContractorDijkstra::Run(const unsigned number_of_targets,
|
||||
}
|
||||
}
|
||||
|
||||
RelaxNode(node, node_weight, forbidden_node, graph);
|
||||
relaxNode(heap, graph, node, node_weight, forbidden_node);
|
||||
}
|
||||
}
|
||||
|
||||
void ContractorDijkstra::RelaxNode(const NodeID node,
|
||||
const EdgeWeight node_weight,
|
||||
const NodeID forbidden_node,
|
||||
const ContractorGraph &graph)
|
||||
{
|
||||
const short current_hop = heap.GetData(node).hop + 1;
|
||||
for (auto edge : graph.GetAdjacentEdgeRange(node))
|
||||
{
|
||||
const ContractorEdgeData &data = graph.GetEdgeData(edge);
|
||||
if (!data.forward)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const NodeID to = graph.GetTarget(edge);
|
||||
if (forbidden_node == to)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const EdgeWeight to_weight = node_weight + data.weight;
|
||||
|
||||
// New Node discovered -> Add to Heap + Node Info Storage
|
||||
if (!heap.WasInserted(to))
|
||||
{
|
||||
heap.Insert(to, to_weight, ContractorHeapData{current_hop, false});
|
||||
}
|
||||
// Found a shorter Path -> Update weight
|
||||
else if (to_weight < GetKey(to))
|
||||
{
|
||||
heap.DecreaseKey(to, to_weight);
|
||||
heap.GetData(to).hop = current_hop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ContractorDijkstra::Clear() { heap.Clear(); }
|
||||
|
||||
bool ContractorDijkstra::WasInserted(const NodeID node) const { return heap.WasInserted(node); }
|
||||
|
||||
void ContractorDijkstra::Insert(const NodeID node,
|
||||
const ContractorHeap::WeightType weight,
|
||||
const ContractorHeap::DataType &data)
|
||||
{
|
||||
heap.Insert(node, weight, data);
|
||||
}
|
||||
|
||||
ContractorHeap::WeightType ContractorDijkstra::GetKey(const NodeID node)
|
||||
{
|
||||
return heap.GetKey(node);
|
||||
}
|
||||
|
||||
} // namespace contractor
|
||||
} // namespace osrm
|
||||
+601
-428
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,9 @@
|
||||
#include "customizer/customizer.hpp"
|
||||
#include "extractor/node_data_container.hpp"
|
||||
|
||||
#include "customizer/cell_customizer.hpp"
|
||||
#include "customizer/customizer.hpp"
|
||||
#include "customizer/edge_based_graph.hpp"
|
||||
#include "customizer/files.hpp"
|
||||
|
||||
#include "partition/cell_storage.hpp"
|
||||
#include "partition/edge_based_graph_reader.hpp"
|
||||
@@ -11,6 +14,7 @@
|
||||
|
||||
#include "updater/updater.hpp"
|
||||
|
||||
#include "util/exclude_flag.hpp"
|
||||
#include "util/log.hpp"
|
||||
#include "util/timing_util.hpp"
|
||||
|
||||
@@ -19,10 +23,13 @@ namespace osrm
|
||||
namespace customizer
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
template <typename Graph, typename Partition, typename CellStorage>
|
||||
void CellStorageStatistics(const Graph &graph,
|
||||
const Partition &partition,
|
||||
const CellStorage &storage)
|
||||
const CellStorage &storage,
|
||||
const CellMetric &metric)
|
||||
{
|
||||
util::Log() << "Cells statistics per level";
|
||||
|
||||
@@ -38,7 +45,7 @@ void CellStorageStatistics(const Graph &graph,
|
||||
std::size_t invalid_sources = 0, invalid_destinations = 0;
|
||||
for (std::uint32_t cell_id = 0; cell_id < partition.GetNumberOfCells(level); ++cell_id)
|
||||
{
|
||||
const auto &cell = storage.GetCell(level, cell_id);
|
||||
const auto &cell = storage.GetCell(metric, level, cell_id);
|
||||
source += cell.GetSourceNodes().size();
|
||||
destination += cell.GetDestinationNodes().size();
|
||||
total += cell_nodes[cell_id];
|
||||
@@ -83,16 +90,29 @@ auto LoadAndUpdateEdgeExpandedGraph(const CustomizationConfig &config,
|
||||
auto directed = partition::splitBidirectionalEdges(edge_based_edge_list);
|
||||
auto tidied =
|
||||
partition::prepareEdgesForUsageInGraph<StaticEdgeBasedGraphEdge>(std::move(directed));
|
||||
auto edge_based_graph =
|
||||
std::make_unique<customizer::MultiLevelEdgeBasedGraph>(mlp, num_nodes, std::move(tidied));
|
||||
|
||||
util::Log() << "Loaded edge based graph for mapping partition ids: "
|
||||
<< edge_based_graph->GetNumberOfEdges() << " edges, "
|
||||
<< edge_based_graph->GetNumberOfNodes() << " nodes";
|
||||
auto edge_based_graph = customizer::MultiLevelEdgeBasedGraph(mlp, num_nodes, std::move(tidied));
|
||||
|
||||
return edge_based_graph;
|
||||
}
|
||||
|
||||
std::vector<CellMetric> customizeFilteredMetrics(const MultiLevelEdgeBasedGraph &graph,
|
||||
const partition::CellStorage &storage,
|
||||
const CellCustomizer &customizer,
|
||||
const std::vector<std::vector<bool>> &node_filters)
|
||||
{
|
||||
std::vector<CellMetric> metrics;
|
||||
|
||||
for (auto filter : node_filters)
|
||||
{
|
||||
auto metric = storage.MakeMetric();
|
||||
customizer.Customize(graph, storage, filter, metric);
|
||||
metrics.push_back(std::move(metric));
|
||||
}
|
||||
|
||||
return metrics;
|
||||
}
|
||||
}
|
||||
|
||||
int Customizer::Run(const CustomizationConfig &config)
|
||||
{
|
||||
TIMER_START(loading_data);
|
||||
@@ -100,30 +120,42 @@ int Customizer::Run(const CustomizationConfig &config)
|
||||
partition::MultiLevelPartition mlp;
|
||||
partition::files::readPartition(config.GetPath(".osrm.partition"), mlp);
|
||||
|
||||
auto edge_based_graph = LoadAndUpdateEdgeExpandedGraph(config, mlp);
|
||||
auto graph = LoadAndUpdateEdgeExpandedGraph(config, mlp);
|
||||
util::Log() << "Loaded edge based graph: " << graph.GetNumberOfEdges() << " edges, "
|
||||
<< graph.GetNumberOfNodes() << " nodes";
|
||||
|
||||
partition::CellStorage storage;
|
||||
partition::files::readCells(config.GetPath(".osrm.cells"), storage);
|
||||
TIMER_STOP(loading_data);
|
||||
|
||||
extractor::EdgeBasedNodeDataContainer node_data;
|
||||
extractor::files::readNodeData(config.GetPath(".osrm.ebg_nodes"), node_data);
|
||||
|
||||
extractor::ProfileProperties properties;
|
||||
extractor::files::readProfileProperties(config.GetPath(".osrm.properties"), properties);
|
||||
|
||||
util::Log() << "Loading partition data took " << TIMER_SEC(loading_data) << " seconds";
|
||||
|
||||
TIMER_START(cell_customize);
|
||||
CellCustomizer customizer(mlp);
|
||||
customizer.Customize(*edge_based_graph, storage);
|
||||
auto filter = util::excludeFlagsToNodeFilter(graph.GetNumberOfNodes(), node_data, properties);
|
||||
auto metrics = customizeFilteredMetrics(graph, storage, CellCustomizer{mlp}, filter);
|
||||
TIMER_STOP(cell_customize);
|
||||
util::Log() << "Cells customization took " << TIMER_SEC(cell_customize) << " seconds";
|
||||
|
||||
TIMER_START(writing_mld_data);
|
||||
partition::files::writeCells(config.GetPath(".osrm.cells"), storage);
|
||||
files::writeCellMetrics(config.GetPath(".osrm.cell_metrics"), metrics);
|
||||
TIMER_STOP(writing_mld_data);
|
||||
util::Log() << "MLD customization writing took " << TIMER_SEC(writing_mld_data) << " seconds";
|
||||
|
||||
TIMER_START(writing_graph);
|
||||
partition::files::writeGraph(config.GetPath(".osrm.mldgr"), *edge_based_graph);
|
||||
partition::files::writeGraph(config.GetPath(".osrm.mldgr"), graph);
|
||||
TIMER_STOP(writing_graph);
|
||||
util::Log() << "Graph writing took " << TIMER_SEC(writing_graph) << " seconds";
|
||||
|
||||
CellStorageStatistics(*edge_based_graph, mlp, storage);
|
||||
for (const auto &metric : metrics)
|
||||
{
|
||||
CellStorageStatistics(graph, mlp, storage, metric);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -41,16 +41,47 @@ const constexpr char *modifier_names[] = {"uturn",
|
||||
"left",
|
||||
"sharp left"};
|
||||
|
||||
// translations of TurnTypes. Not all types are exposed to the outside world.
|
||||
// invalid types should never be returned as part of the API
|
||||
const constexpr char *turn_type_names[] = {
|
||||
"invalid", "new name", "continue", "turn",
|
||||
"merge", "on ramp", "off ramp", "fork",
|
||||
"end of road", "notification", "roundabout", "exit roundabout",
|
||||
"rotary", "exit rotary", "roundabout turn", "roundabout turn",
|
||||
"use lane", "invalid", "invalid", "roundabout",
|
||||
"exit roundabout", "rotary", "exit rotary", "roundabout turn",
|
||||
"exit roundabout", "invalid", "invalid"};
|
||||
/**
|
||||
* Human readable values for TurnType enum values
|
||||
*/
|
||||
struct TurnTypeName
|
||||
{
|
||||
// String value we return with our API
|
||||
const char *external_name;
|
||||
// Internal only string name for the turn type - useful for debugging
|
||||
// and used by debug tiles for visualizing hidden turn types
|
||||
const char *internal_name;
|
||||
};
|
||||
|
||||
// Indexes in this list correspond to the Enum values of osrm::extractor::guidance::TurnType
|
||||
const constexpr TurnTypeName turn_type_names[] = {
|
||||
{"invalid", "(not set)"},
|
||||
{"new name", "new name"},
|
||||
{"continue", "continue"},
|
||||
{"turn", "turn"},
|
||||
{"merge", "merge"},
|
||||
{"on ramp", "on ramp"},
|
||||
{"off ramp", "off ramp"},
|
||||
{"fork", "fork"},
|
||||
{"end of road", "end of road"},
|
||||
{"notification", "notification"},
|
||||
{"roundabout", "enter roundabout"},
|
||||
{"exit roundabout", "enter and exit roundabout"},
|
||||
{"rotary", "enter rotary"},
|
||||
{"exit rotary", "enter and exit rotary"},
|
||||
{"roundabout turn", "enter roundabout turn"},
|
||||
{"roundabout turn", "enter and exit roundabout turn"},
|
||||
{"use lane", "use lane"},
|
||||
{"invalid", "(noturn)"},
|
||||
{"invalid", "(suppressed)"},
|
||||
{"roundabout", "roundabout"},
|
||||
{"exit roundabout", "exit roundabout"},
|
||||
{"rotary", "rotary"},
|
||||
{"exit rotary", "exit rotary"},
|
||||
{"roundabout turn", "roundabout turn"},
|
||||
{"exit roundabout", "exit roundabout turn"},
|
||||
{"invalid", "(stay on roundabout)"},
|
||||
{"invalid", "(sliproad)"}};
|
||||
|
||||
const constexpr char *waypoint_type_names[] = {"invalid", "arrive", "depart"};
|
||||
|
||||
@@ -69,8 +100,15 @@ inline bool hasValidLanes(const guidance::IntermediateIntersection &intersection
|
||||
std::string instructionTypeToString(const TurnType::Enum type)
|
||||
{
|
||||
static_assert(sizeof(turn_type_names) / sizeof(turn_type_names[0]) >= TurnType::MaxTurnType,
|
||||
"Some turn types doesn't have a string representation.");
|
||||
return turn_type_names[static_cast<std::size_t>(type)];
|
||||
"Some turn types have no string representation.");
|
||||
return turn_type_names[static_cast<std::size_t>(type)].external_name;
|
||||
}
|
||||
|
||||
std::string internalInstructionTypeToString(const TurnType::Enum type)
|
||||
{
|
||||
static_assert(sizeof(turn_type_names) / sizeof(turn_type_names[0]) >= TurnType::MaxTurnType,
|
||||
"Some turn types have no string representation.");
|
||||
return turn_type_names[static_cast<std::size_t>(type)].internal_name;
|
||||
}
|
||||
|
||||
util::json::Array lanesFromIntersection(const guidance::IntermediateIntersection &intersection)
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
#include "util/bearing.hpp"
|
||||
#include "util/log.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
|
||||
@@ -14,21 +17,34 @@ namespace guidance
|
||||
namespace detail
|
||||
{
|
||||
|
||||
std::pair<short, short> getDepartBearings(const LegGeometry &leg_geometry)
|
||||
std::pair<short, short> getDepartBearings(const LegGeometry &leg_geometry,
|
||||
const PhantomNode &source_node,
|
||||
const bool traversed_in_reverse)
|
||||
{
|
||||
BOOST_ASSERT(leg_geometry.locations.size() >= 2);
|
||||
const auto turn_coordinate = leg_geometry.locations.front();
|
||||
const auto post_turn_coordinate = *(leg_geometry.locations.begin() + 1);
|
||||
|
||||
if (turn_coordinate == post_turn_coordinate)
|
||||
{
|
||||
return std::make_pair<short, short>(0, source_node.GetBearing(traversed_in_reverse));
|
||||
}
|
||||
return std::make_pair<short, short>(
|
||||
0,
|
||||
std::round(util::coordinate_calculation::bearing(turn_coordinate, post_turn_coordinate)));
|
||||
}
|
||||
|
||||
std::pair<short, short> getArriveBearings(const LegGeometry &leg_geometry)
|
||||
std::pair<short, short> getArriveBearings(const LegGeometry &leg_geometry,
|
||||
const PhantomNode &target_node,
|
||||
const bool traversed_in_reverse)
|
||||
{
|
||||
BOOST_ASSERT(leg_geometry.locations.size() >= 2);
|
||||
const auto turn_coordinate = leg_geometry.locations.back();
|
||||
const auto pre_turn_coordinate = *(leg_geometry.locations.end() - 2);
|
||||
if (turn_coordinate == pre_turn_coordinate)
|
||||
{
|
||||
return std::make_pair<short, short>(target_node.GetBearing(traversed_in_reverse), 0);
|
||||
}
|
||||
return std::make_pair<short, short>(
|
||||
std::round(util::coordinate_calculation::bearing(pre_turn_coordinate, turn_coordinate)), 0);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user