Compare commits

...

68 Commits

Author SHA1 Message Date
Daniel Patterson 838b2a750e Create release branch for 5.12 2017-09-05 12:44:25 -07:00
Moritz Kobitzsch c2dc7e9cd0 use enter + exit for roundabout instructions (#4358)
* Expose roundabout/rotary exit instructions as a new instruction type.
2017-09-05 12:30:34 -07:00
Daniel Patterson 0fc1aa2711 Adds turn instructions to the turns layer in debug tiles. (#4460)
Add turn types and modifiers to turn points in debug tiles.  Also refactor some of the tile code to reduce some repetition.
2017-09-01 15:08:22 -07:00
Patrick Niklaus 89cf6d9e74 Disable alternative loop test since it does not test consistent behavior 2017-09-01 21:26:00 +02:00
Patrick Niklaus 61c430c098 Implement exclude flags on CH using shared core
The core is fully contracted for each exclude flag
and stored in a merged graph data structure.
2017-09-01 21:26:00 +02:00
Patrick Niklaus 4b75cb8b0e Make DynamicGraph copyable 2017-09-01 21:26:00 +02:00
Patrick Niklaus 53f87c08b5 Rename GraphView -> BisectionGraphView to avoid name conflicts 2017-09-01 21:26:00 +02:00
Patrick Niklaus e23dc8977f Refactor contract to be a stateless function 2017-09-01 21:26:00 +02:00
Patrick Niklaus 421dc5b6ec Don't return edge list in contractor but modify graph in-place 2017-09-01 21:26:00 +02:00
Patrick Niklaus 247f1c120f Make renumbering transparent to contraction 2017-09-01 21:26:00 +02:00
Patrick Niklaus e011c60e12 Replace flush in contract with in-place renumbering 2017-09-01 21:26:00 +02:00
Patrick Niklaus 26c909b64b Refactor contractor to pass in graph directly 2017-09-01 21:26:00 +02:00
Daniel Patterson acb7916996 Add test case with valid junk.
Use eps to explicitly match empty string and not consume input
2017-09-01 08:34:58 -07:00
Daniel Patterson 40b5045a0a Update CHANGELOG 2017-09-01 08:34:58 -07:00
Daniel Patterson b17f40862c If traffic CSV has an empty 4th column, preserve the weight on an edge while updating the duration. 2017-09-01 08:34:58 -07:00
Lev Dragunov 7702ebde61 Test fixes 2017-08-31 11:11:05 +02:00
Lev Dragunov 6951eadc18 CI fix 2017-08-31 11:11:05 +02:00
Lev Dragunov cf2573157f clang-format with older version 2017-08-31 11:11:05 +02:00
Lev Dragunov 80c55119d2 Add bearing for the phantom node 2017-08-31 11:11:05 +02:00
Mateusz Loskot 7323221e3b Add --verbosity,-l command line option
Allow to fine-control logging verbosity via command line
and LogPolicy setting (useful when OSRM used as library).

Closes #4299
2017-08-31 00:35:23 +02:00
Mateusz Loskot fb5bd818d9 CanMergeRoad missed test of rhs instance of IntersectionShapeData
Avoids identical sub-expressions road_target(lhs) == intersection_node
2017-08-31 00:34:28 +02:00
Mateusz Loskot 43a4e8db12 Use user-provided osmium::thread::Pool instance
Advantages:
* Allows to use Pool configured with user-preferred number of threads.
* If libosmium used in a shared library, this helps to prevent potential
  deadlock during Pool shutdown at (after) shared library unloading.

See https://github.com/osmcode/libosmium/pull/214

Requires libosmium 2.13.0
2017-08-31 00:34:09 +02:00
Mateusz Loskot 302390696e Remove repeated sub-expression for TurnType::Suppressed test 2017-08-31 00:33:00 +02:00
Michael Krasnyk 2508629d6c Minor fixes in Timezoner 2017-08-30 19:44:52 +02:00
Patrick Niklaus 90c390d7a6 Merge pull request #4455 from mloskot/ml/fix-short-options
Remove superfluous hyphen from short form of --version in tests
2017-08-30 18:30:45 +02:00
Patrick Niklaus 28178b12c7 Merge pull request #4456 from Project-OSRM/update/libosmium
Update libosmium to 2.13
2017-08-30 18:30:25 +02:00
Patrick Niklaus 95442d45aa Merge commit '6eb4f090f98f6b17a23c57768c16b7716b6c9cbd' as 'third_party/libosmium' 2017-08-30 09:30:27 +00:00
Patrick Niklaus 6eb4f090f9 Squashed 'third_party/libosmium/' content from commit ce865381f
git-subtree-dir: third_party/libosmium
git-subtree-split: ce865381fb752323ff1e66181f5a49b7f500ffa3
2017-08-30 09:30:27 +00:00
Patrick Niklaus b4ad6588ed Remove old libosmium 2017-08-30 09:30:18 +00:00
Patrick Niklaus 56282b0e3f Bump libosmium version 2017-08-30 09:28:41 +00:00
Mateusz Loskot 2385602500 Switch to reliable non-existent handle for QueryHeap items.
Default-constructed objects of (boost::heap) handle_type are singular,
including the wrapped handle_type::iterator.
Apparently, MSVC iterator debug facilities strictly require that
one singular instance is compared to another singular instance.
It is not possible to get check-comparabe iterators of non-singular
and singular instances as owning container will always mismatch.
2017-08-30 10:59:43 +02:00
Mateusz Loskot 628a154d7f Remove superfluous hyphen from short form of --version in tests 2017-08-30 10:09:12 +02:00
Daniel J. Hofmann efaed59b9b Whitelists scripts/node_install.sh for from-source compilation, resolves #4431 2017-08-28 19:08:36 -04:00
Daniel Patterson bdee13dea8 We should be using the module-relative @loader_path. @executable_path is relative to the main node process binary 2017-08-25 15:30:49 -07:00
Daniel Patterson c615910874 Don't use Travis' node environment, we create our own. 2017-08-25 13:46:29 -07:00
Daniel Patterson 9e1398c68a Enable OSX binary publishing for both NodeJS 4 and NodeJS 6 2017-08-25 13:46:29 -07:00
Denis Koronchik af10692d2d Fix misprint in documentation 2017-08-23 17:05:24 +02:00
Denis Koronchik e7abe37b10 Prevent linkage errors when load/write extracted graph from external app 2017-08-22 15:46:06 +02:00
Patrick Niklaus bc8617a9f4 Fix remaining PR comments 2017-08-21 13:00:12 +02:00
Patrick Niklaus 00e243b23b Refactor CustomizeCell::RelaxNode to move level check inside function 2017-08-21 13:00:12 +02:00
Patrick Niklaus 27324d0270 Add more tests for exclude validation 2017-08-21 13:00:12 +02:00
Patrick Niklaus 0c838fb60c Fix turnbot profile 2017-08-21 13:00:12 +02:00
Patrick Niklaus 0713ef5862 Add test cases for exclude and table/match service 2017-08-21 13:00:12 +02:00
Patrick Niklaus bee1ba8854 Fix profile docs 2017-08-21 13:00:12 +02:00
Patrick Niklaus da252c7597 Add node binding integration 2017-08-21 13:00:12 +02:00
Patrick Niklaus a4460abc83 Fix another manifestation of the off-by-one 2017-08-21 13:00:12 +02:00
Patrick Niklaus 261636febb Add HTTP docs 2017-08-21 13:00:12 +02:00
Patrick Niklaus 21f15f0a29 Add docs for lua profiles 2017-08-21 13:00:12 +02:00
Patrick Niklaus bd068ff2a6 Add changelog entries for exclude flags 2017-08-21 13:00:12 +02:00
Patrick Niklaus d09f5c0e3a Avoid -> Exclude 2017-08-21 13:00:12 +02:00
Patrick Niklaus 45140ca9f7 Fix off-by-one in avoid search 2017-08-21 13:00:12 +02:00
Patrick Niklaus c6be2e768a Throw an error for invalid classes 2017-08-21 13:00:12 +02:00
Patrick Niklaus 6339395cba Fix clang format 2017-08-21 13:00:12 +02:00
Patrick Niklaus bd6492bb38 Fix compilation after rebase 2017-08-21 13:00:12 +02:00
Patrick Niklaus f93b331817 Add error handling for avoid not implemented 2017-08-21 13:00:12 +02:00
Patrick Niklaus 20e4096c4b Fix stupid vector size bug 2017-08-21 13:00:12 +02:00
Patrick Niklaus 97952a9289 Don't snap to avoided streets 2017-08-21 13:00:12 +02:00
Patrick Niklaus 960a595268 Add test for avoid flags 2017-08-21 13:00:12 +02:00
Patrick Niklaus a3c94ef632 Avoid nodes that are disabled by an avoid flag 2017-08-21 13:00:12 +02:00
Patrick Niklaus 84fd38ac9c Add tests for avoid flags 2017-08-21 13:00:12 +02:00
Patrick Niklaus 303a8fae32 Refactor cell weights and durations to own file and allow for multiple metrics 2017-08-21 13:00:12 +02:00
Patrick Niklaus 21686ee8a9 Add datafacade factory to select avoid flags 2017-08-21 13:00:12 +02:00
Patrick Niklaus c8b142a676 Add placeholders for up to 8 weights 2017-08-21 13:00:12 +02:00
Patrick Niklaus 58061a68c4 Add avoid parameter to the API 2017-08-21 13:00:12 +02:00
Patrick Niklaus 9c11197768 Extract avoidable combinations from profiles into ProfileProperties 2017-08-21 13:00:12 +02:00
Moritz Kobitzsch f347efb006 detect turning onto oneways at the end of the road as non obvious 2017-08-18 16:33:11 +02:00
Moritz Kobitzsch af8ddac2af add test-case illustrating changes to obvious turn discovery 2017-08-18 16:33:11 +02:00
Michael Krasnyk b1358de9bb Use TBB parallel sort of CSV entries, #4418 2017-08-18 14:37:42 +02:00
438 changed files with 21423 additions and 9652 deletions
+1
View File
@@ -13,3 +13,4 @@
!profiles/*
!profiles/lib/*
!profiles/examples/*
!scripts/node_install.sh
+11 -5
View File
@@ -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 -1
View File
@@ -1,4 +1,25 @@
# UNRELEASED
# 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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+2 -2
View File
@@ -122,6 +122,6 @@ Feature: Car - Mode flag
| df | yes | motorway| | yes |
When I route I should get
| from | to | route | turns | classes |
| a | f | ab,df,df | depart,roundabout-exit-2,arrive | [()],[(),(motorway),(toll,motorway)],[()] |
| from | to | route | turns | classes |
| a | f | ab,df,df,df | depart,roundabout-exit-2,exit roundabout slight right,arrive | [()],[(),(motorway)],[(toll,motorway)],[()] |
-29
View File
@@ -47,32 +47,3 @@ Feature: Testbot - side bias
| d | a | bd,ab,ab | 27s +-1 |
# should be inverse of left hand bias
| d | c | bd,bc,bc | 24s +-1 |
Scenario: Roundabout exit counting for left sided driving
Given the profile file "testbot" initialized with
"""
profile.left_hand_driving = true
profile.turn_bias = 1/1.075
"""
And a grid size of 10 meters
And the node map
"""
a
b
h g c d
e
f
"""
And the ways
| nodes | junction |
| ab | |
| cd | |
| ef | |
| gh | |
| bcegb | 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 | depart,roundabout turn straight exit-2,arrive |
| a,h | ab,gh,gh | depart,roundabout turn right exit-3,arrive |
+20 -20
View File
@@ -553,8 +553,8 @@ Feature: Turn Lane Guidance
| fy | | primary | |
When I route I should get
| waypoints | route | turns | lanes |
| a,h | ab,gh,gh | depart,roundabout-exit-5,arrive | ,;;;;;, |
| waypoints | route | turns | lanes |
| a,h | ab,gh,gh,gh | depart,roundabout-exit-5,exit roundabout right,arrive | ,;;;;,, |
@anticipate
Scenario: No Lanes for Roundabouts, see #2626
@@ -569,16 +569,16 @@ Feature: Turn Lane Guidance
| nodes | turn:lanes:forward | highway | junction | name |
| xb | slight_right\|slight_right | primary | | xb |
| dy | | primary | | dy |
| ab | | primary | roundabout | roundabout |
| bc | | primary | roundabout | roundabout |
| cd | left\|slight_right | primary | roundabout | roundabout |
| da | | primary | roundabout | roundabout |
| ab | | primary | roundabout | rotary |
| bc | | primary | roundabout | rotary |
| cd | left\|slight_right | primary | roundabout | rotary |
| da | | primary | roundabout | rotary |
When I route I should get
| waypoints | route | turns | lanes |
| x,y | xb,dy,dy | depart,roundabout-exit-1,arrive | ,;, |
| x,c | xb,roundabout,roundabout | depart,roundabout-exit-undefined,arrive | ,, |
| x,a | xb,roundabout,roundabout | depart,roundabout-exit-undefined,arrive | ,;, |
| waypoints | route | turns | lanes |
| x,y | xb,dy,dy,dy | depart,rotary-exit-1,exit rotary right,arrive | ,,, |
| x,c | xb,rotary,rotary | depart,rotary-exit-undefined,arrive | ,, |
| x,a | xb,rotary,rotary | depart,rotary-exit-undefined,arrive | ,;, |
@anticipate
Scenario: No Lanes for Roundabouts, see #2626
@@ -614,8 +614,8 @@ Feature: Turn Lane Guidance
| fy | | primary | |
When I route I should get
| waypoints | route | turns | lanes |
| a,h | ab,ch,ch | depart,roundabout-exit-5,arrive | ,;;;;;, |
| waypoints | route | turns | lanes |
| a,h | ab,ch,ch,ch | depart,roundabout-exit-5,exit roundabout left,arrive | ,;;;;,, |
@anticipate
Scenario: No Lanes for Roundabouts, see #2626
@@ -635,16 +635,16 @@ Feature: Turn Lane Guidance
| nodes | turn:lanes:forward | highway | junction | name |
| xb | slight_right\|slight_right | primary | | xb |
| dy | | primary | | dy |
| ab | | primary | roundabout | roundabout |
| bc | | primary | roundabout | roundabout |
| cd | left\|slight_right | primary | roundabout | roundabout |
| da | | primary | roundabout | roundabout |
| ab | | primary | roundabout | rotary |
| bc | | primary | roundabout | rotary |
| cd | left\|slight_right | primary | roundabout | rotary |
| da | | primary | roundabout | rotary |
When I route I should get
| waypoints | route | turns | lanes |
| x,y | xb,dy,dy | depart,roundabout-exit-1,arrive | ,;, |
| x,c | xb,roundabout,roundabout | depart,roundabout-exit-undefined,arrive | ,, |
| x,a | xb,roundabout,roundabout | depart,roundabout-exit-undefined,arrive | ,;, |
| waypoints | route | turns | lanes |
| x,y | xb,dy,dy,dy | depart,rotary-exit-1,exit rotary right,arrive | ,,, |
| x,c | xb,rotary,rotary | depart,rotary-exit-undefined,arrive | ,, |
| x,a | xb,rotary,rotary | depart,rotary-exit-undefined,arrive | ,;, |
@anticipate @todo @2032
Scenario: No Lanes for Roundabouts, see #2626
+17 -257
View File
@@ -1,5 +1,8 @@
@routing @guidance
Feature: Rotary
Feature: Circular
# Circular tags are treated just as rotaries. We can rely on the rotary tests for their handling on special cases.
# Here we only ensure that the `circular` tag is handled and assigned a rotary type
Background:
Given the profile "car"
@@ -24,259 +27,16 @@ Feature: Rotary
| bgecb | circular |
When I route I should get
| waypoints | route | turns |
| a,d | ab,cd,cd | depart,bgecb-exit-3,arrive |
| a,f | ab,ef,ef | depart,bgecb-exit-2,arrive |
| a,h | ab,gh,gh | depart,bgecb-exit-1,arrive |
| d,f | cd,ef,ef | depart,bgecb-exit-3,arrive |
| d,h | cd,gh,gh | depart,bgecb-exit-2,arrive |
| d,a | cd,ab,ab | depart,bgecb-exit-1,arrive |
| f,h | ef,gh,gh | depart,bgecb-exit-3,arrive |
| f,a | ef,ab,ab | depart,bgecb-exit-2,arrive |
| f,d | ef,cd,cd | depart,bgecb-exit-1,arrive |
| h,a | gh,ab,ab | depart,bgecb-exit-3,arrive |
| h,d | gh,cd,cd | depart,bgecb-exit-2,arrive |
| h,f | gh,ef,ef | depart,bgecb-exit-1,arrive |
Scenario: Only Enter
Given the node map
"""
a
b
d c g h
e
f
"""
And the ways
| nodes | junction |
| ab | |
| cd | |
| ef | |
| gh | |
| bcegb | circular |
When I route I should get
| waypoints | route | turns |
| a,c | ab,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
| a,e | ab,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
| a,g | ab,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
| d,e | cd,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
| d,g | cd,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
| d,b | cd,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
| f,g | ef,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
| f,b | ef,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
| f,c | ef,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
| h,b | gh,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
| h,c | gh,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
| h,e | gh,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
Scenario: Only Exit
Given the node map
"""
a
b
d c g h
e
f
"""
And the ways
| nodes | junction |
| ab | |
| cd | |
| ef | |
| gh | |
| bcegb | circular |
When I route I should get
| waypoints | route | turns |
| b,d | bcegb,cd,cd | depart,bcegb-exit-1,arrive |
| b,f | bcegb,ef,ef | depart,bcegb-exit-2,arrive |
| b,h | bcegb,gh,gh | depart,bcegb-exit-3,arrive |
| c,f | bcegb,ef,ef | depart,bcegb-exit-1,arrive |
| c,h | bcegb,gh,gh | depart,bcegb-exit-2,arrive |
| c,a | bcegb,ab,ab | depart,bcegb-exit-3,arrive |
| e,h | bcegb,gh,gh | depart,bcegb-exit-1,arrive |
| e,a | bcegb,ab,ab | depart,bcegb-exit-2,arrive |
| e,d | bcegb,cd,cd | depart,bcegb-exit-3,arrive |
| g,a | bcegb,ab,ab | depart,bcegb-exit-1,arrive |
| g,d | bcegb,cd,cd | depart,bcegb-exit-2,arrive |
| g,f | bcegb,ef,ef | depart,bcegb-exit-3,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
Given the node map
"""
a
b
d c g h
e
f
"""
And the ways
| nodes | junction |
| ab | |
| cd | |
| ef | |
| gh | |
| bcegb | circular |
When I route I should get
| waypoints | route | turns |
| b,c | bcegb,bcegb | depart,arrive |
| b,e | bcegb,bcegb | depart,arrive |
| b,g | bcegb,bcegb | depart,arrive |
| c,e | bcegb,bcegb | depart,arrive |
| c,g | bcegb,bcegb | depart,arrive |
| c,b | bcegb,bcegb | depart,arrive |
| e,g | bcegb,bcegb | depart,arrive |
| e,b | bcegb,bcegb | depart,arrive |
| e,c | bcegb,bcegb | depart,arrive |
| g,b | bcegb,bcegb | depart,arrive |
| g,c | bcegb,bcegb | depart,arrive |
| g,e | bcegb,bcegb | depart,arrive |
#needs to be adjusted when name-discovery works for entrys
Scenario: Mixed Entry and Exit
Given the node map
"""
c a
j b f
k e
l h d
g i
"""
And the ways
| nodes | junction | oneway |
| abc | | yes |
| def | | yes |
| ghi | | yes |
| jkl | | yes |
| bkheb | circular | yes |
When I route I should get
| waypoints | route | turns |
| a,c | abc,abc,abc | depart,rotary-exit-1,arrive |
| a,l | abc,jkl,jkl | depart,bkheb-exit-2,arrive |
| a,i | abc,ghi,ghi | depart,bkheb-exit-3,arrive |
| a,f | abc,def,def | depart,bkheb-exit-4,arrive |
| d,f | def,def,def | depart,rotary-exit-1,arrive |
| d,c | def,abc,abc | depart,bkheb-exit-2,arrive |
| d,l | def,jkl,jkl | depart,bkheb-exit-3,arrive |
| d,i | def,ghi,ghi | depart,bkheb-exit-4,arrive |
| g,i | ghi,ghi,ghi | depart,rotary-exit-1,arrive |
| g,f | ghi,def,def | depart,bkheb-exit-2,arrive |
| g,c | ghi,abc,abc | depart,bkheb-exit-3,arrive |
| g,l | ghi,jkl,jkl | depart,bkheb-exit-4,arrive |
| j,l | jkl,jkl,jkl | depart,rotary-exit-1,arrive |
| j,i | jkl,ghi,ghi | depart,bkheb-exit-2,arrive |
| j,f | jkl,def,def | depart,bkheb-exit-3,arrive |
| j,c | jkl,abc,abc | depart,bkheb-exit-4,arrive |
Scenario: Collinear in X,Y
Given the node map
"""
a
b
c d f
e
"""
And the ways
| nodes | junction |
| ab | |
| bcdb | circular |
| ce | |
| df | |
When I route I should get
| waypoints | route | turns |
| a,e | ab,ce,ce | depart,bcdb-exit-1,arrive |
| a,f | ab,df,df | depart,bcdb-exit-2,arrive |
Scenario: Collinear in X,Y
Given the node map
"""
a
d
b c f
e
"""
And the ways
| nodes | junction |
| ad | |
| bcdb | circular |
| be | |
| cf | |
When I route I should get
| waypoints | route | turns |
| a,e | ad,be,be | depart,bcdb-exit-1,arrive |
| a,f | ad,cf,cf | depart,bcdb-exit-2,arrive |
Scenario: Collinear in X,Y
Given the node map
"""
a
c
d b f
e
"""
And the ways
| nodes | junction |
| ac | |
| bcdb | circular |
| de | |
| bf | |
When I route I should get
| waypoints | route | turns |
| a,e | ac,de,de | depart,bcdb-exit-1,arrive |
| a,f | ac,bf,bf | depart,bcdb-exit-2,arrive |
Scenario: Collinear in X,Y
Given the node map
"""
f
d c e
b
a
"""
And the ways
| nodes | junction |
| ab | |
| bcdb | circular |
| ce | |
| df | |
When I route I should get
| waypoints | route | turns |
| a,e | ab,ce,ce | depart,bcdb-exit-1,arrive |
| a,f | ab,df,df | depart,bcdb-exit-2,arrive |
Scenario: Collinear in X,Y
Given the node map
"""
f
d c e
b
a
"""
And the ways
| nodes | junction |
| ab | |
| bcdb | circular |
| ce | |
| df | |
When I route I should get
| waypoints | route | turns |
| a,e | ab,ce,ce | depart,bcdb-exit-1,arrive |
| a,f | ab,df,df | depart,bcdb-exit-2,arrive |
| waypoints | route | turns |
| a,d | ab,cd,cd,cd | depart,bgecb-exit-3,exit rotary right,arrive |
| a,f | ab,ef,ef,ef | depart,bgecb-exit-2,exit rotary right,arrive |
| a,h | ab,gh,gh,gh | depart,bgecb-exit-1,exit rotary right,arrive |
| d,f | cd,ef,ef,ef | depart,bgecb-exit-3,exit rotary right,arrive |
| d,h | cd,gh,gh,gh | depart,bgecb-exit-2,exit rotary right,arrive |
| d,a | cd,ab,ab,ab | depart,bgecb-exit-1,exit rotary right,arrive |
| f,h | ef,gh,gh,gh | depart,bgecb-exit-3,exit rotary right,arrive |
| f,a | ef,ab,ab,ab | depart,bgecb-exit-2,exit rotary right,arrive |
| f,d | ef,cd,cd,cd | depart,bgecb-exit-1,exit rotary right,arrive |
| h,a | gh,ab,ab,ab | depart,bgecb-exit-3,exit rotary right,arrive |
| h,d | gh,cd,cd,cd | depart,bgecb-exit-2,exit rotary right,arrive |
| h,f | gh,ef,ef,ef | depart,bgecb-exit-1,exit rotary right,arrive |
@@ -905,8 +905,8 @@ Feature: Slipways and Dedicated Turn Lanes
| restriction | yb | be | b | only_straight |
When I route I should get
| waypoints | route | turns | locations |
| z,t | through,,out,out | depart,off ramp slight right,round-exit-3,arrive | z,s,c,t |
| waypoints | route | turns | locations |
| z,t | through,,out,out,out | depart,off ramp slight right,round-exit-3,exit rotary right,arrive | z,s,c,e,t |
Scenario: Sliproad before a roundabout
Given the node map
@@ -942,8 +942,8 @@ Feature: Slipways and Dedicated Turn Lanes
| restriction | bc | cd | c | only_straight |
When I route I should get
| waypoints | route | turns | locations |
| a,k | road,,, | depart,turn right,roundabout turn right exit-1,arrive | a,b,h,k |
| waypoints | route | turns | locations |
| a,k | road,,, | depart,turn right,roundabout turn right exit-1,arrive | a,b,h,k |
@sliproads
Scenario: Sliproad with 4 roads at target
@@ -995,4 +995,4 @@ Feature: Slipways and Dedicated Turn Lanes
When I route I should get
| waypoints | route | turns | locations |
| s,f | sabc,dbef,dbef | depart,turn right,arrive | s,a,f |
| s,f | sabc,dbef,dbef | depart,turn right,arrive | s,a,f |
+19
View File
@@ -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&center=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
+4 -4
View File
@@ -140,9 +140,9 @@ Feature: Intersections Data
When I route I should get
| waypoints | route | intersections |
| e,f | ea,fb,fb | true:180;false:0 false:150 true:210,false:30 true:150 true:270;true:90 |
| e,g | ea,gc,gc | true:180;false:0 false:150 true:210,false:30 true:150 true:270,true:30 true:180 false:330;true:0 |
| e,h | ea,hd,hd | true:180;false:0 false:150 true:210,false:30 true:150 true:270,true:30 true:180 false:330,true:90 false:210 true:330;true:270 |
| e,f | ea,fb,fb,fb | true:180;false:0 false:150 true:210;false:30 true:150 true:270;true:90 |
| e,g | ea,gc,gc,gc | true:180;false:0 false:150 true:210,false:30 true:150 true:270;true:30 true:180 false:330;true:0 |
| e,h | ea,hd,hd,hd | true:180;false:0 false:150 true:210,false:30 true:150 true:270,true:30 true:180 false:330;true:90 false:210 true:330;true:270 |
| e,2 | ea,abcda,abcda | true:180;false:0 false:150 true:210,false:30 true:150 true:270;true:327 +-1 |
| 1,g | abcda,gc,gc | true:214;false:30 true:150 true:270,true:30 true:180 false:330;true:0 |
| 1,g | abcda,gc,gc | true:214,false:30 true:150 true:270;true:30 true:180 false:330;true:0 |
| 1,3 | abcda,abcda | true:214,false:30 true:150 true:270,true:30 true:180 false:330;true:214 |
-177
View File
@@ -1,177 +0,0 @@
@routing @guidance
Feature: Rotary
Background:
Given the profile "bicycle"
Given a grid size of 30 meters
Scenario: Enter and Exit
Given the node map
"""
a
b
h g c d
e
f
"""
And the ways
| nodes | junction |
| ab | |
| cd | |
| ef | |
| gh | |
| bgecb | roundabout |
When I route I should get
| waypoints | route | turns |
| a,d | ab,cd,cd | depart,bgecb-exit-3,arrive |
| a,f | ab,ef,ef | depart,bgecb-exit-2,arrive |
| a,h | ab,gh,gh | depart,bgecb-exit-1,arrive |
| d,f | cd,ef,ef | depart,bgecb-exit-3,arrive |
| d,h | cd,gh,gh | depart,bgecb-exit-2,arrive |
| d,a | cd,ab,ab | depart,bgecb-exit-1,arrive |
| f,h | ef,gh,gh | depart,bgecb-exit-3,arrive |
| f,a | ef,ab,ab | depart,bgecb-exit-2,arrive |
| f,d | ef,cd,cd | depart,bgecb-exit-1,arrive |
| h,a | gh,ab,ab | depart,bgecb-exit-3,arrive |
| h,d | gh,cd,cd | depart,bgecb-exit-2,arrive |
| h,f | gh,ef,ef | depart,bgecb-exit-1,arrive |
Scenario: Only Enter
Given the node map
"""
a
b
d c g h
e
f
"""
And the ways
| nodes | junction |
| ab | |
| cd | |
| ef | |
| gh | |
| bcegb | roundabout |
When I route I should get
| waypoints | route | turns |
| a,c | ab,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
| a,e | ab,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
| a,g | ab,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
| d,e | cd,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
| d,g | cd,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
| d,b | cd,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
| f,g | ef,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
| f,b | ef,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
| f,c | ef,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
| h,b | gh,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
| h,c | gh,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
| h,e | gh,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
Scenario: Only Exit
Given the node map
"""
a
b
d c g h
e
f
"""
And the ways
| nodes | junction |
| ab | |
| cd | |
| ef | |
| gh | |
| bcegb | roundabout |
When I route I should get
| waypoints | route | turns |
| b,d | bcegb,cd,cd | depart,bcegb-exit-1,arrive |
| b,f | bcegb,ef,ef | depart,bcegb-exit-2,arrive |
| b,h | bcegb,gh,gh | depart,bcegb-exit-3,arrive |
| c,f | bcegb,ef,ef | depart,bcegb-exit-1,arrive |
| c,h | bcegb,gh,gh | depart,bcegb-exit-2,arrive |
| c,a | bcegb,ab,ab | depart,bcegb-exit-3,arrive |
| e,h | bcegb,gh,gh | depart,bcegb-exit-1,arrive |
| e,a | bcegb,ab,ab | depart,bcegb-exit-2,arrive |
| e,d | bcegb,cd,cd | depart,bcegb-exit-3,arrive |
| g,a | bcegb,ab,ab | depart,bcegb-exit-1,arrive |
| g,d | bcegb,cd,cd | depart,bcegb-exit-2,arrive |
| g,f | bcegb,ef,ef | depart,bcegb-exit-3,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
Given the node map
"""
a
b
d c g h
e
f
"""
And the ways
| nodes | junction |
| ab | |
| cd | |
| ef | |
| gh | |
| bcegb | roundabout |
When I route I should get
| waypoints | route | turns |
| b,c | bcegb,bcegb | depart,arrive |
| b,e | bcegb,bcegb | depart,arrive |
| b,g | bcegb,bcegb | depart,arrive |
| c,e | bcegb,bcegb | depart,arrive |
| c,g | bcegb,bcegb | depart,arrive |
| c,b | bcegb,bcegb | depart,arrive |
| e,g | bcegb,bcegb | depart,arrive |
| e,b | bcegb,bcegb | depart,arrive |
| e,c | bcegb,bcegb | depart,arrive |
| g,b | bcegb,bcegb | depart,arrive |
| g,c | bcegb,bcegb | depart,arrive |
| g,e | bcegb,bcegb | depart,arrive |
#needs to be adjusted when name-discovery works for entrys
Scenario: Mixed Entry and Exit
Given the node map
"""
c a
j b f
k e
l h d
g i
"""
And the ways
| nodes | junction | oneway |
| abc | | yes |
| def | | yes |
| ghi | | yes |
| jkl | | yes |
| bkheb | roundabout | yes |
When I route I should get
| waypoints | route | turns |
| a,c | abc,abc,abc | depart,rotary-exit-1,arrive |
| a,l | abc,jkl,jkl | depart,bkheb-exit-2,arrive |
| a,i | abc,ghi,ghi | depart,bkheb-exit-3,arrive |
| a,f | abc,def,def | depart,bkheb-exit-4,arrive |
| d,f | def,def,def | depart,rotary-exit-1,arrive |
| d,c | def,abc,abc | depart,bkheb-exit-2,arrive |
| d,l | def,jkl,jkl | depart,bkheb-exit-3,arrive |
| d,i | def,ghi,ghi | depart,bkheb-exit-4,arrive |
| g,i | ghi,ghi,ghi | depart,rotary-exit-1,arrive |
| g,f | ghi,def,def | depart,bkheb-exit-2,arrive |
| g,c | ghi,abc,abc | depart,bkheb-exit-3,arrive |
| g,l | ghi,jkl,jkl | depart,bkheb-exit-4,arrive |
| j,l | jkl,jkl,jkl | depart,rotary-exit-1,arrive |
| j,i | jkl,ghi,ghi | depart,bkheb-exit-2,arrive |
| j,f | jkl,def,def | depart,bkheb-exit-3,arrive |
| j,c | jkl,abc,abc | depart,bkheb-exit-4,arrive |
+58 -58
View File
@@ -24,19 +24,19 @@ Feature: Rotary
| bgecb | roundabout |
When I route I should get
| waypoints | route | turns |
| a,d | ab,cd,cd | depart,bgecb-exit-3,arrive |
| a,f | ab,ef,ef | depart,bgecb-exit-2,arrive |
| a,h | ab,gh,gh | depart,bgecb-exit-1,arrive |
| d,f | cd,ef,ef | depart,bgecb-exit-3,arrive |
| d,h | cd,gh,gh | depart,bgecb-exit-2,arrive |
| d,a | cd,ab,ab | depart,bgecb-exit-1,arrive |
| f,h | ef,gh,gh | depart,bgecb-exit-3,arrive |
| f,a | ef,ab,ab | depart,bgecb-exit-2,arrive |
| f,d | ef,cd,cd | depart,bgecb-exit-1,arrive |
| h,a | gh,ab,ab | depart,bgecb-exit-3,arrive |
| h,d | gh,cd,cd | depart,bgecb-exit-2,arrive |
| h,f | gh,ef,ef | depart,bgecb-exit-1,arrive |
| waypoints | route | turns |
| a,d | ab,cd,cd,cd | depart,bgecb-exit-3,exit rotary right,arrive |
| a,f | ab,ef,ef,ef | depart,bgecb-exit-2,exit rotary right,arrive |
| a,h | ab,gh,gh,gh | depart,bgecb-exit-1,exit rotary right,arrive |
| d,f | cd,ef,ef,ef | depart,bgecb-exit-3,exit rotary right,arrive |
| d,h | cd,gh,gh,gh | depart,bgecb-exit-2,exit rotary right,arrive |
| d,a | cd,ab,ab,ab | depart,bgecb-exit-1,exit rotary right,arrive |
| f,h | ef,gh,gh,gh | depart,bgecb-exit-3,exit rotary right,arrive |
| f,a | ef,ab,ab,ab | depart,bgecb-exit-2,exit rotary right,arrive |
| f,d | ef,cd,cd,cd | depart,bgecb-exit-1,exit rotary right,arrive |
| h,a | gh,ab,ab,ab | depart,bgecb-exit-3,exit rotary right,arrive |
| h,d | gh,cd,cd,cd | depart,bgecb-exit-2,exit rotary right,arrive |
| h,f | gh,ef,ef,ef | depart,bgecb-exit-1,exit rotary right,arrive |
Scenario: Only Enter
Given the node map
@@ -90,19 +90,19 @@ Feature: Rotary
| bcegb | roundabout |
When I route I should get
| waypoints | route | turns |
| b,d | bcegb,cd,cd | depart,bcegb-exit-1,arrive |
| b,f | bcegb,ef,ef | depart,bcegb-exit-2,arrive |
| b,h | bcegb,gh,gh | depart,bcegb-exit-3,arrive |
| c,f | bcegb,ef,ef | depart,bcegb-exit-1,arrive |
| c,h | bcegb,gh,gh | depart,bcegb-exit-2,arrive |
| c,a | bcegb,ab,ab | depart,bcegb-exit-3,arrive |
| e,h | bcegb,gh,gh | depart,bcegb-exit-1,arrive |
| e,a | bcegb,ab,ab | depart,bcegb-exit-2,arrive |
| e,d | bcegb,cd,cd | depart,bcegb-exit-3,arrive |
| g,a | bcegb,ab,ab | depart,bcegb-exit-1,arrive |
| g,d | bcegb,cd,cd | depart,bcegb-exit-2,arrive |
| g,f | bcegb,ef,ef | depart,bcegb-exit-3,arrive |
| waypoints | route | turns |
| b,d | bcegb,cd,cd | depart,exit rotary right,arrive |
| b,f | bcegb,ef,ef | depart,exit rotary right,arrive |
| b,h | bcegb,gh,gh | depart,exit rotary right,arrive |
| c,f | bcegb,ef,ef | depart,exit rotary right,arrive |
| c,h | bcegb,gh,gh | depart,exit rotary right,arrive |
| c,a | bcegb,ab,ab | depart,exit rotary right,arrive |
| e,h | bcegb,gh,gh | depart,exit rotary right,arrive |
| e,a | bcegb,ab,ab | depart,exit rotary right,arrive |
| e,d | bcegb,cd,cd | depart,exit rotary right,arrive |
| g,a | bcegb,ab,ab | depart,exit rotary right,arrive |
| g,d | bcegb,cd,cd | depart,exit rotary right,arrive |
| g,f | bcegb,ef,ef | depart,exit rotary 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
@@ -158,23 +158,23 @@ Feature: Rotary
| bkheb | roundabout | yes |
When I route I should get
| waypoints | route | turns |
| a,c | abc,abc,abc | depart,rotary-exit-1,arrive |
| a,l | abc,jkl,jkl | depart,bkheb-exit-2,arrive |
| a,i | abc,ghi,ghi | depart,bkheb-exit-3,arrive |
| a,f | abc,def,def | depart,bkheb-exit-4,arrive |
| d,f | def,def,def | depart,rotary-exit-1,arrive |
| d,c | def,abc,abc | depart,bkheb-exit-2,arrive |
| d,l | def,jkl,jkl | depart,bkheb-exit-3,arrive |
| d,i | def,ghi,ghi | depart,bkheb-exit-4,arrive |
| g,i | ghi,ghi,ghi | depart,rotary-exit-1,arrive |
| g,f | ghi,def,def | depart,bkheb-exit-2,arrive |
| g,c | ghi,abc,abc | depart,bkheb-exit-3,arrive |
| g,l | ghi,jkl,jkl | depart,bkheb-exit-4,arrive |
| j,l | jkl,jkl,jkl | depart,rotary-exit-1,arrive |
| j,i | jkl,ghi,ghi | depart,bkheb-exit-2,arrive |
| j,f | jkl,def,def | depart,bkheb-exit-3,arrive |
| j,c | jkl,abc,abc | depart,bkheb-exit-4,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,9 +193,9 @@ Feature: Rotary
| df | |
When I route I should get
| waypoints | route | turns |
| a,e | ab,ce,ce | depart,bcdb-exit-1,arrive |
| a,f | ab,df,df | depart,bcdb-exit-2,arrive |
| 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 |
Scenario: Collinear in X,Y
Given the node map
@@ -214,9 +214,9 @@ Feature: Rotary
| cf | |
When I route I should get
| waypoints | route | turns |
| a,e | ad,be,be | depart,bcdb-exit-1,arrive |
| a,f | ad,cf,cf | depart,bcdb-exit-2,arrive |
| 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 |
Scenario: Collinear in X,Y
Given the node map
@@ -235,9 +235,9 @@ Feature: Rotary
| bf | |
When I route I should get
| waypoints | route | turns |
| a,e | ac,de,de | depart,bcdb-exit-1,arrive |
| a,f | ac,bf,bf | depart,bcdb-exit-2,arrive |
| 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 |
Scenario: Collinear in X,Y
Given the node map
@@ -256,9 +256,9 @@ Feature: Rotary
| df | |
When I route I should get
| waypoints | route | turns |
| a,e | ab,ce,ce | depart,bcdb-exit-1,arrive |
| a,f | ab,df,df | depart,bcdb-exit-2,arrive |
| 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 |
Scenario: Collinear in X,Y
Given the node map
@@ -277,6 +277,6 @@ Feature: Rotary
| df | |
When I route I should get
| waypoints | route | turns |
| a,e | ab,ce,ce | depart,bcdb-exit-1,arrive |
| a,f | ab,df,df | depart,bcdb-exit-2,arrive |
| 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 |
+30 -96
View File
@@ -5,39 +5,6 @@ Feature: Basic Roundabout
Given the profile "bicycle"
Given a grid size of 10 meters
Scenario: Only Enter
Given the node map
"""
a
b
d c g h
e
f
"""
And the ways
| nodes | junction |
| ab | |
| cd | |
| ef | |
| gh | |
| bcegb | roundabout |
When I route I should get
| waypoints | route | turns |
| a,c | ab,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
| a,e | ab,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
| a,g | ab,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
| d,e | cd,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
| d,g | cd,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
| d,b | cd,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
| f,g | ef,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
| f,b | ef,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
| f,c | ef,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
| h,b | gh,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
| h,c | gh,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
| h,e | gh,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
Scenario: Only Exit
Given the node map
"""
@@ -57,54 +24,21 @@ Feature: Basic Roundabout
| bcegb | roundabout |
When I route I should get
| waypoints | route | turns |
| b,d | bcegb,cd,cd | depart,roundabout-exit-1,arrive |
| b,f | bcegb,ef,ef | depart,roundabout-exit-2,arrive |
| b,h | bcegb,gh,gh | depart,roundabout-exit-3,arrive |
| c,f | bcegb,ef,ef | depart,roundabout-exit-1,arrive |
| c,h | bcegb,gh,gh | depart,roundabout-exit-2,arrive |
| c,a | bcegb,ab,ab | depart,roundabout-exit-3,arrive |
| e,h | bcegb,gh,gh | depart,roundabout-exit-1,arrive |
| e,a | bcegb,ab,ab | depart,roundabout-exit-2,arrive |
| e,d | bcegb,cd,cd | depart,roundabout-exit-3,arrive |
| g,a | bcegb,ab,ab | depart,roundabout-exit-1,arrive |
| g,d | bcegb,cd,cd | depart,roundabout-exit-2,arrive |
| g,f | bcegb,ef,ef | depart,roundabout-exit-3,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
Given the node map
"""
a
b
d c g h
e
f
"""
And the ways
| nodes | junction |
| ab | |
| cd | |
| ef | |
| gh | |
| bcegb | roundabout |
When I route I should get
| waypoints | route | turns |
| b,c | bcegb,bcegb | depart,arrive |
| b,e | bcegb,bcegb | depart,arrive |
| b,g | bcegb,bcegb | depart,arrive |
| c,e | bcegb,bcegb | depart,arrive |
| c,g | bcegb,bcegb | depart,arrive |
| c,b | bcegb,bcegb | depart,arrive |
| e,g | bcegb,bcegb | depart,arrive |
| e,b | bcegb,bcegb | depart,arrive |
| e,c | bcegb,bcegb | depart,arrive |
| g,b | bcegb,bcegb | depart,arrive |
| g,c | bcegb,bcegb | depart,arrive |
| g,e | bcegb,bcegb | depart,arrive |
Scenario: Mixed Entry and Exit
Given the node map
"""
@@ -124,20 +58,20 @@ Feature: Basic Roundabout
| bkheb | roundabout | yes |
When I route I should get
| waypoints | route | turns |
| a,c | abc,abc,abc | depart,roundabout-exit-1,arrive |
| a,l | abc,jkl,jkl | depart,roundabout-exit-2,arrive |
| a,i | abc,ghi,ghi | depart,roundabout-exit-3,arrive |
| a,f | abc,def,def | depart,roundabout-exit-4,arrive |
| d,f | def,def,def | depart,roundabout-exit-1,arrive |
| d,c | def,abc,abc | depart,roundabout-exit-2,arrive |
| d,l | def,jkl,jkl | depart,roundabout-exit-3,arrive |
| d,i | def,ghi,ghi | depart,roundabout-exit-4,arrive |
| g,i | ghi,ghi,ghi | depart,roundabout-exit-1,arrive |
| g,f | ghi,def,def | depart,roundabout-exit-2,arrive |
| g,c | ghi,abc,abc | depart,roundabout-exit-3,arrive |
| g,l | ghi,jkl,jkl | depart,roundabout-exit-4,arrive |
| j,l | jkl,jkl,jkl | depart,roundabout-exit-1,arrive |
| j,i | jkl,ghi,ghi | depart,roundabout-exit-2,arrive |
| j,f | jkl,def,def | depart,roundabout-exit-3,arrive |
| j,c | jkl,abc,abc | depart,roundabout-exit-4,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 |
@@ -52,7 +52,7 @@ Feature: Basic Roundabout
| behkb | roundabout | yes |
When I route I should get
| waypoints | route | turns |
| c,a | cba,cba,cba | depart,roundabout-exit-1,arrive |
| l,a | lkj,cba,cba | depart,roundabout-exit-2,arrive |
| i,a | ihg,cba,cba | depart,roundabout-exit-3,arrive |
| waypoints | route | turns |
| c,a | cba,cba,cba | depart,exit roundabout left,arrive |
| l,a | lkj,cba,cba,cba | depart,roundabout-exit-2,exit roundabout straight,arrive |
| i,a | ihg,cba,cba,cba | depart,roundabout-exit-3,exit roundabout straight,arrive |
+13 -46
View File
@@ -5,39 +5,6 @@ Feature: Basic Roundabout
Given the profile "bicycle"
Given a grid size of 10 meters
Scenario: Enter and Exit
Given the node map
"""
a
b
h g c d
e
f
"""
And the ways
| nodes | junction |
| ab | |
| cd | |
| ef | |
| gh | |
| 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 |
# https://www.openstreetmap.org/way/223225602
Scenario: Enter and Exit with changing mode
Given the node map
@@ -58,16 +25,16 @@ Feature: Basic Roundabout
| bgecb | roundabout | residential |
When I route I should get
| waypoints | route | turns |
| a,d | ab,cd,cd | depart,roundabout turn left exit-1,arrive |
| a,f | ab,ef,ef,ef | depart,roundabout turn left exit-1,notification right,arrive |
| a,h | ab,bgecb,gh,gh | depart,roundabout turn right exit-1,notification right,arrive |
| d,f | cd,ef,ef,ef | depart,roundabout turn sharp left exit-2,notification right,arrive |
| d,h | cd,gh,gh,gh | depart,roundabout turn left exit-2,notification right,arrive |
| d,a | cd,ab,ab | depart,roundabout turn right exit-1,arrive |
| f,h | ef,gh,gh,gh | depart,roundabout turn sharp left exit-3,notification right,arrive |
| f,a | ef,ab,ab | depart,roundabout turn straight exit-2,arrive |
| f,d | ef,cd,cd | depart,roundabout turn right exit-1,arrive |
| h,a | gh,ab,ab | depart,roundabout turn left exit-2,arrive |
| h,d | gh,cd,cd | depart,roundabout turn straight exit-1,arrive |
| h,f | gh,bgecb,ef,ef | depart,roundabout turn right exit-1,notification right,arrive |
| 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 |
+86 -86
View File
@@ -56,19 +56,19 @@ 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
Given the node map
@@ -122,19 +122,19 @@ Feature: Basic Roundabout
| bcegb | roundabout |
When I route I should get
| waypoints | route | turns |
| b,d | bcegb,cd,cd | depart,roundabout-exit-1,arrive |
| b,f | bcegb,ef,ef | depart,roundabout-exit-2,arrive |
| b,h | bcegb,gh,gh | depart,roundabout-exit-3,arrive |
| c,f | bcegb,ef,ef | depart,roundabout-exit-1,arrive |
| c,h | bcegb,gh,gh | depart,roundabout-exit-2,arrive |
| c,a | bcegb,ab,ab | depart,roundabout-exit-3,arrive |
| e,h | bcegb,gh,gh | depart,roundabout-exit-1,arrive |
| e,a | bcegb,ab,ab | depart,roundabout-exit-2,arrive |
| e,d | bcegb,cd,cd | depart,roundabout-exit-3,arrive |
| g,a | bcegb,ab,ab | depart,roundabout-exit-1,arrive |
| g,d | bcegb,cd,cd | depart,roundabout-exit-2,arrive |
| g,f | bcegb,ef,ef | depart,roundabout-exit-3,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,roundabout-exit-1,arrive |
| a,l | abc,jkl,jkl | depart,roundabout-exit-2,arrive |
| a,i | abc,ghi,ghi | depart,roundabout-exit-3,arrive |
| a,f | abc,def,def | depart,roundabout-exit-4,arrive |
| d,f | def,def,def | depart,roundabout-exit-1,arrive |
| d,c | def,abc,abc | depart,roundabout-exit-2,arrive |
| d,l | def,jkl,jkl | depart,roundabout-exit-3,arrive |
| d,i | def,ghi,ghi | depart,roundabout-exit-4,arrive |
| g,i | ghi,ghi,ghi | depart,roundabout-exit-1,arrive |
| g,f | ghi,def,def | depart,roundabout-exit-2,arrive |
| g,c | ghi,abc,abc | depart,roundabout-exit-3,arrive |
| g,l | ghi,jkl,jkl | depart,roundabout-exit-4,arrive |
| j,l | jkl,jkl,jkl | depart,roundabout-exit-1,arrive |
| j,i | jkl,ghi,ghi | depart,roundabout-exit-2,arrive |
| j,f | jkl,def,def | depart,roundabout-exit-3,arrive |
| j,c | jkl,abc,abc | depart,roundabout-exit-4,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 | depart,roundabout-exit-4,arrive |
| a,l | abc,jkl,jkl | depart,roundabout-exit-1,arrive |
| a,i | abc,ghi,ghi | depart,roundabout-exit-2,arrive |
| a,f | abc,def,def | depart,roundabout-exit-3,arrive |
| d,f | def,def,def | depart,roundabout-exit-4,arrive |
| d,c | def,abc,abc | depart,roundabout-exit-1,arrive |
| d,l | def,jkl,jkl | depart,roundabout-exit-2,arrive |
| d,i | def,ghi,ghi | depart,roundabout-exit-3,arrive |
| g,i | ghi,ghi,ghi | depart,roundabout-exit-4,arrive |
| g,f | ghi,def,def | depart,roundabout-exit-1,arrive |
| g,c | ghi,abc,abc | depart,roundabout-exit-2,arrive |
| g,l | ghi,jkl,jkl | depart,roundabout-exit-3,arrive |
| j,l | jkl,jkl,jkl | depart,roundabout-exit-4,arrive |
| j,i | jkl,ghi,ghi | depart,roundabout-exit-1,arrive |
| j,f | jkl,def,def | depart,roundabout-exit-2,arrive |
| j,c | jkl,abc,abc | depart,roundabout-exit-3,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
@@ -368,19 +368,19 @@ Feature: Basic Roundabout
| bigecb | roundabout |
When I route I should get
| waypoints | route | turns |
| a,d | ab,cd,cd | depart,roundabout-exit-4,arrive |
| a,f | ab,ef,ef | depart,roundabout-exit-3,arrive |
| a,h | ab,gh,gh | depart,roundabout-exit-2,arrive |
| d,f | cd,ef,ef | depart,roundabout-exit-4,arrive |
| d,h | cd,gh,gh | depart,roundabout-exit-3,arrive |
| d,a | cd,ab,ab | depart,roundabout-exit-1,arrive |
| f,h | ef,gh,gh | depart,roundabout-exit-4,arrive |
| f,a | ef,ab,ab | depart,roundabout-exit-2,arrive |
| f,d | ef,cd,cd | depart,roundabout-exit-1,arrive |
| h,a | gh,ab,ab | depart,roundabout-exit-3,arrive |
| h,d | gh,cd,cd | depart,roundabout-exit-2,arrive |
| h,f | gh,ef,ef | depart,roundabout-exit-1,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 | depart,roundabout-exit-3,arrive |
| a,f | ab,ef,ef | depart,roundabout-exit-2,arrive |
| a,h | ab,gh,gh | depart,roundabout-exit-1,arrive |
| d,f | cd,ef,ef | depart,roundabout-exit-3,arrive |
| d,h | cd,gh,gh | depart,roundabout-exit-2,arrive |
| d,a | cd,ab,ab | depart,roundabout-exit-1,arrive |
| f,h | ef,gh,gh | depart,roundabout-exit-3,arrive |
| f,a | ef,ab,ab | depart,roundabout-exit-2,arrive |
| f,d | ef,cd,cd | depart,roundabout-exit-1,arrive |
| h,a | gh,ab,ab | depart,roundabout-exit-3,arrive |
| h,d | gh,cd,cd | depart,roundabout-exit-2,arrive |
| h,f | gh,ef,ef | depart,roundabout-exit-1,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
+150 -151
View File
@@ -57,10 +57,10 @@ Feature: Basic Roundabout
| bcegb | roundabout | primary |
When I route I should get
| waypoints | route | turns |
| a,d | ab,cd,cd | depart,roundabout-exit-1,arrive |
| a,h | ab,gh,gh | depart,roundabout-exit-2,arrive |
| a,f | ab,ef,ef | depart,roundabout-exit-2,arrive |
| waypoints | route | turns |
| a,d | ab,cd,cd,cd | depart,roundabout-exit-1,exit roundabout right,arrive |
| a,h | ab,gh,gh,gh | depart,roundabout-exit-2,exit roundabout straight,arrive |
| a,f | ab,ef,ef,ef | depart,roundabout-exit-2,exit roundabout right,arrive |
#2927
Scenario: Only Roundabout
@@ -98,19 +98,19 @@ Feature: Basic Roundabout
| bcegb | roundabout |
When I route I should get
| waypoints | route | turns |
| b,d | bcegb,cd,cd | depart,roundabout-exit-1,arrive |
| b,f | bcegb,ef,ef | depart,roundabout-exit-2,arrive |
| b,h | bcegb,gh,gh | depart,roundabout-exit-3,arrive |
| c,f | bcegb,ef,ef | depart,roundabout-exit-1,arrive |
| c,h | bcegb,gh,gh | depart,roundabout-exit-2,arrive |
| c,a | bcegb,ab,ab | depart,roundabout-exit-3,arrive |
| e,h | bcegb,gh,gh | depart,roundabout-exit-1,arrive |
| e,a | bcegb,ab,ab | depart,roundabout-exit-2,arrive |
| e,d | bcegb,cd,cd | depart,roundabout-exit-3,arrive |
| g,a | bcegb,ab,ab | depart,roundabout-exit-1,arrive |
| g,d | bcegb,cd,cd | depart,roundabout-exit-2,arrive |
| g,f | bcegb,ef,ef | depart,roundabout-exit-3,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
@@ -165,23 +165,23 @@ Feature: Basic Roundabout
| bkheb | roundabout | yes |
When I route I should get
| waypoints | route | turns |
| a,c | abc,abc,abc | depart,roundabout-exit-1,arrive |
| a,l | abc,jkl,jkl | depart,roundabout-exit-2,arrive |
| a,i | abc,ghi,ghi | depart,roundabout-exit-3,arrive |
| a,f | abc,def,def | depart,roundabout-exit-4,arrive |
| d,f | def,def,def | depart,roundabout-exit-1,arrive |
| d,c | def,abc,abc | depart,roundabout-exit-2,arrive |
| d,l | def,jkl,jkl | depart,roundabout-exit-3,arrive |
| d,i | def,ghi,ghi | depart,roundabout-exit-4,arrive |
| g,i | ghi,ghi,ghi | depart,roundabout-exit-1,arrive |
| g,f | ghi,def,def | depart,roundabout-exit-2,arrive |
| g,c | ghi,abc,abc | depart,roundabout-exit-3,arrive |
| g,l | ghi,jkl,jkl | depart,roundabout-exit-4,arrive |
| j,l | jkl,jkl,jkl | depart,roundabout-exit-1,arrive |
| j,i | jkl,ghi,ghi | depart,roundabout-exit-2,arrive |
| j,f | jkl,def,def | depart,roundabout-exit-3,arrive |
| j,c | jkl,abc,abc | depart,roundabout-exit-4,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: Mixed Entry and Exit - segregated roads
Given the node map
@@ -204,23 +204,23 @@ Feature: Basic Roundabout
| bkheb | roundabout | yes |
When I route I should get
| waypoints | route | turns |
| a,c | abc,abc,abc | depart,roundabout-exit-4,arrive |
| a,l | abc,jkl,jkl | depart,roundabout-exit-1,arrive |
| a,i | abc,ghi,ghi | depart,roundabout-exit-2,arrive |
| a,f | abc,def,def | depart,roundabout-exit-3,arrive |
| d,f | def,def,def | depart,roundabout-exit-4,arrive |
| d,c | def,abc,abc | depart,roundabout-exit-1,arrive |
| d,l | def,jkl,jkl | depart,roundabout-exit-2,arrive |
| d,i | def,ghi,ghi | depart,roundabout-exit-3,arrive |
| g,i | ghi,ghi,ghi | depart,roundabout-exit-4,arrive |
| g,f | ghi,def,def | depart,roundabout-exit-1,arrive |
| g,c | ghi,abc,abc | depart,roundabout-exit-2,arrive |
| g,l | ghi,jkl,jkl | depart,roundabout-exit-3,arrive |
| j,l | jkl,jkl,jkl | depart,roundabout-exit-4,arrive |
| j,i | jkl,ghi,ghi | depart,roundabout-exit-1,arrive |
| j,f | jkl,def,def | depart,roundabout-exit-2,arrive |
| j,c | jkl,abc,abc | depart,roundabout-exit-3,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: Mixed Entry and Exit - clockwise order
Given the node map
@@ -241,23 +241,23 @@ Feature: Basic Roundabout
| behkb | roundabout | yes |
When I route I should get
| waypoints | route | turns |
| a,c | abc,abc,abc | depart,roundabout-exit-4,arrive |
| a,l | abc,jkl,jkl | depart,roundabout-exit-3,arrive |
| a,i | abc,ghi,ghi | depart,roundabout-exit-2,arrive |
| a,f | abc,def,def | depart,roundabout-exit-1,arrive |
| d,f | def,def,def | depart,roundabout-exit-4,arrive |
| d,c | def,abc,abc | depart,roundabout-exit-3,arrive |
| d,l | def,jkl,jkl | depart,roundabout-exit-2,arrive |
| d,i | def,ghi,ghi | depart,roundabout-exit-1,arrive |
| g,i | ghi,ghi,ghi | depart,roundabout-exit-4,arrive |
| g,f | ghi,def,def | depart,roundabout-exit-3,arrive |
| g,c | ghi,abc,abc | depart,roundabout-exit-2,arrive |
| g,l | ghi,jkl,jkl | depart,roundabout-exit-1,arrive |
| j,l | jkl,jkl,jkl | depart,roundabout-exit-4,arrive |
| j,i | jkl,ghi,ghi | depart,roundabout-exit-3,arrive |
| j,f | jkl,def,def | depart,roundabout-exit-2,arrive |
| j,c | jkl,abc,abc | depart,roundabout-exit-1,arrive |
| waypoints | route | turns |
| a,c | abc,abc,abc,abc | depart,roundabout-exit-4,exit roundabout left,arrive |
| a,l | abc,jkl,jkl,jkl | depart,roundabout-exit-3,exit roundabout left,arrive |
| a,i | abc,ghi,ghi,ghi | depart,roundabout-exit-2,exit roundabout left,arrive |
| a,f | abc,def,def,def | depart,roundabout-exit-1,exit roundabout left,arrive |
| d,f | def,def,def,def | depart,roundabout-exit-4,exit roundabout left,arrive |
| d,c | def,abc,abc,abc | depart,roundabout-exit-3,exit roundabout left,arrive |
| d,l | def,jkl,jkl,jkl | depart,roundabout-exit-2,exit roundabout left,arrive |
| d,i | def,ghi,ghi,ghi | depart,roundabout-exit-1,exit roundabout left,arrive |
| g,i | ghi,ghi,ghi,ghi | depart,roundabout-exit-4,exit roundabout left,arrive |
| g,f | ghi,def,def,def | depart,roundabout-exit-3,exit roundabout left,arrive |
| g,c | ghi,abc,abc,abc | depart,roundabout-exit-2,exit roundabout left,arrive |
| g,l | ghi,jkl,jkl,jkl | depart,roundabout-exit-1,exit roundabout left,arrive |
| j,l | jkl,jkl,jkl,jkl | depart,roundabout-exit-4,exit roundabout left,arrive |
| j,i | jkl,ghi,ghi,ghi | depart,roundabout-exit-3,exit roundabout left,arrive |
| j,f | jkl,def,def,def | depart,roundabout-exit-2,exit roundabout left,arrive |
| j,c | jkl,abc,abc,abc | depart,roundabout-exit-1,exit roundabout left,arrive |
Scenario: Mixed Entry and Exit - segregated roads, different names
Given the node map
@@ -284,23 +284,23 @@ Feature: Basic Roundabout
| bkheb | roundabout | yes |
When I route I should get
| waypoints | route | turns |
| a,c | ab,bc,bc | depart,roundabout-exit-4,arrive |
| a,l | ab,kl,kl | depart,roundabout-exit-1,arrive |
| a,i | ab,hi,hi | depart,roundabout-exit-2,arrive |
| a,f | ab,ef,ef | depart,roundabout-exit-3,arrive |
| d,f | de,ef,ef | depart,roundabout-exit-4,arrive |
| d,c | de,bc,bc | depart,roundabout-exit-1,arrive |
| d,l | de,kl,kl | depart,roundabout-exit-2,arrive |
| d,i | de,hi,hi | depart,roundabout-exit-3,arrive |
| g,i | gh,hi,hi | depart,roundabout-exit-4,arrive |
| g,f | gh,ef,ef | depart,roundabout-exit-1,arrive |
| g,c | gh,bc,bc | depart,roundabout-exit-2,arrive |
| g,l | gh,kl,kl | depart,roundabout-exit-3,arrive |
| j,l | jk,kl,kl | depart,roundabout-exit-4,arrive |
| j,i | jk,hi,hi | depart,roundabout-exit-1,arrive |
| j,f | jk,ef,ef | depart,roundabout-exit-2,arrive |
| j,c | jk,bc,bc | depart,roundabout-exit-3,arrive |
| waypoints | route | turns |
| a,c | ab,bc,bc,bc | depart,roundabout-exit-4,exit roundabout right,arrive |
| a,l | ab,kl,kl,kl | depart,roundabout-exit-1,exit roundabout right,arrive |
| a,i | ab,hi,hi,hi | depart,roundabout-exit-2,exit roundabout right,arrive |
| a,f | ab,ef,ef,ef | depart,roundabout-exit-3,exit roundabout right,arrive |
| d,f | de,ef,ef,ef | depart,roundabout-exit-4,exit roundabout right,arrive |
| d,c | de,bc,bc,bc | depart,roundabout-exit-1,exit roundabout right,arrive |
| d,l | de,kl,kl,kl | depart,roundabout-exit-2,exit roundabout right,arrive |
| d,i | de,hi,hi,hi | depart,roundabout-exit-3,exit roundabout right,arrive |
| g,i | gh,hi,hi,hi | depart,roundabout-exit-4,exit roundabout right,arrive |
| g,f | gh,ef,ef,ef | depart,roundabout-exit-1,exit roundabout right,arrive |
| g,c | gh,bc,bc,bc | depart,roundabout-exit-2,exit roundabout right,arrive |
| g,l | gh,kl,kl,kl | depart,roundabout-exit-3,exit roundabout right,arrive |
| j,l | jk,kl,kl,kl | depart,roundabout-exit-4,exit roundabout right,arrive |
| j,i | jk,hi,hi,hi | depart,roundabout-exit-1,exit roundabout right,arrive |
| j,f | jk,ef,ef,ef | depart,roundabout-exit-2,exit roundabout right,arrive |
| j,c | jk,bc,bc,bc | depart,roundabout-exit-3,exit roundabout right,arrive |
Scenario: Motorway Roundabout
#See 39.933742 -75.082345
@@ -338,11 +338,10 @@ Feature: Basic Roundabout
| dmg | roundabout | | trunk_link | yes | |
When I route I should get
| waypoints | route | turns | ref |
| a,e | crescent,crescent,crescent | depart,roundabout-exit-3,arrive | US 130,US 130,US 130 |
| j,l | ,, | depart,roundabout-exit-2,arrive | NJ 38,NJ 38,NJ 38 |
| waypoints | route | turns | ref |
| a,e | crescent,crescent,crescent,crescent | depart,roundabout-exit-3,exit roundabout straight,arrive | US 130,US 130,US 130,US 130 |
| j,l | ,,, | depart,roundabout-exit-2,exit roundabout straight,arrive | NJ 38,NJ 38,NJ 38,NJ 38 |
@todo
# this test previously only passed by accident. We need to handle throughabouts correctly, since staying on massachusetts is actually
# the desired setting. Rotary instructions here are not wanted but rather no instruction at all to go through the roundabout (or add
# a throughabout instruction)
@@ -389,8 +388,8 @@ Feature: Basic Roundabout
| restriction | pi | ij | i | no_left_turn |
When I route I should get
| waypoints | route | turns |
| a,k | massachusetts,massachusetts,massachusetts,massachusetts | depart,sheridan circle-exit-2,rotary-exit-1,arrive |
| waypoints | route | turns |
| a,k | massachusetts,massachusetts,massachusetts,massachusetts | depart,sheridan circle-exit-2,exit rotary right,arrive |
#2856 - http://www.openstreetmap.org/#map=19/47.23318/-1.56563
Scenario: Linked Roundabouts
@@ -477,8 +476,8 @@ Feature: Basic Roundabout
| cdefib | roundabout | roundabout | yes | |
When I route I should get
| waypoints | route | turns |
| 1,h | roundabout,right-bot-out,right-bot-out | depart,roundabout-exit-1,arrive |
| waypoints | route | turns |
| 1,h | roundabout,right-bot-out,right-bot-out | depart,exit rotary straight,arrive |
@3254
Scenario: Driving up to and through a roundabout
@@ -495,15 +494,15 @@ Feature: Basic Roundabout
And the ways
| nodes | junction | name | highway |
| abcda | roundabout | roundabout | residential |
| abcda | roundabout | rotary | residential |
| gfi | | side | residential |
| efb | | left | residential |
| dh | | right | residential |
| ck | | bottom | residential |
When I route I should get
| waypoints | route | turns |
| e,h | left,right,right | depart,roundabout-exit-2,arrive |
| waypoints | route | turns |
| e,h | left,right,right,right | depart,rotary-exit-2,exit rotary right,arrive |
@3254
Scenario: Driving up to and through a roundabout
@@ -520,15 +519,15 @@ Feature: Basic Roundabout
And the ways
| nodes | junction | name | highway |
| abcda | roundabout | roundabout | residential |
| abcda | roundabout | rotary | residential |
| gfi | | side | residential |
| efb | | left | residential |
| dh | | right | residential |
| ck | | bottom | residential |
When I route I should get
| waypoints | route | turns |
| e,h | left,right,right | depart,roundabout-exit-2,arrive |
| waypoints | route | turns |
| e,h | left,right,right,right | depart,rotary-exit-2,exit rotary right,arrive |
@3361
Scenario: Bersarinplatz (Not a Roundabout)
@@ -565,13 +564,13 @@ Feature: Basic Roundabout
| mn | | Petersburger Strasse | B 96a | primary | yes |
When I route I should get
| waypoints | route | turns |
| a,g | Petersburger Strasse,Petersburger Strasse,Petersburger Strasse | depart,Bersarinplatz-exit-2,arrive |
| d,g | Weidenweg,Petersburger Strasse,Petersburger Strasse | depart,Bersarinplatz-exit-1,arrive |
| i,k | Petersburger Strasse,Rigaer Strasse,Rigaer Strasse | depart,Bersarinplatz-exit-1,arrive |
| i,n | Petersburger Strasse,Petersburger Strasse,Petersburger Strasse | depart,Bersarinplatz-exit-2,arrive |
| i,d | Petersburger Strasse,Weidenweg,Weidenweg | depart,Bersarinplatz-exit-3,arrive |
| i,g | Petersburger Strasse,Petersburger Strasse,Petersburger Strasse | depart,Bersarinplatz-exit-4,arrive |
| waypoints | route | turns |
| a,g | Petersburger Strasse,Petersburger Strasse,Petersburger Strasse,Petersburger Strasse | depart,Bersarinplatz-exit-2,exit rotary straight,arrive |
| d,g | Weidenweg,Petersburger Strasse,Petersburger Strasse,Petersburger Strasse | depart,Bersarinplatz-exit-1,exit rotary straight,arrive |
| i,k | Petersburger Strasse,Rigaer Strasse,Rigaer Strasse,Rigaer Strasse | depart,Bersarinplatz-exit-1,exit rotary right,arrive |
| i,n | Petersburger Strasse,Petersburger Strasse,Petersburger Strasse,Petersburger Strasse | depart,Bersarinplatz-exit-2,exit rotary straight,arrive |
| i,d | Petersburger Strasse,Weidenweg,Weidenweg,Weidenweg | depart,Bersarinplatz-exit-3,exit rotary right,arrive |
| i,g | Petersburger Strasse,Petersburger Strasse,Petersburger Strasse,Petersburger Strasse | depart,Bersarinplatz-exit-4,exit rotary straight,arrive |
@turboroundabout
# http://www.openstreetmap.org/?mlat=48.782118&mlon=8.194456&zoom=16#map=19/48.78216/8.19457
@@ -606,11 +605,11 @@ Feature: Basic Roundabout
| ob | trunk | yes | roundabout | Europaplatz | |
When I route I should get
| waypoints | route | turns |
| a,d | ,Europastrasse,Europastrasse | depart,Europaplatz-exit-1,arrive |
| a,h | ,Allee Cite,Allee Cite | depart,Europaplatz-exit-2,arrive |
| a,l | ,Europastrasse,Europastrasse | depart,Europaplatz-exit-3,arrive |
| a,p | ,, | depart,Europaplatz-exit-4,arrive |
| waypoints | route | turns |
| a,d | ,Europastrasse,Europastrasse,Europastrasse | depart,Europaplatz-exit-1,exit rotary right,arrive |
| a,h | ,Allee Cite,Allee Cite,Allee Cite | depart,Europaplatz-exit-2,exit rotary right,arrive |
| a,l | ,Europastrasse,Europastrasse,Europastrasse | depart,Europaplatz-exit-3,exit rotary right,arrive |
| a,p | ,,, | depart,Europaplatz-exit-4,exit rotary right,arrive |
@turboroundabout
# http://www.openstreetmap.org/?mlat=50.180039&mlon=8.474939&zoom=16#map=19/50.17999/8.47506
@@ -658,14 +657,14 @@ Feature: Basic Roundabout
| wb | primary | yes | roundabout | | through\|through;right |
When I route I should get
| waypoints | route | turns |
| a,w | Le-Cannet-Rocheville-Strasse,, | depart,roundabout-exit-undefined,arrive |
| a,r | Le-Cannet-Rocheville-Strasse,, | depart,roundabout-exit-4,arrive |
| a,f | Le-Cannet-Rocheville-Strasse,Frankfurter Strasse,Frankfurter Strasse | depart,roundabout-exit-1,arrive |
| a,h | Le-Cannet-Rocheville-Strasse,Bischof-Kaller-Strasse,Bischof-Kaller-Strasse | depart,roundabout-exit-2,arrive |
| u,r | ,, | depart,roundabout-exit-5,arrive |
| j,h | Bischof-Kaller-Strasse,Bischof-Kaller-Strasse,Bischof-Kaller-Strasse | depart,roundabout-exit-5,arrive |
| n,m | , | depart,arrive |
| waypoints | route | turns |
| a,w | Le-Cannet-Rocheville-Strasse,, | depart,roundabout-exit-undefined,arrive |
| a,r | Le-Cannet-Rocheville-Strasse,,, | depart,roundabout-exit-4,exit roundabout right,arrive |
| a,f | Le-Cannet-Rocheville-Strasse,Frankfurter Strasse,Frankfurter Strasse,Frankfurter Strasse | depart,roundabout-exit-1,exit roundabout right,arrive |
| a,h | Le-Cannet-Rocheville-Strasse,Bischof-Kaller-Strasse,Bischof-Kaller-Strasse,Bischof-Kaller-Strasse | depart,roundabout-exit-2,exit roundabout right,arrive |
| u,r | ,,, | depart,roundabout-exit-5,exit roundabout right,arrive |
| j,h | Bischof-Kaller-Strasse,Bischof-Kaller-Strasse,Bischof-Kaller-Strasse,Bischof-Kaller-Strasse | depart,roundabout-exit-5,exit roundabout right,arrive |
| n,m | , | depart,arrive |
@turboroundabout
# http://www.openstreetmap.org/?mlat=47.57723&mlon=7.796765&zoom=16#map=19/47.57720/7.79711
@@ -708,17 +707,16 @@ Feature: Basic Roundabout
| la | primary | yes | roundabout |
When I route I should get
| waypoints | route | turns |
| w,r | wk,ar,ar | depart,roundabout-exit-1,arrive |
| w,s | wk,ds,ds | depart,roundabout-exit-2,arrive |
| w,t | wk,ft,ft | depart,roundabout-exit-3,arrive |
| w,v | wk,hv,hv | depart,roundabout-exit-4,arrive |
| u,v | ug,hv,hv | depart,roundabout-exit-1,arrive |
| u,w | ug,jw,jw | depart,roundabout-exit-2,arrive |
| u,r | ug,ar,ar | depart,roundabout-exit-3,arrive |
| u,s | ug,ds,ds | depart,roundabout-exit-4,arrive |
| u,t | ug,ft,ft | depart,roundabout-exit-5,arrive |
| waypoints | route | turns |
| w,r | wk,ar,ar,ar | depart,roundabout-exit-1,exit roundabout slight right,arrive |
| w,s | wk,ds,ds,ds | depart,roundabout-exit-2,exit roundabout right,arrive |
| w,t | wk,ft,ft,ft | depart,roundabout-exit-3,exit roundabout right,arrive |
| w,v | wk,hv,hv,hv | depart,roundabout-exit-4,exit roundabout straight,arrive |
| u,v | ug,hv,hv,hv | depart,roundabout-exit-1,exit roundabout straight,arrive |
| u,w | ug,jw,jw,jw | depart,roundabout-exit-2,exit roundabout slight right,arrive |
| u,r | ug,ar,ar,ar | depart,roundabout-exit-3,exit roundabout slight right,arrive |
| u,s | ug,ds,ds,ds | depart,roundabout-exit-4,exit roundabout right,arrive |
| u,t | ug,ft,ft,ft | depart,roundabout-exit-5,exit roundabout right,arrive |
@3762
Scenario: Only Enter
@@ -742,8 +740,8 @@ Feature: Basic Roundabout
| bcjdeb | roundabout | |
When I route I should get
| waypoints | route | turns |
| a,h | ab,ef,ef,fh,fh | depart,roundabout-exit-4,notification slight right,notification straight,arrive |
| waypoints | route | turns |
| a,h | ab,ef,ef,fh,fh | depart,roundabout-exit-4,exit roundabout slight right,notification straight,arrive |
Scenario: Drive through roundabout
@@ -762,12 +760,12 @@ Feature: Basic Roundabout
| gch | | yes |
When I route I should get
| waypoints | bearings | route | turns |
| e,f | 90 90 | edf,edf | depart,arrive |
| e,h | 90 135 | edf,gch,gch | depart,roundabout-exit-2,arrive |
| g,f | 45 90 | gch,edf,edf | depart,roundabout-exit-2,arrive |
| g,h | 45 135 | gch,gch,gch | depart,roundabout-exit-1,arrive |
| e,e | 90 270 | edf,edf,edf | depart,roundabout-exit-3,arrive |
| waypoints | bearings | route | turns |
| e,f | 90 90 | edf,edf | depart,arrive |
| e,h | 90 135 | edf,gch,gch,gch | depart,roundabout-exit-2,exit roundabout straight,arrive |
| g,f | 45 90 | gch,edf,edf,edf | depart,roundabout-exit-2,exit roundabout right,arrive |
| g,h | 45 135 | gch,gch,gch | depart,exit roundabout right,arrive |
| e,e | 90 270 | edf,edf,edf,edf | depart,roundabout-exit-3,exit roundabout sharp left,arrive |
Scenario: CCW and CW roundabouts with overlaps
Given the node map
@@ -787,12 +785,13 @@ Feature: Basic Roundabout
| kg | tertiary | |
| hl | tertiary | |
# the turn angles here are quite strange, so we do get uturns for exiting
When I route I should get
| from | to | route | turns | distance |
| e | f | ed,af,af | depart,roundabout-exit-1,arrive | 80.1m |
| f | e | af,ed,ed | depart,roundabout-exit-1,arrive | 120.1m |
| k | l | kg,hl,hl | depart,roundabout-exit-1,arrive | 80.1m |
| l | k | hl,kg,kg | depart,roundabout-exit-1,arrive | 120.1m |
| from | to | route | turns | distance |
| e | f | ed,af,af,af | depart,roundabout-exit-1,exit roundabout left,arrive | 80.1m |
| f | e | af,ed,ed,ed | depart,roundabout-exit-1,exit roundabout uturn,arrive | 120.1m |
| k | l | kg,hl,hl,hl | depart,roundabout-exit-1,exit roundabout right,arrive | 80.1m |
| l | k | hl,kg,kg,kg | depart,roundabout-exit-1,exit roundabout uturn,arrive | 120.1m |
@4030 @4075
Scenario: Service roundabout with service exits
@@ -811,11 +810,11 @@ Feature: Basic Roundabout
| bh | service | |
When I route I should get
| from | to | route | turns |
| 1 | e | abcda,ce,ce | depart,roundabout-exit-1,arrive |
| 1 | f | abcda,df,df | depart,roundabout-exit-2,arrive |
| 1 | g | abcda,ag,ag | depart,roundabout-exit-3,arrive |
| 1 | h | abcda,bh,bh | depart,roundabout-exit-4,arrive |
| from | to | route | turns |
| 1 | e | abcda,ce,ce | depart,exit roundabout straight,arrive |
| 1 | f | abcda,df,df | depart,exit roundabout right,arrive |
| 1 | g | abcda,ag,ag | depart,exit roundabout straight,arrive |
| 1 | h | abcda,bh,bh | depart,exit roundabout right,arrive |
Scenario: Collapsing a sliproad step after roundabouts
Given the node map
@@ -843,6 +842,6 @@ Feature: Basic Roundabout
When I route I should get
| from | to | route | turns | distance |
| e | k | ebds,ufghl,jhik,jhik | depart,rstur-exit-2,turn right,arrive | 189.1m |
| 1 | k | ebds,ufghl,jhik,jhik | depart,rstur-exit-2,turn right,arrive | 159.1m |
| from | to | route | turns | distance |
| e | k | ebds,ufghl,ufghl,jhik,jhik | depart,rstur-exit-2,exit rotary right,turn right,arrive | 189.1m |
| 1 | k | ebds,ufghl,ufghl,jhik,jhik | depart,rstur-exit-2,exit rotary right,turn right,arrive | 159.1m |
+6 -6
View File
@@ -248,8 +248,8 @@ Feature: Simple Turns
| bcdefghijklmnob | residential | road | 1 | yes | roundabout |
When I route I should get
| waypoints | route | turns | intersections |
| a,p | road,road,road | depart,roundabout turn straight exit-1,arrive | true:90;true:165 false:270 false:345,true:90 false:180 true:345;true:270 |
| waypoints | route | turns | intersections |
| a,p | road,road,road | depart,roundabout turn straight exit-1,arrive | true:90;true:165 false:270 false:345,true:90 false:180 true:345;true:270 |
Scenario: Splitting Road with many lanes
Given the node map
@@ -1317,11 +1317,11 @@ Feature: Simple Turns
#https://github.com/Project-OSRM/osrm-backend/pull/3469#issuecomment-270806580
Scenario: Oszillating Lower Priority Road
#Given the node map
# """
# a -db c
#Given the node map
# """
# a -db c
# f
# """
# """
Given the node locations
| node | lat | lon | # |
| a | 1.0 | 1.0 | |
+1 -1
View File
@@ -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
+3
View File
@@ -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"
+1 -1
View File
@@ -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+)/
+1 -1
View File
@@ -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
+3
View File
@@ -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
+1 -1
View File
@@ -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+)/
+3
View File
@@ -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"
+1 -1
View File
@@ -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+)/
+3
View File
@@ -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"
+1 -1
View File
@@ -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+)/
+3
View File
@@ -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"
+1 -1
View File
@@ -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+)/
+4 -1
View File
@@ -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
"""
+1 -1
View File
@@ -167,7 +167,7 @@ Feature: Approach parameter
| s | e | unrestricted curb | |
Scenario: UTurn test, router can found a route because he can use the roundabout
Scenario: UTurn test, router can find a route because he can use the roundabout
Given the node map
"""
h
+11 -11
View File
@@ -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 |
+99
View File
@@ -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. |
-1
View File
@@ -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
+33
View File
@@ -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
+3 -6
View File
@@ -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
+4 -6
View File
@@ -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>;
+26
View File
@@ -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
+42 -11
View File
@@ -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
+23 -409
View 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;
}
+11
View File
@@ -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;
+44 -12
View File
@@ -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 ||
+31
View File
@@ -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
+7 -6
View File
@@ -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)
{
}
+59
View File
@@ -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
+34
View File
@@ -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
+12
View File
@@ -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
{
};
}
}
}
+4 -2
View File
@@ -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_)
{
}
+9
View File
@@ -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);
+3 -3
View File
@@ -167,12 +167,12 @@ class RouteAPI : public BaseAPI
* the overall response consistent.
*
* CAUTION: order of post-processing steps is important
* - postProcess must be called before collapseTurnInstructions that expects
* post-processed roundabouts without Exit instructions
* - handleRoundabouts must be called before collapseTurnInstructions that
* expects post-processed roundabouts
*/
guidance::trimShortSegments(steps, leg_geometry);
leg.steps = guidance::postProcess(std::move(steps));
leg.steps = guidance::handleRoundabouts(std::move(steps));
leg.steps = guidance::collapseTurnInstructions(std::move(leg.steps));
leg.steps = guidance::anticipateLaneChange(std::move(leg.steps));
leg.steps = guidance::buildIntersections(std::move(leg.steps));
+10 -4
View File
@@ -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.
{
}
+36 -14
View File
@@ -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 &params) const
{
return facade_factory.Get(params);
}
std::shared_ptr<const Facade> Get(const api::TileParameters &params) 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,
+134
View File
@@ -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 &params) 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 &params, 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 &params, 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
+22 -7
View File
@@ -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 &params) const override final
{
return facade_factory.Get(params);
}
std::shared_ptr<const Facade> Get(const api::BaseParameters &params) 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 &params) const override final
{
return watchdog.Get(params);
}
std::shared_ptr<const Facade> Get(const api::BaseParameters &params) const override final
{
return watchdog.Get(params);
}
};
}
+14 -15
View File
@@ -85,46 +85,44 @@ template <typename Algorithm> class Engine final : public EngineInterface
Status Route(const api::RouteParameters &params,
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 &params,
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 &params,
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 &params, 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 &params,
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 &params, 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 &params) 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;
}
}
+59 -26
View File
@@ -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
+9 -4
View File
@@ -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,
+1 -1
View File
@@ -17,7 +17,7 @@ namespace guidance
// passed as none-reference to modify in-place and move out again
OSRM_ATTR_WARN_UNUSED
std::vector<RouteStep> postProcess(std::vector<RouteStep> steps);
std::vector<RouteStep> handleRoundabouts(std::vector<RouteStep> steps);
// trim initial/final segment of very short length.
// This function uses in/out parameter passing to modify both steps and geometry in place.
+13 -4
View File
@@ -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");
+26
View File
@@ -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 &params,
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
+9
View File
@@ -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;
+17
View File
@@ -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)
@@ -199,7 +199,10 @@ inline bool leavesRoundabout(const extractor::guidance::TurnInstruction instruct
inline bool staysOnRoundabout(const extractor::guidance::TurnInstruction instruction)
{
return instruction.type == extractor::guidance::TurnType::StayOnRoundabout;
return instruction.type == extractor::guidance::TurnType::StayOnRoundabout ||
instruction.type == extractor::guidance::TurnType::EnterRoundaboutAtExit ||
instruction.type == extractor::guidance::TurnType::EnterRotaryAtExit ||
instruction.type == extractor::guidance::TurnType::EnterRoundaboutIntersectionAtExit;
}
// Silent Turn Instructions are not to be mentioned to the outside world but
+24 -1
View File
@@ -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;
+33
View File
@@ -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;
+31 -15
View File
@@ -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;
+9 -8
View File
@@ -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 =
+2 -2
View File
@@ -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);
+4
View File
@@ -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;
-4
View File
@@ -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);
+11 -4
View File
@@ -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;
+65 -7
View File
@@ -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];
}
+1
View File
@@ -66,6 +66,7 @@ struct StorageConfig final : IOConfig
".osrm.ebg_nodes",
".osrm.core",
".osrm.cells",
".osrm.cell_metrics",
".osrm.mldgr",
".osrm.tld",
".osrm.tls",
+4 -3
View File
@@ -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.
+2 -2
View File
@@ -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;
};
+1 -2
View File
@@ -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"})
{
}
+20 -3
View File
@@ -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)
+95 -20
View File
@@ -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;

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