Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ea91790ae8 | |||
| bde65bd117 | |||
| 5d10e0408a | |||
| 872e055fe9 | |||
| 42866310d2 | |||
| 0c12bd784c | |||
| dc07382099 | |||
| bf2fc5accf | |||
| 8e8ac128c6 | |||
| 3c57c046fd | |||
| c9be11eb39 | |||
| f217fa9cff | |||
| b265686480 | |||
| bba1e05967 | |||
| 2e38d3c170 | |||
| 020c0d1c11 | |||
| 1bc18d6275 | |||
| 4334810c71 | |||
| a307371c27 | |||
| 5351a258a9 | |||
| adadb45f02 | |||
| adee18468c | |||
| 9a9abf4c11 | |||
| 331eeca4c1 | |||
| f8d6e4750a | |||
| cf505386f9 | |||
| e346e9ae07 | |||
| ad37fcce2d | |||
| 0e19f07c3c | |||
| 59c0795c7f | |||
| 3653e51f67 | |||
| 8c24507f8f | |||
| 3e444777e0 | |||
| 670cd8813c |
@@ -15,6 +15,7 @@ branches:
|
||||
- master
|
||||
# enable building tags
|
||||
- /^v\d+\.\d+(\.\d+)?(-\S*)?$/
|
||||
- "5.17"
|
||||
|
||||
cache:
|
||||
yarn: true
|
||||
|
||||
+4
-19
@@ -1,24 +1,9 @@
|
||||
# UNRELEASED
|
||||
- Changes from 5.18.0:
|
||||
- Optimizations:
|
||||
- CHANGED: Map matching is now almost twice as fast. [#5060](https://github.com/Project-OSRM/osrm-backend/pull/5060)
|
||||
# 5.17.3
|
||||
- Changes from 5.17.2:
|
||||
- Bugfixes:
|
||||
- FIXED: collapsing of ExitRoundabout instructions [#5114](https://github.com/Project-OSRM/osrm-backend/issues/5114)
|
||||
- FIXED: negative distances in table plugin annotation [#5106](https://github.com/Project-OSRM/osrm-backend/issues/5106)
|
||||
- FIXED: Increased probability of returning alternatives and quality of the ones returned for MLD. [#5048](https://github.com/Project-OSRM/osrm-backend/issues/5048)
|
||||
|
||||
# 5.18.0
|
||||
- Changes from 5.17.0:
|
||||
- Features:
|
||||
- ADDED: `table` plugin now optionally returns `distance` matrix as part of response [#4990](https://github.com/Project-OSRM/osrm-backend/pull/4990)
|
||||
- ADDED: New optional parameter `annotations` for `table` that accepts `distance`, `duration`, or both `distance,duration` as values [#4990](https://github.com/Project-OSRM/osrm-backend/pull/4990)
|
||||
- Infrastructure:
|
||||
- ADDED: Updated libosmium and added protozero and vtzero libraries [#5037](https://github.com/Project-OSRM/osrm-backend/pull/5037)
|
||||
- CHANGED: Use vtzero library in tile plugin [#4686](https://github.com/Project-OSRM/osrm-backend/pull/4686)
|
||||
- Profile:
|
||||
- ADDED: Bicycle profile now returns classes for ferry and tunnel routes. [#5054](https://github.com/Project-OSRM/osrm-backend/pull/5054)
|
||||
- ADDED: Bicycle profile allows to exclude ferry routes (default to not enabled) [#5054](https://github.com/Project-OSRM/osrm-backend/pull/5054)
|
||||
|
||||
# 5.17.1
|
||||
# 5.17.2
|
||||
- Changes from 5.17.0:
|
||||
- Bugfixes:
|
||||
- FIXED: Do not combine a segregated edge with a roundabout [#5039](https://github.com/Project-OSRM/osrm-backend/issues/5039)
|
||||
|
||||
+13
-24
@@ -167,7 +167,7 @@ add_executable(osrm-customize src/tools/customize.cpp)
|
||||
add_executable(osrm-contract src/tools/contract.cpp)
|
||||
add_executable(osrm-routed src/tools/routed.cpp $<TARGET_OBJECTS:SERVER> $<TARGET_OBJECTS:UTIL>)
|
||||
add_executable(osrm-datastore src/tools/store.cpp $<TARGET_OBJECTS:MICROTAR> $<TARGET_OBJECTS:UTIL>)
|
||||
add_library(osrm src/osrm/osrm.cpp $<TARGET_OBJECTS:ENGINE> $<TARGET_OBJECTS:STORAGE> $<TARGET_OBJECTS:MICROTAR> $<TARGET_OBJECTS:UTIL>)
|
||||
add_library(osrm src/osrm/osrm.cpp $<TARGET_OBJECTS:ENGINE> $<TARGET_OBJECTS:STORAGE> $<TARGET_OBJECTS:MICROTAR> $<TARGET_OBJECTS:UTIL> )
|
||||
add_library(osrm_contract src/osrm/contractor.cpp $<TARGET_OBJECTS:CONTRACTOR> $<TARGET_OBJECTS:UTIL>)
|
||||
add_library(osrm_extract src/osrm/extractor.cpp $<TARGET_OBJECTS:EXTRACTOR> $<TARGET_OBJECTS:MICROTAR> $<TARGET_OBJECTS:UTIL>)
|
||||
add_library(osrm_guidance $<TARGET_OBJECTS:GUIDANCE> $<TARGET_OBJECTS:UTIL>)
|
||||
@@ -416,30 +416,11 @@ if(UNIX AND NOT APPLE)
|
||||
set(MAYBE_RT_LIBRARY -lrt)
|
||||
endif()
|
||||
|
||||
# Disallow deprecated protozero APIs
|
||||
add_definitions(-DPROTOZERO_STRICT_API)
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
# Third-party libraries
|
||||
set(RAPIDJSON_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/rapidjson/include")
|
||||
include_directories(SYSTEM ${RAPIDJSON_INCLUDE_DIR})
|
||||
|
||||
set(MICROTAR_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/microtar/src")
|
||||
include_directories(SYSTEM ${MICROTAR_INCLUDE_DIR})
|
||||
|
||||
set(MBXGEOM_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/geometry.hpp-0.9.2/include")
|
||||
include_directories(SYSTEM ${MBXGEOM_INCLUDE_DIR})
|
||||
set(CHEAPRULER_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/cheap-ruler-cpp-2.5.4/include")
|
||||
include_directories(SYSTEM ${CHEAPRULER_INCLUDE_DIR})
|
||||
|
||||
add_library(MICROTAR OBJECT "${CMAKE_CURRENT_SOURCE_DIR}/third_party/microtar/src/microtar.c")
|
||||
set_property(TARGET MICROTAR PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
set(PROTOZERO_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/protozero/include")
|
||||
include_directories(SYSTEM ${PROTOZERO_INCLUDE_DIR})
|
||||
|
||||
set(VTZERO_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/vtzero/include")
|
||||
include_directories(SYSTEM ${VTZERO_INCLUDE_DIR})
|
||||
|
||||
|
||||
# if mason is enabled no find_package calls are made
|
||||
# to ensure that we are only compiling and linking against
|
||||
# fully portable mason packages
|
||||
@@ -573,6 +554,14 @@ else()
|
||||
include_directories(SYSTEM ${OSMIUM_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
set(RAPIDJSON_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/rapidjson/include")
|
||||
include_directories(SYSTEM ${RAPIDJSON_INCLUDE_DIR})
|
||||
|
||||
set(MICROTAR_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/microtar/src")
|
||||
include_directories(SYSTEM ${MICROTAR_INCLUDE_DIR})
|
||||
add_library(MICROTAR OBJECT "${CMAKE_CURRENT_SOURCE_DIR}/third_party/microtar/src/microtar.c")
|
||||
set_property(TARGET MICROTAR PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
# prefix compilation with ccache by default if available and on clang or gcc
|
||||
if(ENABLE_CCACHE AND (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU"))
|
||||
find_program(CCACHE_FOUND ccache)
|
||||
@@ -873,4 +862,4 @@ if (ENABLE_NODE_BINDINGS)
|
||||
endforeach()
|
||||
add_library(check-headers STATIC EXCLUDE_FROM_ALL ${sources})
|
||||
set_target_properties(check-headers PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${check_headers_dir})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -9,7 +9,7 @@ High performance routing engine written in C++14 designed to run on OpenStreetMa
|
||||
The following services are available via HTTP API, C++ library interface and NodeJs wrapper:
|
||||
- Nearest - Snaps coordinates to the street network and returns the nearest matches
|
||||
- Route - Finds the fastest route between coordinates
|
||||
- Table - Computes the duration or distances of the fastest route between all pairs of supplied coordinates
|
||||
- Table - Computes the duration of the fastest route between all pairs of supplied coordinates
|
||||
- Match - Snaps noisy GPS traces to the road network in the most plausible way
|
||||
- Trip - Solves the Traveling Salesman Problem using a greedy heuristic
|
||||
- Tile - Generates Mapbox Vector Tiles with internal routing metadata
|
||||
|
||||
+1
-1
@@ -3,5 +3,5 @@ module.exports = {
|
||||
verify: '--strict --tags ~@stress --tags ~@todo --tags ~@mld-only -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 ~@ch --require features/support --require features/step_definitions -f progress'
|
||||
mld: '--strict --tags ~@stress --tags ~@todo --require features/support --require features/step_definitions -f progress'
|
||||
};
|
||||
|
||||
+8
-117
@@ -222,13 +222,13 @@ curl 'http://router.project-osrm.org/route/v1/driving/13.388860,52.517037;13.397
|
||||
|
||||
### Table service
|
||||
|
||||
Computes the duration of the fastest route between all pairs of supplied coordinates. Returns the durations or distances or both between the coordinate pairs. Note that the distances are not the shortest distance between two coordinates, but rather the distances of the fastest routes. Duration is in seconds and distances is in meters.
|
||||
Computes the duration of the fastest route between all pairs of supplied coordinates.
|
||||
|
||||
```endpoint
|
||||
GET /table/v1/{profile}/{coordinates}?{sources}=[{elem}...];&{destinations}=[{elem}...]&annotations={duration|distance|duration,distance}
|
||||
GET /table/v1/{profile}/{coordinates}?{sources}=[{elem}...];&destinations=[{elem}...]
|
||||
```
|
||||
|
||||
**Options**
|
||||
**Coordinates**
|
||||
|
||||
In addition to the [general options](#general-options) the following options are supported for this service:
|
||||
|
||||
@@ -236,8 +236,6 @@ In addition to the [general options](#general-options) the following options are
|
||||
|------------|--------------------------------------------------|---------------------------------------------|
|
||||
|sources |`{index};{index}[;{index} ...]` or `all` (default)|Use location with given index as source. |
|
||||
|destinations|`{index};{index}[;{index} ...]` or `all` (default)|Use location with given index as destination.|
|
||||
|annotations |`duration` (default), `distance`, or `duration,distance`|Return the requested table or tables in response. Note that computing the `distances` table is currently only implemented for CH. If `annotations=distance` or `annotations=duration,distance` is requested when running a MLD router, a `NotImplemented` error will be returned.
|
||||
|
|
||||
|
||||
Unlike other array encoded options, the length of `sources` and `destinations` can be **smaller or equal**
|
||||
to number of input locations;
|
||||
@@ -255,23 +253,14 @@ sources=0;5;7&destinations=5;1;4;2;3;6
|
||||
#### Example Request
|
||||
|
||||
```curl
|
||||
# Returns a 3x3 duration matrix:
|
||||
# Returns a 3x3 matrix:
|
||||
curl 'http://router.project-osrm.org/table/v1/driving/13.388860,52.517037;13.397634,52.529407;13.428555,52.523219'
|
||||
|
||||
# Returns a 1x3 duration matrix
|
||||
# Returns a 1x3 matrix
|
||||
curl 'http://router.project-osrm.org/table/v1/driving/13.388860,52.517037;13.397634,52.529407;13.428555,52.523219?sources=0'
|
||||
|
||||
# Returns a asymmetric 3x2 duration matrix with from the polyline encoded locations `qikdcB}~dpXkkHz`:
|
||||
# Returns a asymmetric 3x2 matrix with from the polyline encoded locations `qikdcB}~dpXkkHz`:
|
||||
curl 'http://router.project-osrm.org/table/v1/driving/polyline(egs_Iq_aqAppHzbHulFzeMe`EuvKpnCglA)?sources=0;1;3&destinations=2;4'
|
||||
|
||||
# Returns a 3x3 duration matrix:
|
||||
curl 'http://router.project-osrm.org/table/v1/driving/13.388860,52.517037;13.397634,52.529407;13.428555,52.523219?annotations=duration'
|
||||
|
||||
# Returns a 3x3 distance matrix for CH:
|
||||
curl 'http://router.project-osrm.org/table/v1/driving/13.388860,52.517037;13.397634,52.529407;13.428555,52.523219?annotations=distance'
|
||||
|
||||
# Returns a 3x3 duration matrix and a 3x3 distance matrix for CH:
|
||||
curl 'http://router.project-osrm.org/table/v1/driving/13.388860,52.517037;13.397634,52.529407;13.428555,52.523219?annotations=distance,duration'
|
||||
```
|
||||
|
||||
**Response**
|
||||
@@ -279,115 +268,17 @@ curl 'http://router.project-osrm.org/table/v1/driving/13.388860,52.517037;13.397
|
||||
- `code` if the request was successful `Ok` otherwise see the service dependent and general status codes.
|
||||
- `durations` array of arrays that stores the matrix in row-major order. `durations[i][j]` gives the travel time from
|
||||
the i-th waypoint to the j-th waypoint. Values are given in seconds. Can be `null` if no route between `i` and `j` can be found.
|
||||
- `distances` array of arrays that stores the matrix in row-major order. `distances[i][j]` gives the travel distance from
|
||||
the i-th waypoint to the j-th waypoint. Values are given in meters. Can be `null` if no route between `i` and `j` can be found. Note that computing the `distances` table is currently only implemented for CH. If `annotations=distance` or `annotations=duration,distance` is requested when running a MLD router, a `NotImplemented` error will be returned.
|
||||
- `sources` array of `Waypoint` objects describing all sources in order
|
||||
- `destinations` array of `Waypoint` objects describing all destinations in order
|
||||
|
||||
In case of error the following `code`s are supported in addition to the general ones:
|
||||
|
||||
| Type | Description |
|
||||
|------------------|-----------------|
|
||||
| Type | Description |
|
||||
|-------------------|-----------------|
|
||||
| `NoTable` | No route found. |
|
||||
| `NotImplemented` | This request is not supported |
|
||||
|
||||
All other properties might be undefined.
|
||||
|
||||
#### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"sources": [
|
||||
{
|
||||
"location": [
|
||||
13.3888,
|
||||
52.517033
|
||||
],
|
||||
"hint": "PAMAgEVJAoAUAAAAIAAAAAcAAAAAAAAArss0Qa7LNEHiVIRA4lSEQAoAAAAQAAAABAAAAAAAAADMAAAAAEzMAKlYIQM8TMwArVghAwEA3wps52D3",
|
||||
"name": "Friedrichstraße"
|
||||
},
|
||||
{
|
||||
"location": [
|
||||
13.397631,
|
||||
52.529432
|
||||
],
|
||||
"hint": "WIQBgL6mAoAEAAAABgAAAAAAAAA7AAAAhU6PQHvHj0IAAAAAQbyYQgQAAAAGAAAAAAAAADsAAADMAAAAf27MABiJIQOCbswA_4ghAwAAXwVs52D3",
|
||||
"name": "Torstraße"
|
||||
},
|
||||
{
|
||||
"location": [
|
||||
13.428554,
|
||||
52.523239
|
||||
],
|
||||
"hint": "7UcAgP___38fAAAAUQAAACYAAABTAAAAhSQKQrXq5kKRbiZCWJo_Qx8AAABRAAAAJgAAAFMAAADMAAAASufMAOdwIQNL58wA03AhAwMAvxBs52D3",
|
||||
"name": "Platz der Vereinten Nationen"
|
||||
}
|
||||
],
|
||||
"durations": [
|
||||
[
|
||||
0,
|
||||
192.6,
|
||||
382.8
|
||||
],
|
||||
[
|
||||
199,
|
||||
0,
|
||||
283.9
|
||||
],
|
||||
[
|
||||
344.7,
|
||||
222.3,
|
||||
0
|
||||
]
|
||||
],
|
||||
"destinations": [
|
||||
{
|
||||
"location": [
|
||||
13.3888,
|
||||
52.517033
|
||||
],
|
||||
"hint": "PAMAgEVJAoAUAAAAIAAAAAcAAAAAAAAArss0Qa7LNEHiVIRA4lSEQAoAAAAQAAAABAAAAAAAAADMAAAAAEzMAKlYIQM8TMwArVghAwEA3wps52D3",
|
||||
"name": "Friedrichstraße"
|
||||
},
|
||||
{
|
||||
"location": [
|
||||
13.397631,
|
||||
52.529432
|
||||
],
|
||||
"hint": "WIQBgL6mAoAEAAAABgAAAAAAAAA7AAAAhU6PQHvHj0IAAAAAQbyYQgQAAAAGAAAAAAAAADsAAADMAAAAf27MABiJIQOCbswA_4ghAwAAXwVs52D3",
|
||||
"name": "Torstraße"
|
||||
},
|
||||
{
|
||||
"location": [
|
||||
13.428554,
|
||||
52.523239
|
||||
],
|
||||
"hint": "7UcAgP___38fAAAAUQAAACYAAABTAAAAhSQKQrXq5kKRbiZCWJo_Qx8AAABRAAAAJgAAAFMAAADMAAAASufMAOdwIQNL58wA03AhAwMAvxBs52D3",
|
||||
"name": "Platz der Vereinten Nationen"
|
||||
}
|
||||
],
|
||||
"code": "Ok",
|
||||
"distances": [
|
||||
[
|
||||
0,
|
||||
1886.89,
|
||||
3791.3
|
||||
],
|
||||
[
|
||||
1824,
|
||||
0,
|
||||
2838.09
|
||||
],
|
||||
[
|
||||
3275.36,
|
||||
2361.73,
|
||||
0
|
||||
]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Match service
|
||||
|
||||
Map matching matches/snaps given GPS points to the road network in the most plausible way.
|
||||
|
||||
+2
-2
@@ -110,8 +110,8 @@ Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refer
|
||||
|
||||
### table
|
||||
|
||||
Computes duration table for the given locations. Allows for both symmetric and asymmetric
|
||||
tables. Optionally returns distance table.
|
||||
Computes duration tables for the given locations. Allows for both symmetric and asymmetric
|
||||
tables.
|
||||
|
||||
**Parameters**
|
||||
|
||||
|
||||
+5
-5
@@ -68,7 +68,7 @@ If you want to prioritize certain streets, increase the rate on these.
|
||||
|
||||
## Elements
|
||||
### api_version
|
||||
A profile should set `api_version` at the top of your profile. This is done to ensure that older profiles are still supported when the api changes. If `api_version` is not defined, 0 will be assumed. The current api version is 4.
|
||||
A profile should set `api_version` at the top of your profile. This is done to ensure that older profiles are still supported when the api changes. If `api_version` is not defined, 0 will be assumed. The current api version is 2.
|
||||
|
||||
### Library files
|
||||
The folder [profiles/lib/](../profiles/lib/) contains LUA library files for handling many common processing tasks.
|
||||
@@ -138,7 +138,7 @@ Given an OpenStreetMap way, the `process_way` function will either return nothin
|
||||
Argument | Description
|
||||
---------|-------------------------------------------------------
|
||||
profile | The configuration table you returned in `setup`.
|
||||
way | The input way to process (read-only).
|
||||
node | The input way to process (read-only).
|
||||
result | The output that you will modify.
|
||||
relations| Storage of relations to access relations, where `way` is a member.
|
||||
|
||||
@@ -199,7 +199,7 @@ source.lon | Read | Float | Co-ordinates of segment start
|
||||
source.lat | Read | Float | ""
|
||||
target.lon | Read | Float | Co-ordinates of segment end
|
||||
target.lat | Read | Float | ""
|
||||
distance | Read | Float | Length of segment
|
||||
target.distance | Read | Float | Length of segment
|
||||
weight | Read/write | Float | Routing weight for this segment
|
||||
duration | Read/write | Float | Duration for this segment
|
||||
|
||||
@@ -224,8 +224,8 @@ source_highway_turn_classification | Read | Integer |
|
||||
source_access_turn_classification | Read | Integer | Classification based on access tag defined by user during setup. (default when not set: 0, allowed classification values are: 0-15))
|
||||
source_speed | Read | Integer | Speed on this source road in km/h
|
||||
source_priority_class | Read | Enum | The type of road priority class of the source. Defined in `include/extractor/guidance/road_classification.hpp`
|
||||
target_restricted | Read | Boolean | Is the target a restricted access road? (See definition in `process_way`)
|
||||
target_mode | Read | Enum | Travel mode after the turn. Defined in `include/extractor/travel_mode.hpp`
|
||||
target_restricted | Read | Boolean | Is it from a restricted access road? (See definition in `process_way`)
|
||||
target_mode | Read | Enum | Travel mode before the turn. Defined in `include/extractor/travel_mode.hpp`
|
||||
target_is_motorway | Read | Boolean | Is the target road a motorway?
|
||||
target_is_link | Read | Boolean | Is the target road a link?
|
||||
target_number_of_lanes | Read | Integer | How many lanes does the target road have? (default when not tagged: 0)
|
||||
|
||||
+3
-3
@@ -43,9 +43,9 @@ We may introduce forward-compatible changes: query parameters and response prope
|
||||
|
||||
1. Check out the appropriate release branch `x.y`
|
||||
2. Make sure `CHANGELOG.md` is up to date.
|
||||
3. Make sure the `package.json` on branch `x.y` has been committed.
|
||||
4. Make sure all tests are passing (e.g. Travis CI gives you a :green_apple:)
|
||||
5. Use an annotated tag to mark the release: `git tag vx.y.z -a` Body of the tag description should be the changelog entries. Commit should be one in which the `package.json` version matches the version you want to release.
|
||||
3. Make sure the `package.json` is up to date.
|
||||
4. Make sure all tests are passing (e.g. Travis CI gives you a :thumbs_up:)
|
||||
5. Use an annotated tag to mark the release: `git tag vx.y.z -a` Body of the tag description should be the changelog entries.
|
||||
6. Use `npm run docs` to generate the API documentation. Copy `build/docs/*` to `https://github.com/Project-OSRM/project-osrm.github.com` in the `docs/vN.N.N/api` directory
|
||||
7. Push tags and commits: `git push; git push --tags`
|
||||
8. On https://github.com/Project-OSRM/osrm-backend/releases press `Draft a new release`,
|
||||
|
||||
@@ -19,7 +19,7 @@ Feature: Barriers
|
||||
| entrance | x |
|
||||
| wall | |
|
||||
| fence | |
|
||||
| some_tag | x |
|
||||
| some_tag | |
|
||||
| block | x |
|
||||
|
||||
Scenario: Bike - Access tag trumphs barriers
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
@routing @bicycle @mode
|
||||
Feature: Bicycle - Mode flag
|
||||
Background:
|
||||
Given the profile "bicycle"
|
||||
|
||||
Scenario: Bicycle - We tag ferries with a class
|
||||
Given the node map
|
||||
"""
|
||||
a b
|
||||
c d
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | highway | route |
|
||||
| ab | primary | |
|
||||
| bc | | ferry |
|
||||
| cd | primary | |
|
||||
|
||||
When I route I should get
|
||||
| from | to | route | turns | classes |
|
||||
| a | d | ab,bc,cd,cd | depart,notification right,notification left,arrive | [()],[(ferry)],[()],[()] |
|
||||
| d | a | cd,bc,ab,ab | depart,notification right,notification left,arrive | [()],[(ferry)],[()],[()] |
|
||||
| c | a | bc,ab,ab | depart,notification left,arrive | [(ferry)],[()],[()] |
|
||||
| d | b | cd,bc,bc | depart,notification right,arrive | [()],[(ferry)],[()] |
|
||||
| a | c | ab,bc,bc | depart,notification right,arrive | [()],[(ferry)],[()] |
|
||||
| b | d | bc,cd,cd | depart,notification left,arrive | [(ferry)],[()],[()] |
|
||||
|
||||
Scenario: Bicycle - We tag tunnel with a class
|
||||
Background:
|
||||
Given a grid size of 200 meters
|
||||
|
||||
Given the node map
|
||||
"""
|
||||
a b
|
||||
c d
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | tunnel |
|
||||
| ab | no |
|
||||
| bc | yes |
|
||||
| cd | |
|
||||
|
||||
When I route I should get
|
||||
| from | to | route | turns | classes |
|
||||
| a | d | ab,bc,cd,cd | depart,new name right,new name left,arrive | [()],[(tunnel)],[()],[()] |
|
||||
|
||||
Scenario: Bicycle - We tag classes without intersections
|
||||
Background:
|
||||
Given a grid size of 200 meters
|
||||
|
||||
Given the node map
|
||||
"""
|
||||
a b c d
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | name | tunnel |
|
||||
| ab | road | |
|
||||
| bc | road | yes |
|
||||
| cd | road | |
|
||||
|
||||
When I route I should get
|
||||
| from | to | route | turns | classes |
|
||||
| a | d | road,road | depart,arrive | [(),(tunnel),()],[()] |
|
||||
|
||||
Scenario: Bicycle - From roundabout on ferry
|
||||
Given the node map
|
||||
"""
|
||||
c
|
||||
/ \
|
||||
a---b d---f--h
|
||||
\ /
|
||||
e
|
||||
|
|
||||
g
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | oneway | highway | junction | route |
|
||||
| ab | yes | service | | |
|
||||
| cb | yes | service | roundabout | |
|
||||
| dc | yes | service | roundabout | |
|
||||
| be | yes | service | roundabout | |
|
||||
| ed | yes | service | roundabout | |
|
||||
| eg | yes | service | | |
|
||||
| df | | | | ferry |
|
||||
| fh | yes | service | | |
|
||||
|
||||
When I route I should get
|
||||
| from | to | route | turns | classes |
|
||||
| a | h | ab,df,df,fh,fh | depart,roundabout-exit-2,exit roundabout slight right,notification straight,arrive | [()],[(),()],[(ferry)],[()],[()] |
|
||||
@@ -1,55 +0,0 @@
|
||||
@routing @bicycle @exclude
|
||||
Feature: Bicycle - Exclude flags
|
||||
Background:
|
||||
Given the profile file "bicycle" initialized with
|
||||
"""
|
||||
profile.excludable = Sequence { Set { 'ferry' } }
|
||||
"""
|
||||
Given the node map
|
||||
"""
|
||||
a....b~~~~~c...f
|
||||
: :
|
||||
d.....e
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | highway | route | duration | # |
|
||||
| ab | service | | | always drivable |
|
||||
| bc | | ferry | 00:00:01 | not drivable for exclude=ferry, but fast. |
|
||||
| bd | service | | | always drivable |
|
||||
| de | service | | | always drivable |
|
||||
| ec | service | | | always drivable |
|
||||
| cf | service | | | always drivable |
|
||||
|
||||
Scenario: Bicycle - exclude nothing
|
||||
When I route I should get
|
||||
| from | to | route |
|
||||
| a | f | ab,bc,cf,cf |
|
||||
|
||||
When I match I should get
|
||||
| trace | matchings | duration |
|
||||
| abcf | abcf | 109 |
|
||||
|
||||
When I request a travel time matrix I should get
|
||||
| | a | f |
|
||||
| a | 0 | 109 |
|
||||
| f | 109 | 0 |
|
||||
|
||||
Scenario: Bicycle - exclude ferry
|
||||
Given the query options
|
||||
| exclude | ferry |
|
||||
|
||||
When I route I should get
|
||||
| from | to | route |
|
||||
| a | f | ab,bd,de,ec,cf,cf |
|
||||
|
||||
When I match I should get
|
||||
| trace | matchings | duration |
|
||||
| abcf | abcf | 301.2 |
|
||||
|
||||
When I request a travel time matrix I should get
|
||||
| | a | f |
|
||||
| a | 0 | 301 +- 1 |
|
||||
| f | 301.2 +- 1 | 0 |
|
||||
|
||||
|
||||
@@ -48,22 +48,3 @@ Feature: Car - Handle physical limitation
|
||||
| primary | | none | x |
|
||||
| primary | | no-sign | x |
|
||||
| primary | | unsigned | x |
|
||||
|
||||
Scenario: Car - Limited by length
|
||||
Then routability should be
|
||||
| highway | maxlength | bothw |
|
||||
| primary | | x |
|
||||
| primary | 1 | |
|
||||
| primary | 5 | x |
|
||||
| primary | unsigned | x |
|
||||
|
||||
Scenario: Car - Limited by weight
|
||||
Then routability should be
|
||||
| highway | maxweight | bothw |
|
||||
| primary | | x |
|
||||
| primary | 1 | |
|
||||
| primary | 3.5 | x |
|
||||
| primary | 35000 kg | x |
|
||||
| primary | 8.9t | x |
|
||||
| primary | 0.1 lbs | |
|
||||
| primary | unsigned | x |
|
||||
|
||||
@@ -19,7 +19,7 @@ Feature: Barriers
|
||||
| entrance | x |
|
||||
| wall | |
|
||||
| fence | |
|
||||
| some_tag | x |
|
||||
| some_tag | |
|
||||
| block | x |
|
||||
|
||||
Scenario: Foot - Access tag trumphs barriers
|
||||
|
||||
@@ -1209,33 +1209,3 @@ Feature: Simple Turns
|
||||
| a | c | knob,knob | depart,arrive |
|
||||
| d | e | soph,soph | depart,arrive |
|
||||
| d | a | soph,knob,knob | depart,turn left,arrive |
|
||||
|
||||
|
||||
# https://www.openstreetmap.org/node/30797565
|
||||
Scenario: No turn instruction when turning from unnamed onto unnamed
|
||||
Given the node map
|
||||
"""
|
||||
a
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
b----------------c
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
d
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | highway | name | ref |
|
||||
| ab | trunk_link | | |
|
||||
| db | secondary | | L 460 |
|
||||
| bc | secondary | | |
|
||||
|
||||
When I route I should get
|
||||
| from | to | route | turns |
|
||||
| d | c | ,, | depart,turn right,arrive |
|
||||
|
||||
@@ -1,88 +1,74 @@
|
||||
var util = require('util');
|
||||
|
||||
module.exports = function () {
|
||||
const durationsRegex = new RegExp(/^I request a travel time matrix I should get$/);
|
||||
const distancesRegex = new RegExp(/^I request a travel distance matrix I should get$/);
|
||||
this.When(/^I request a travel time matrix I should get$/, (table, callback) => {
|
||||
var NO_ROUTE = 2147483647; // MAX_INT
|
||||
|
||||
const DURATIONS_NO_ROUTE = 2147483647; // MAX_INT
|
||||
const DISTANCES_NO_ROUTE = 3.40282e+38; // MAX_FLOAT
|
||||
var tableRows = table.raw();
|
||||
|
||||
this.When(durationsRegex, function(table, callback) {tableParse.call(this, table, DURATIONS_NO_ROUTE, 'durations', callback);}.bind(this));
|
||||
this.When(distancesRegex, function(table, callback) {tableParse.call(this, table, DISTANCES_NO_ROUTE, 'distances', callback);}.bind(this));
|
||||
};
|
||||
if (tableRows[0][0] !== '') throw new Error('*** Top-left cell of matrix table must be empty');
|
||||
|
||||
const durationsParse = function(v) { return isNaN(parseInt(v)); };
|
||||
const distancesParse = function(v) { return isNaN(parseFloat(v)); };
|
||||
var waypoints = [],
|
||||
columnHeaders = tableRows[0].slice(1),
|
||||
rowHeaders = tableRows.map((h) => h[0]).slice(1),
|
||||
symmetric = columnHeaders.length == rowHeaders.length && columnHeaders.every((ele, i) => ele === rowHeaders[i]);
|
||||
|
||||
function tableParse(table, noRoute, annotation, callback) {
|
||||
|
||||
const parse = annotation == 'distances' ? distancesParse : durationsParse;
|
||||
const params = this.queryParams;
|
||||
params.annotations = annotation == 'distances' ? 'distance' : 'duration';
|
||||
|
||||
var tableRows = table.raw();
|
||||
|
||||
if (tableRows[0][0] !== '') throw new Error('*** Top-left cell of matrix table must be empty');
|
||||
|
||||
var waypoints = [],
|
||||
columnHeaders = tableRows[0].slice(1),
|
||||
rowHeaders = tableRows.map((h) => h[0]).slice(1),
|
||||
symmetric = columnHeaders.length == rowHeaders.length && columnHeaders.every((ele, i) => ele === rowHeaders[i]);
|
||||
|
||||
if (symmetric) {
|
||||
columnHeaders.forEach((nodeName) => {
|
||||
var node = this.findNodeByName(nodeName);
|
||||
if (!node) throw new Error(util.format('*** unknown node "%s"', nodeName));
|
||||
waypoints.push({ coord: node, type: 'loc' });
|
||||
});
|
||||
} else {
|
||||
columnHeaders.forEach((nodeName) => {
|
||||
var node = this.findNodeByName(nodeName);
|
||||
if (!node) throw new Error(util.format('*** unknown node "%s"', nodeName));
|
||||
waypoints.push({ coord: node, type: 'dst' });
|
||||
});
|
||||
rowHeaders.forEach((nodeName) => {
|
||||
var node = this.findNodeByName(nodeName);
|
||||
if (!node) throw new Error(util.format('*** unknown node "%s"', nodeName));
|
||||
waypoints.push({ coord: node, type: 'src' });
|
||||
});
|
||||
}
|
||||
|
||||
var actual = [];
|
||||
actual.push(table.headers);
|
||||
|
||||
this.reprocessAndLoadData((e) => {
|
||||
if (e) return callback(e);
|
||||
// compute matrix
|
||||
|
||||
this.requestTable(waypoints, params, (err, response) => {
|
||||
if (err) return callback(err);
|
||||
if (!response.body.length) return callback(new Error('Invalid response body'));
|
||||
|
||||
var json = JSON.parse(response.body);
|
||||
|
||||
var result = json[annotation].map(row => {
|
||||
var hashes = {};
|
||||
row.forEach((v, i) => { hashes[tableRows[0][i+1]] = parse(v) ? '' : v; });
|
||||
return hashes;
|
||||
if (symmetric) {
|
||||
columnHeaders.forEach((nodeName) => {
|
||||
var node = this.findNodeByName(nodeName);
|
||||
if (!node) throw new Error(util.format('*** unknown node "%s"', nodeName));
|
||||
waypoints.push({ coord: node, type: 'loc' });
|
||||
});
|
||||
} else {
|
||||
columnHeaders.forEach((nodeName) => {
|
||||
var node = this.findNodeByName(nodeName);
|
||||
if (!node) throw new Error(util.format('*** unknown node "%s"', nodeName));
|
||||
waypoints.push({ coord: node, type: 'dst' });
|
||||
});
|
||||
rowHeaders.forEach((nodeName) => {
|
||||
var node = this.findNodeByName(nodeName);
|
||||
if (!node) throw new Error(util.format('*** unknown node "%s"', nodeName));
|
||||
waypoints.push({ coord: node, type: 'src' });
|
||||
});
|
||||
}
|
||||
|
||||
var testRow = (row, ri, cb) => {
|
||||
for (var k in result[ri]) {
|
||||
if (this.FuzzyMatch.match(result[ri][k], row[k])) {
|
||||
result[ri][k] = row[k];
|
||||
} else if (row[k] === '' && result[ri][k] === noRoute) {
|
||||
result[ri][k] = '';
|
||||
} else {
|
||||
result[ri][k] = result[ri][k].toString();
|
||||
var actual = [];
|
||||
actual.push(table.headers);
|
||||
|
||||
this.reprocessAndLoadData((e) => {
|
||||
if (e) return callback(e);
|
||||
// compute matrix
|
||||
var params = this.queryParams;
|
||||
|
||||
this.requestTable(waypoints, params, (err, response) => {
|
||||
if (err) return callback(err);
|
||||
if (!response.body.length) return callback(new Error('Invalid response body'));
|
||||
|
||||
var json = JSON.parse(response.body);
|
||||
|
||||
var result = json['durations'].map(row => {
|
||||
var hashes = {};
|
||||
row.forEach((v, i) => { hashes[tableRows[0][i+1]] = isNaN(parseInt(v)) ? '' : v; });
|
||||
return hashes;
|
||||
});
|
||||
|
||||
var testRow = (row, ri, cb) => {
|
||||
for (var k in result[ri]) {
|
||||
if (this.FuzzyMatch.match(result[ri][k], row[k])) {
|
||||
result[ri][k] = row[k];
|
||||
} else if (row[k] === '' && result[ri][k] === NO_ROUTE) {
|
||||
result[ri][k] = '';
|
||||
} else {
|
||||
result[ri][k] = result[ri][k].toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result[ri][''] = row[''];
|
||||
cb(null, result[ri]);
|
||||
};
|
||||
result[ri][''] = row[''];
|
||||
cb(null, result[ri]);
|
||||
};
|
||||
|
||||
this.processRowsAndDiff(table, testRow, callback);
|
||||
this.processRowsAndDiff(table, testRow, callback);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -21,8 +21,7 @@ module.exports = {
|
||||
matchRe = want.match(/^\/(.*)\/$/),
|
||||
// we use this for matching before/after bearing
|
||||
matchBearingListAbs = want.match(/^((\d+)->(\d+))(,(\d+)->(\d+))*\s+\+\-(.+)$/),
|
||||
matchIntersectionListAbs = want.match(/^(((((true|false):\d+)\s{0,1})+,{0,1})+;{0,1})+\s+\+\-(.+)$/),
|
||||
matchRangeNumbers = want.match(/\d+\+\-\d+/);
|
||||
matchIntersectionListAbs = want.match(/^(((((true|false):\d+)\s{0,1})+,{0,1})+;{0,1})+\s+\+\-(.+)$/);
|
||||
|
||||
function inRange(margin, got, want) {
|
||||
var fromR = parseFloat(want) - margin,
|
||||
@@ -106,11 +105,6 @@ module.exports = {
|
||||
return inRange(margin, got, matchAbs[1]);
|
||||
} else if (matchRe) { // regex: /a,b,.*/
|
||||
return got.match(matchRe[1]);
|
||||
} else if (matchRangeNumbers) {
|
||||
let real_want_and_margin = want.split('+-'),
|
||||
margin = parseFloat(real_want_and_margin[1].trim()),
|
||||
real_want = parseFloat(real_want_and_margin[0].trim());
|
||||
return inRange(margin, got, real_want);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ module.exports = function () {
|
||||
.defer(rimraf, this.scenarioLogFile)
|
||||
.awaitAll(callback);
|
||||
// uncomment to get path to logfile
|
||||
// console.log(' Writing logging output to ' + this.scenarioLogFile);
|
||||
// console.log(" Writing logging output to " + this.scenarioLogFile)
|
||||
});
|
||||
|
||||
this.After((scenario, callback) => {
|
||||
|
||||
@@ -226,22 +226,3 @@ Feature: Distance calculation
|
||||
| x | v | xv,xv | 424m +-1 |
|
||||
| x | w | xw,xw | 360m +-1 |
|
||||
| x | y | xy,xy | 316m +-1 |
|
||||
|
||||
|
||||
# Check rounding errors
|
||||
Scenario: Distances Long distances
|
||||
Given a grid size of 1000 meters
|
||||
Given the node map
|
||||
"""
|
||||
a b c d
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes |
|
||||
| abcd |
|
||||
|
||||
When I route I should get
|
||||
| from | to | distance |
|
||||
| a | b | 1000m +-3 |
|
||||
| a | c | 2000m +-3 |
|
||||
| a | d | 3000m +-3 |
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
@matrix @testbot
|
||||
Feature: Basic Distance Matrix
|
||||
# note that results of travel distance are in metres
|
||||
# note that results are travel time, specified in 1/10th of seconds
|
||||
# since testbot uses a default speed of 100m/10s, the result matches
|
||||
# the number of meters as long as the way type is the default 'primary'
|
||||
|
||||
Background:
|
||||
Given the profile "testbot"
|
||||
And the partition extra arguments "--small-component-size 1 --max-cell-sizes 2,4,8,16"
|
||||
|
||||
Scenario: Testbot - Travel distance matrix of minimal network
|
||||
Scenario: Testbot - Travel time matrix of minimal network
|
||||
Given the node map
|
||||
"""
|
||||
a b
|
||||
@@ -16,99 +18,12 @@ Feature: Basic Distance Matrix
|
||||
| nodes |
|
||||
| ab |
|
||||
|
||||
When I request a travel distance matrix I should get
|
||||
| | a | b |
|
||||
| a | 0 | 100+-1 |
|
||||
| b | 100+-1 | 0 |
|
||||
When I request a travel time matrix I should get
|
||||
| | a | b |
|
||||
| a | 0 | 10 |
|
||||
| b | 10 | 0 |
|
||||
|
||||
Scenario: Testbot - Travel distance matrix of minimal network with toll exclude
|
||||
Given the query options
|
||||
| exclude | toll |
|
||||
|
||||
Given the node map
|
||||
"""
|
||||
a b
|
||||
c d
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | highway | toll | # |
|
||||
| ab | motorway | | not drivable for exclude=motorway |
|
||||
| cd | primary | | always drivable |
|
||||
| ac | primary | yes | not drivable for exclude=toll and exclude=motorway,toll |
|
||||
| bd | motorway | yes | not drivable for exclude=toll and exclude=motorway,toll |
|
||||
|
||||
When I request a travel distance matrix I should get
|
||||
| | a | b | c | d |
|
||||
| a | 0 | 100+-1 | | |
|
||||
| b | 100+-1 | 0 | | |
|
||||
| c | | | 0 | 100+-1 |
|
||||
| d | | | 100+-1 | 0 |
|
||||
|
||||
Scenario: Testbot - Travel distance matrix of minimal network with motorway exclude
|
||||
Given the query options
|
||||
| exclude | motorway |
|
||||
|
||||
Given the node map
|
||||
"""
|
||||
a b
|
||||
c d
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | highway | # |
|
||||
| ab | motorway | not drivable for exclude=motorway |
|
||||
| cd | residential | |
|
||||
| ac | residential | |
|
||||
| bd | residential | |
|
||||
|
||||
When I request a travel distance matrix I should get
|
||||
| | a | b | c | d |
|
||||
| a | 0 | 300+-2 | 100+-2 | 200+-2 |
|
||||
|
||||
Scenario: Testbot - Travel distance matrix of minimal network disconnected motorway exclude
|
||||
Given the query options
|
||||
| exclude | motorway |
|
||||
And the extract extra arguments "--small-component-size 4"
|
||||
|
||||
Given the node map
|
||||
"""
|
||||
ab efgh
|
||||
cd
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | highway | # |
|
||||
| be | motorway | not drivable for exclude=motorway |
|
||||
| abcd | residential | |
|
||||
| efgh | residential | |
|
||||
|
||||
When I request a travel distance matrix I should get
|
||||
| | a | b | e |
|
||||
| a | 0 | 50+-1 | |
|
||||
|
||||
Scenario: Testbot - Travel distance matrix of minimal network with motorway and toll excludes
|
||||
Given the query options
|
||||
| exclude | motorway,toll |
|
||||
|
||||
Given the node map
|
||||
"""
|
||||
a b e f
|
||||
c d g h
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | highway | toll | # |
|
||||
| be | motorway | | not drivable for exclude=motorway |
|
||||
| dg | primary | yes | not drivable for exclude=toll |
|
||||
| abcd | residential | | |
|
||||
| efgh | residential | | |
|
||||
|
||||
When I request a travel distance matrix I should get
|
||||
| | a | b | e | g |
|
||||
| a | 0 | 100+-1 | | |
|
||||
|
||||
Scenario: Testbot - Travel distance matrix with different way speeds
|
||||
Scenario: Testbot - Travel time matrix with different way speeds
|
||||
Given the node map
|
||||
"""
|
||||
a b c d
|
||||
@@ -120,25 +35,40 @@ Feature: Basic Distance Matrix
|
||||
| bc | secondary |
|
||||
| cd | tertiary |
|
||||
|
||||
When I request a travel distance matrix I should get
|
||||
| | a | b | c | d |
|
||||
| a | 0 | 100+-1 | 200+-1 | 300+-1 |
|
||||
| b | 100+-1 | 0 | 100+-1 | 200+-1 |
|
||||
| c | 200+-1 | 100+-1 | 0 | 100+-1 |
|
||||
| d | 300+-1 | 200+-1 | 100+-1 | 0 |
|
||||
When I request a travel time matrix I should get
|
||||
| | a | b | c | d |
|
||||
| a | 0 | 10 | 30 | 60 |
|
||||
| b | 10 | 0 | 20 | 50 |
|
||||
| c | 30 | 20 | 0 | 30 |
|
||||
| d | 60 | 50 | 30 | 0 |
|
||||
|
||||
When I request a travel distance matrix I should get
|
||||
| | a | b | c | d |
|
||||
| a | 0 | 100+-1 | 200+-1 | 300+-1 |
|
||||
When I request a travel time matrix I should get
|
||||
| | a | b | c | d |
|
||||
| a | 0 | 10 | 30 | 60 |
|
||||
|
||||
When I request a travel distance matrix I should get
|
||||
| | a |
|
||||
| a | 0 |
|
||||
| b | 100+-1 |
|
||||
| c | 200+-1 |
|
||||
| d | 300+-1 |
|
||||
When I request a travel time matrix I should get
|
||||
| | a |
|
||||
| a | 0 |
|
||||
| b | 10 |
|
||||
| c | 30 |
|
||||
| d | 60 |
|
||||
|
||||
Scenario: Testbot - Travel distance matrix of small grid
|
||||
Scenario: Testbot - Travel time matrix with fuzzy match
|
||||
Given the node map
|
||||
"""
|
||||
a b
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes |
|
||||
| ab |
|
||||
|
||||
When I request a travel time matrix I should get
|
||||
| | a | b |
|
||||
| a | 0 | 10 |
|
||||
| b | 10 | 0 |
|
||||
|
||||
Scenario: Testbot - Travel time matrix of small grid
|
||||
Given the node map
|
||||
"""
|
||||
a b c
|
||||
@@ -153,14 +83,14 @@ Feature: Basic Distance Matrix
|
||||
| be |
|
||||
| cf |
|
||||
|
||||
When I request a travel distance matrix I should get
|
||||
| | a | b | e | f |
|
||||
| a | 0 | 100+-1 | 200+-1 | 300+-1 |
|
||||
| b | 100+-1 | 0 | 100+-1 | 200+-1 |
|
||||
| e | 200+-1 | 100+-1 | 0 | 100+-1 |
|
||||
| f | 300+-1 | 200+-1 | 100+-1 | 0 |
|
||||
When I request a travel time matrix I should get
|
||||
| | a | b | e | f |
|
||||
| a | 0 | 10 | 20 | 30 |
|
||||
| b | 10 | 0 | 10 | 20 |
|
||||
| e | 20 | 10 | 0 | 10 |
|
||||
| f | 30 | 20 | 10 | 0 |
|
||||
|
||||
Scenario: Testbot - Travel distance matrix of network with unroutable parts
|
||||
Scenario: Testbot - Travel time matrix of network with unroutable parts
|
||||
Given the node map
|
||||
"""
|
||||
a b
|
||||
@@ -170,12 +100,12 @@ Feature: Basic Distance Matrix
|
||||
| nodes | oneway |
|
||||
| ab | yes |
|
||||
|
||||
When I request a travel distance matrix I should get
|
||||
| | a | b |
|
||||
| a | 0 | 100+-1 |
|
||||
| b | | 0 |
|
||||
When I request a travel time matrix I should get
|
||||
| | a | b |
|
||||
| a | 0 | 10 |
|
||||
| b | | 0 |
|
||||
|
||||
Scenario: Testbot - Travel distance matrix of network with oneways
|
||||
Scenario: Testbot - Travel time matrix of network with oneways
|
||||
Given the node map
|
||||
"""
|
||||
x a b y
|
||||
@@ -188,14 +118,14 @@ Feature: Basic Distance Matrix
|
||||
| xa | |
|
||||
| by | |
|
||||
|
||||
When I request a travel distance matrix I should get
|
||||
| | x | y | d | e |
|
||||
| x | 0 | 300+-2 | 400+-2 | 300+-2 |
|
||||
| y | 500+-2 | 0 | 300+-2 | 200+-2 |
|
||||
| d | 200+-2 | 300+-2 | 0 | 300+-2 |
|
||||
| e | 300+-2 | 400+-2 | 100+-2 | 0 |
|
||||
When I request a travel time matrix I should get
|
||||
| | x | y | d | e |
|
||||
| x | 0 | 30 | 40 | 30 |
|
||||
| y | 50 | 0 | 30 | 20 |
|
||||
| d | 20 | 30 | 0 | 30 |
|
||||
| e | 30 | 40 | 10 | 0 |
|
||||
|
||||
Scenario: Testbot - Rectangular travel distance matrix
|
||||
Scenario: Testbot - Rectangular travel time matrix
|
||||
Given the node map
|
||||
"""
|
||||
a b c
|
||||
@@ -210,57 +140,51 @@ Feature: Basic Distance Matrix
|
||||
| be |
|
||||
| cf |
|
||||
|
||||
When I route I should get
|
||||
| from | to | distance |
|
||||
| e | a | 200m +- 1 |
|
||||
| e | b | 100m +- 1 |
|
||||
| f | a | 300m +- 1 |
|
||||
| f | b | 200m +- 1 |
|
||||
When I request a travel time matrix I should get
|
||||
| | a | b | e | f |
|
||||
| a | 0 | 10 | 20 | 30 |
|
||||
|
||||
When I request a travel distance matrix I should get
|
||||
| | a | b | e | f |
|
||||
| a | 0 | 100+-1 | 200+-1 | 300+-1 |
|
||||
When I request a travel time matrix I should get
|
||||
| | a |
|
||||
| a | 0 |
|
||||
| b | 10 |
|
||||
| e | 20 |
|
||||
| f | 30 |
|
||||
|
||||
When I request a travel distance matrix I should get
|
||||
| | a |
|
||||
| a | 0 |
|
||||
| b | 100+-1 |
|
||||
| e | 200+-1 |
|
||||
| f | 300+-1 |
|
||||
When I request a travel time matrix I should get
|
||||
| | a | b | e | f |
|
||||
| a | 0 | 10 | 20 | 30 |
|
||||
| b | 10 | 0 | 10 | 20 |
|
||||
|
||||
When I request a travel distance matrix I should get
|
||||
| | a | b | e | f |
|
||||
| a | 0 | 100+-1 | 200+-1 | 300+-1 |
|
||||
| b | 100+-1 | 0 | 100+-1 | 200+-1 |
|
||||
When I request a travel time matrix I should get
|
||||
| | a | b |
|
||||
| a | 0 | 10 |
|
||||
| b | 10 | 0 |
|
||||
| e | 20 | 10 |
|
||||
| f | 30 | 20 |
|
||||
|
||||
When I request a travel distance matrix I should get
|
||||
| | a | b |
|
||||
| a | 0 | 100+-1 |
|
||||
| b | 100+-1 | 0 |
|
||||
| e | 200+-1 | 100+-1 |
|
||||
| f | 300+-1 | 200+-1 |
|
||||
When I request a travel time matrix I should get
|
||||
| | a | b | e | f |
|
||||
| a | 0 | 10 | 20 | 30 |
|
||||
| b | 10 | 0 | 10 | 20 |
|
||||
| e | 20 | 10 | 0 | 10 |
|
||||
|
||||
When I request a travel distance matrix I should get
|
||||
| | a | b | e | f |
|
||||
| a | 0 | 100+-1 | 200+-1 | 300+-1 |
|
||||
| b | 100+-1 | 0 | 100+-1 | 200+-1 |
|
||||
| e | 200+-1 | 100+-1 | 0 | 100+-1 |
|
||||
When I request a travel time matrix I should get
|
||||
| | a | b | e |
|
||||
| a | 0 | 10 | 20 |
|
||||
| b | 10 | 0 | 10 |
|
||||
| e | 20 | 10 | 0 |
|
||||
| f | 30 | 20 | 10 |
|
||||
|
||||
When I request a travel distance matrix I should get
|
||||
| | a | b | e |
|
||||
| a | 0 | 100+-1 | 200+-1 |
|
||||
| b | 100+-1 | 0 | 100+-1 |
|
||||
| e | 200+-1 | 100+-1 | 0 |
|
||||
| f | 300+-1 | 200+-1 | 100+-1 |
|
||||
When I request a travel time matrix I should get
|
||||
| | a | b | e | f |
|
||||
| a | 0 | 10 | 20 | 30 |
|
||||
| b | 10 | 0 | 10 | 20 |
|
||||
| e | 20 | 10 | 0 | 10 |
|
||||
| f | 30 | 20 | 10 | 0 |
|
||||
|
||||
When I request a travel distance matrix I should get
|
||||
| | a | b | e | f |
|
||||
| a | 0 | 100+-1 | 200+-1 | 300+-1 |
|
||||
| b | 100+-1 | 0 | 100+-1 | 200+-1 |
|
||||
| e | 200+-1 | 100+-1 | 0 | 100+-1 |
|
||||
| f | 300+-1 | 200+-1 | 100+-1 | 0 |
|
||||
|
||||
Scenario: Testbot - Travel distance 3x2 matrix
|
||||
Scenario: Testbot - Travel time 3x2 matrix
|
||||
Given the node map
|
||||
"""
|
||||
a b c
|
||||
@@ -275,11 +199,10 @@ Feature: Basic Distance Matrix
|
||||
| be |
|
||||
| cf |
|
||||
|
||||
|
||||
When I request a travel distance matrix I should get
|
||||
| | b | e | f |
|
||||
| a | 100+-1 | 200+-1 | 300+-1 |
|
||||
| b | 0 | 100+-1 | 200+-1 |
|
||||
When I request a travel time matrix I should get
|
||||
| | b | e | f |
|
||||
| a | 10 | 20 | 30 |
|
||||
| b | 0 | 10 | 20 |
|
||||
|
||||
Scenario: Testbot - All coordinates are from same small component
|
||||
Given a grid size of 300 meters
|
||||
@@ -298,10 +221,10 @@ Feature: Basic Distance Matrix
|
||||
| da |
|
||||
| fg |
|
||||
|
||||
When I request a travel distance matrix I should get
|
||||
| | f | g |
|
||||
| f | 0 | 300+-2 |
|
||||
| g | 300+-2 | 0 |
|
||||
When I request a travel time matrix I should get
|
||||
| | f | g |
|
||||
| f | 0 | 30 |
|
||||
| g | 30 | 0 |
|
||||
|
||||
Scenario: Testbot - Coordinates are from different small component and snap to big CC
|
||||
Given a grid size of 300 meters
|
||||
@@ -321,25 +244,14 @@ Feature: Basic Distance Matrix
|
||||
| fg |
|
||||
| hi |
|
||||
|
||||
When I route I should get
|
||||
| from | to | distance |
|
||||
| f | g | 300m |
|
||||
| f | i | 300m |
|
||||
| g | f | 300m |
|
||||
| g | h | 300m |
|
||||
| h | g | 300m |
|
||||
| h | i | 300m |
|
||||
| i | f | 300m |
|
||||
| i | h | 300m |
|
||||
When I request a travel time matrix I should get
|
||||
| | f | g | h | i |
|
||||
| f | 0 | 30 | 0 | 30 |
|
||||
| g | 30 | 0 | 30 | 0 |
|
||||
| h | 0 | 30 | 0 | 30 |
|
||||
| i | 30 | 0 | 30 | 0 |
|
||||
|
||||
When I request a travel distance matrix I should get
|
||||
| | f | g | h | i |
|
||||
| f | 0 | 300+-2 | 0 | 300+-2 |
|
||||
| g | 300+-2 | 0 | 300+-2 | 0 |
|
||||
| h | 0 | 300+-2 | 0 | 300+-2 |
|
||||
| i | 300+-2 | 0 | 300+-2 | 0 |
|
||||
|
||||
Scenario: Testbot - Travel distance matrix with loops
|
||||
Scenario: Testbot - Travel time matrix with loops
|
||||
Given the node map
|
||||
"""
|
||||
a 1 2 b
|
||||
@@ -353,15 +265,14 @@ Feature: Basic Distance Matrix
|
||||
| cd | yes |
|
||||
| da | yes |
|
||||
|
||||
When I request a travel distance matrix I should get
|
||||
| | 1 | 2 | 3 | 4 |
|
||||
| 1 | 0 | 100+-1 | 400+-1 | 500+-1 |
|
||||
| 2 | 700+-1 | 0 | 300+-1 | 400+-1 |
|
||||
| 3 | 400+-1 | 500+-1 | 0 | 100+-1 |
|
||||
| 4 | 300+-1 | 400+-1 | 700+-1 | 0 |
|
||||
When I request a travel time matrix I should get
|
||||
| | 1 | 2 | 3 | 4 |
|
||||
| 1 | 0 | 10 +-1 | 40 +-1 | 50 +-1 |
|
||||
| 2 | 70 +-1 | 0 | 30 +-1 | 40 +-1 |
|
||||
| 3 | 40 +-1 | 50 +-1 | 0 | 10 +-1 |
|
||||
| 4 | 30 +-1 | 40 +-1 | 70 +-1 | 0 |
|
||||
|
||||
|
||||
Scenario: Testbot - Travel distance matrix based on segment durations
|
||||
Scenario: Testbot - Travel time matrix based on segment durations
|
||||
Given the profile file
|
||||
"""
|
||||
local functions = require('testbot')
|
||||
@@ -390,19 +301,20 @@ Feature: Basic Distance Matrix
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes |
|
||||
| abcd |
|
||||
| ce |
|
||||
| nodes |
|
||||
| abcd |
|
||||
| ce |
|
||||
|
||||
When I request a travel distance matrix I should get
|
||||
| | a | b | c | d | e |
|
||||
| a | 0 | 100+-2 | 200+-2 | 300+-2 | 400+-2 |
|
||||
| b | 100+-2 | 0 | 100+-2 | 200+-2 | 300+-2 |
|
||||
| c | 200+-2 | 100+-2 | 0 | 100+-2 | 200+-2 |
|
||||
| d | 300+-2 | 200+-2 | 100+-2 | 0 | 300+-2 |
|
||||
| e | 400+-2 | 300+-2 | 200+-2 | 300+-2 | 0 |
|
||||
When I request a travel time matrix I should get
|
||||
| | a | b | c | d | e |
|
||||
| a | 0 | 11 | 22 | 33 | 33 |
|
||||
| b | 11 | 0 | 11 | 22 | 22 |
|
||||
| c | 22 | 11 | 0 | 11 | 11 |
|
||||
| d | 33 | 22 | 11 | 0 | 22 |
|
||||
| e | 33 | 22 | 11 | 22 | 0 |
|
||||
|
||||
Scenario: Testbot - Travel distance matrix for alternative loop paths
|
||||
|
||||
Scenario: Testbot - Travel time matrix for alternative loop paths
|
||||
Given the profile file
|
||||
"""
|
||||
local functions = require('testbot')
|
||||
@@ -438,132 +350,62 @@ Feature: Basic Distance Matrix
|
||||
| dc | yes |
|
||||
| ca | yes |
|
||||
|
||||
When I request a travel distance matrix I should get
|
||||
| | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
|
||||
| 1 | 0 | 1100+-5 | 300+-5 | 200+-5 | 600+-5 | 500+-5 | 900+-5 | 800+-5 |
|
||||
| 2 | 100+-5 | 0 | 400+-5 | 300+-5 | 700+-5 | 600+-5 | 1000+-5 | 900+-5 |
|
||||
| 3 | 900+-5 | 800+-5 | 0 | 1100+-5 | 300+-5 | 200+-5 | 600+-5 | 500+-5 |
|
||||
| 4 | 1000+-5 | 900+-5 | 100+-5 | 0 | 400+-5 | 300+-5 | 700+-5 | 600+-5 |
|
||||
| 5 | 600+-5 | 500+-5 | 900+-5 | 800+-5 | 0 | 1100+-5 | 300+-5 | 200+-5 |
|
||||
| 6 | 700+-5 | 600+-5 | 1000+-5 | 900+-5 | 100+-5 | 0 | 400+-5 | 300+-5 |
|
||||
| 7 | 300+-5 | 200+-5 | 600+-5 | 500+-5 | 900+-5 | 800+-5 | 0 | 1100+-5 |
|
||||
| 8 | 400+-5 | 300+-5 | 700+-5 | 600+-5 | 1000+-5 | 900+-5 | 100+-5 | 0 |
|
||||
When I request a travel time matrix I should get
|
||||
| | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
|
||||
| 1 | 0 | 11 | 3 | 2 | 6 | 5 | 8.9 | 7.9 |
|
||||
| 2 | 1 | 0 | 4 | 3 | 7 | 6 | 9.9 | 8.9 |
|
||||
| 3 | 9 | 8 | 0 | 11 | 3 | 2 | 5.9 | 4.9 |
|
||||
| 4 | 10 | 9 | 1 | 0 | 4 | 3 | 6.9 | 5.9 |
|
||||
| 5 | 6 | 5 | 9 | 8 | 0 | 11 | 2.9 | 1.9 |
|
||||
| 6 | 7 | 6 | 10 | 9 | 1 | 0 | 3.9 | 2.9 |
|
||||
| 7 | 3.1 | 2.1 | 6.1 | 5.1 | 9.1 | 8.1 | 0 | 11 |
|
||||
| 8 | 4.1 | 3.1 | 7.1 | 6.1 | 10.1 | 9.1 | 1 | 0 |
|
||||
|
||||
When I request a travel distance matrix I should get
|
||||
| | 1 |
|
||||
| 1 | 0 |
|
||||
| 2 | 100+-5 |
|
||||
| 3 | 900+-5 |
|
||||
| 4 | 1000+-5 |
|
||||
| 5 | 600+-5 |
|
||||
| 6 | 700+-5 |
|
||||
| 7 | 300+-5 |
|
||||
| 8 | 400+-5 |
|
||||
|
||||
Scenario: Testbot - Travel distance matrix with ties
|
||||
Given the node map
|
||||
Scenario: Testbot - Travel time matrix with ties
|
||||
Given the profile file
|
||||
"""
|
||||
local functions = require('testbot')
|
||||
functions.process_segment = function(profile, segment)
|
||||
segment.weight = 1
|
||||
segment.duration = 1
|
||||
end
|
||||
functions.process_turn = function(profile, turn)
|
||||
if turn.angle >= 0 then
|
||||
turn.duration = 16
|
||||
else
|
||||
turn.duration = 4
|
||||
end
|
||||
turn.weight = 0
|
||||
end
|
||||
return functions
|
||||
"""
|
||||
And the node map
|
||||
"""
|
||||
a b
|
||||
a b
|
||||
|
||||
c d
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes |
|
||||
| ab |
|
||||
| ac |
|
||||
| bd |
|
||||
| dc |
|
||||
| nodes |
|
||||
| ab |
|
||||
| ac |
|
||||
| bd |
|
||||
| dc |
|
||||
|
||||
|
||||
When I route I should get
|
||||
| from | to | route | distance | time | weight |
|
||||
| a | c | ac,ac | 200m | 20s | 20 |
|
||||
| from | to | route | distance | time | weight |
|
||||
| a | c | ac,ac | 200m | 5s | 5 |
|
||||
|
||||
When I route I should get
|
||||
| from | to | route | distance |
|
||||
| a | b | ab,ab | 450m |
|
||||
| a | c | ac,ac | 200m |
|
||||
| a | d | ac,dc,dc | 500m +- 1 |
|
||||
When I request a travel time matrix I should get
|
||||
| | a | b | c | d |
|
||||
| a | 0 | 1 | 5 | 10 |
|
||||
|
||||
When I request a travel distance matrix I should get
|
||||
| | a | b | c | d |
|
||||
| a | 0 | 450+-2 | 200+-2 | 500+-2 |
|
||||
|
||||
When I request a travel distance matrix I should get
|
||||
| | a |
|
||||
| a | 0 |
|
||||
| b | 450+-2 |
|
||||
| c | 200+-2 |
|
||||
| d | 500+-2 |
|
||||
|
||||
When I request a travel distance matrix I should get
|
||||
| | a | c |
|
||||
| a | 0 | 200+-2 |
|
||||
| c | 200+-2 | 0 |
|
||||
|
||||
|
||||
# Check rounding errors
|
||||
Scenario: Testbot - Long distances in tables
|
||||
Given a grid size of 1000 meters
|
||||
Given the node map
|
||||
"""
|
||||
a b c d
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes |
|
||||
| abcd |
|
||||
|
||||
When I request a travel distance matrix I should get
|
||||
| | a | b | c | d |
|
||||
| a | 0 | 1000+-3 | 2000+-3 | 3000+-3 |
|
||||
|
||||
|
||||
Scenario: Testbot - OneToMany vs ManyToOne
|
||||
Given the node map
|
||||
"""
|
||||
a b
|
||||
c
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | oneway |
|
||||
| ab | yes |
|
||||
| ac | |
|
||||
| bc | |
|
||||
|
||||
When I request a travel distance matrix I should get
|
||||
| | a | b |
|
||||
| b | 240.4 | 0 |
|
||||
|
||||
When I request a travel distance matrix I should get
|
||||
| | a |
|
||||
| a | 0 |
|
||||
| b | 240.4 |
|
||||
|
||||
Scenario: Testbot - Varying distances between nodes
|
||||
Given the node map
|
||||
"""
|
||||
a b c d
|
||||
|
||||
e
|
||||
|
||||
|
||||
|
||||
f
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | oneway |
|
||||
| feabcd | yes |
|
||||
| ec | |
|
||||
| fd | |
|
||||
|
||||
When I request a travel distance matrix I should get
|
||||
| | a | b | c | d | e | f |
|
||||
| a | 0 | 100+-1 | 300+-1 | 650+-1 | 1930+-1 | 1533+-1 |
|
||||
| b | 760+-1 | 0 | 200+-1 | 550+-1 | 1830+-1 | 1433+-1 |
|
||||
| c | 560+-2 | 660+-2 | 0 | 350+-1 | 1630+-1 | 1233+-1 |
|
||||
| d | 1480+-2 | 1580+-1 | 1780+-1 | 0 | 1280+-1 | 883+-1 |
|
||||
| e | 200+-2 | 300+-2 | 500+-1 | 710+-1 | 0 | 1593+-1 |
|
||||
| f | 597+-1 | 696+-1 | 896+-1 | 1108+-1 | 400+-3 | 0 |
|
||||
When I request a travel time matrix I should get
|
||||
| | a |
|
||||
| a | 0 |
|
||||
| b | 1 |
|
||||
| c | 15 |
|
||||
| d | 10 |
|
||||
|
||||
@@ -1,512 +0,0 @@
|
||||
@matrix @testbot
|
||||
Feature: Basic Duration Matrix
|
||||
# note that results of travel time are in seconds
|
||||
|
||||
Background:
|
||||
Given the profile "testbot"
|
||||
And the partition extra arguments "--small-component-size 1 --max-cell-sizes 2,4,8,16"
|
||||
|
||||
Scenario: Testbot - Travel time matrix of minimal network
|
||||
Given the node map
|
||||
"""
|
||||
a b
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes |
|
||||
| ab |
|
||||
|
||||
When I request a travel time matrix I should get
|
||||
| | a | b |
|
||||
| a | 0 | 10 |
|
||||
| b | 10 | 0 |
|
||||
|
||||
@ch
|
||||
Scenario: Testbot - Travel time matrix of minimal network with toll exclude
|
||||
Given the query options
|
||||
| exclude | toll |
|
||||
|
||||
Given the node map
|
||||
"""
|
||||
a b
|
||||
c d
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | highway | toll | # |
|
||||
| ab | motorway | | not drivable for exclude=motorway |
|
||||
| cd | primary | | always drivable |
|
||||
| ac | motorway | yes | not drivable for exclude=toll and exclude=motorway,toll |
|
||||
| bd | motorway | yes | not drivable for exclude=toll and exclude=motorway,toll |
|
||||
|
||||
When I request a travel time matrix I should get
|
||||
| | a | b | c | d |
|
||||
| a | 0 | 15 | | |
|
||||
| b | 15 | 0 | | |
|
||||
| c | | | 0 | 10 |
|
||||
| d | | | 10 | 0 |
|
||||
|
||||
@ch
|
||||
Scenario: Testbot - Travel time matrix of minimal network with motorway exclude
|
||||
Given the query options
|
||||
| exclude | motorway |
|
||||
|
||||
Given the node map
|
||||
"""
|
||||
a b
|
||||
c d
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | highway | # |
|
||||
| ab | motorway | not drivable for exclude=motorway |
|
||||
| cd | residential | |
|
||||
| ac | residential | |
|
||||
| bd | residential | |
|
||||
|
||||
When I request a travel time matrix I should get
|
||||
| | a | b | c | d |
|
||||
| a | 0 | 45 | 15 | 30 |
|
||||
|
||||
@ch
|
||||
Scenario: Testbot - Travel time matrix of minimal network disconnected motorway exclude
|
||||
Given the query options
|
||||
| exclude | motorway |
|
||||
|
||||
Given the node map
|
||||
"""
|
||||
ab efgh
|
||||
cd
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | highway | # |
|
||||
| be | motorway | not drivable for exclude=motorway |
|
||||
| abcd | residential | |
|
||||
| efgh | residential | |
|
||||
|
||||
When I request a travel time matrix I should get
|
||||
| | a | b | e |
|
||||
| a | 0 | 7.5 | |
|
||||
|
||||
@ch
|
||||
Scenario: Testbot - Travel time matrix of minimal network with motorway and toll excludes
|
||||
Given the query options
|
||||
| exclude | motorway,toll |
|
||||
|
||||
Given the node map
|
||||
"""
|
||||
a b e f
|
||||
c d g h
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | highway | toll | # |
|
||||
| be | motorway | | not drivable for exclude=motorway |
|
||||
| dg | primary | yes | not drivable for exclude=toll |
|
||||
| abcd | residential | | |
|
||||
| efgh | residential | | |
|
||||
|
||||
When I request a travel time matrix I should get
|
||||
| | a | b | e | g |
|
||||
| a | 0 | 15 | | |
|
||||
|
||||
Scenario: Testbot - Travel time matrix with different way speeds
|
||||
Given the node map
|
||||
"""
|
||||
a b c d
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | highway |
|
||||
| ab | primary |
|
||||
| bc | secondary |
|
||||
| cd | tertiary |
|
||||
|
||||
When I request a travel time matrix I should get
|
||||
| | a | b | c | d |
|
||||
| a | 0 | 10 | 30 | 60 |
|
||||
| b | 10 | 0 | 20 | 50 |
|
||||
| c | 30 | 20 | 0 | 30 |
|
||||
| d | 60 | 50 | 30 | 0 |
|
||||
|
||||
When I request a travel time matrix I should get
|
||||
| | a | b | c | d |
|
||||
| a | 0 | 10 | 30 | 60 |
|
||||
|
||||
When I request a travel time matrix I should get
|
||||
| | a |
|
||||
| a | 0 |
|
||||
| b | 10 |
|
||||
| c | 30 |
|
||||
| d | 60 |
|
||||
|
||||
|
||||
Scenario: Testbot - Travel time matrix of small grid
|
||||
Given the node map
|
||||
"""
|
||||
a b c
|
||||
d e f
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes |
|
||||
| abc |
|
||||
| def |
|
||||
| ad |
|
||||
| be |
|
||||
| cf |
|
||||
|
||||
When I request a travel time matrix I should get
|
||||
| | a | b | e | f |
|
||||
| a | 0 | 10 | 20 | 30 |
|
||||
| b | 10 | 0 | 10 | 20 |
|
||||
| e | 20 | 10 | 0 | 10 |
|
||||
| f | 30 | 20 | 10 | 0 |
|
||||
|
||||
|
||||
Scenario: Testbot - Travel time matrix of network with unroutable parts
|
||||
Given the node map
|
||||
"""
|
||||
a b
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | oneway |
|
||||
| ab | yes |
|
||||
|
||||
When I request a travel time matrix I should get
|
||||
| | a | b |
|
||||
| a | 0 | 10 |
|
||||
| b | | 0 |
|
||||
|
||||
|
||||
Scenario: Testbot - Travel time matrix of network with oneways
|
||||
Given the node map
|
||||
"""
|
||||
x a b y
|
||||
d e
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | oneway |
|
||||
| abeda | yes |
|
||||
| xa | |
|
||||
| by | |
|
||||
|
||||
When I request a travel time matrix I should get
|
||||
| | x | y | d | e |
|
||||
| x | 0 | 30 | 40 | 30 |
|
||||
| y | 50 | 0 | 30 | 20 |
|
||||
| d | 20 | 30 | 0 | 30 |
|
||||
| e | 30 | 40 | 10 | 0 |
|
||||
|
||||
|
||||
Scenario: Testbot - Rectangular travel time matrix
|
||||
Given the node map
|
||||
"""
|
||||
a b c
|
||||
d e f
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes |
|
||||
| abc |
|
||||
| def |
|
||||
| ad |
|
||||
| be |
|
||||
| cf |
|
||||
|
||||
When I request a travel time matrix I should get
|
||||
| | a | b | e | f |
|
||||
| a | 0 | 10 | 20 | 30 |
|
||||
|
||||
When I request a travel time matrix I should get
|
||||
| | a |
|
||||
| a | 0 |
|
||||
| b | 10 |
|
||||
| e | 20 |
|
||||
| f | 30 |
|
||||
|
||||
When I request a travel time matrix I should get
|
||||
| | a | b | e | f |
|
||||
| a | 0 | 10 | 20 | 30 |
|
||||
| b | 10 | 0 | 10 | 20 |
|
||||
|
||||
When I request a travel time matrix I should get
|
||||
| | a | b |
|
||||
| a | 0 | 10 |
|
||||
| b | 10 | 0 |
|
||||
| e | 20 | 10 |
|
||||
| f | 30 | 20 |
|
||||
|
||||
When I request a travel time matrix I should get
|
||||
| | a | b | e | f |
|
||||
| a | 0 | 10 | 20 | 30 |
|
||||
| b | 10 | 0 | 10 | 20 |
|
||||
| e | 20 | 10 | 0 | 10 |
|
||||
|
||||
When I request a travel time matrix I should get
|
||||
| | a | b | e |
|
||||
| a | 0 | 10 | 20 |
|
||||
| b | 10 | 0 | 10 |
|
||||
| e | 20 | 10 | 0 |
|
||||
| f | 30 | 20 | 10 |
|
||||
|
||||
When I request a travel time matrix I should get
|
||||
| | a | b | e | f |
|
||||
| a | 0 | 10 | 20 | 30 |
|
||||
| b | 10 | 0 | 10 | 20 |
|
||||
| e | 20 | 10 | 0 | 10 |
|
||||
| f | 30 | 20 | 10 | 0 |
|
||||
|
||||
Scenario: Testbot - Travel time 3x2 matrix
|
||||
Given the node map
|
||||
"""
|
||||
a b c
|
||||
d e f
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes |
|
||||
| abc |
|
||||
| def |
|
||||
| ad |
|
||||
| be |
|
||||
| cf |
|
||||
|
||||
When I request a travel time matrix I should get
|
||||
| | b | e | f |
|
||||
| a | 10 | 20 | 30 |
|
||||
| b | 0 | 10 | 20 |
|
||||
|
||||
|
||||
Scenario: Testbot - All coordinates are from same small component
|
||||
Given a grid size of 300 meters
|
||||
Given the extract extra arguments "--small-component-size 4"
|
||||
Given the node map
|
||||
"""
|
||||
a b f
|
||||
d e g
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes |
|
||||
| ab |
|
||||
| be |
|
||||
| ed |
|
||||
| da |
|
||||
| fg |
|
||||
|
||||
When I request a travel time matrix I should get
|
||||
| | f | g |
|
||||
| f | 0 | 30 |
|
||||
| g | 30 | 0 |
|
||||
|
||||
|
||||
Scenario: Testbot - Coordinates are from different small component and snap to big CC
|
||||
Given a grid size of 300 meters
|
||||
Given the extract extra arguments "--small-component-size 4"
|
||||
Given the node map
|
||||
"""
|
||||
a b f h
|
||||
d e g i
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes |
|
||||
| ab |
|
||||
| be |
|
||||
| ed |
|
||||
| da |
|
||||
| fg |
|
||||
| hi |
|
||||
|
||||
When I request a travel time matrix I should get
|
||||
| | f | g | h | i |
|
||||
| f | 0 | 30 | 0 | 30 |
|
||||
| g | 30 | 0 | 30 | 0 |
|
||||
| h | 0 | 30 | 0 | 30 |
|
||||
| i | 30 | 0 | 30 | 0 |
|
||||
|
||||
|
||||
Scenario: Testbot - Travel time matrix with loops
|
||||
Given the node map
|
||||
"""
|
||||
a 1 2 b
|
||||
d 4 3 c
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | oneway |
|
||||
| ab | yes |
|
||||
| bc | yes |
|
||||
| cd | yes |
|
||||
| da | yes |
|
||||
|
||||
When I request a travel time matrix I should get
|
||||
| | 1 | 2 | 3 | 4 |
|
||||
| 1 | 0 | 10 +-1 | 40 +-1 | 50 +-1 |
|
||||
| 2 | 70 +-1 | 0 | 30 +-1 | 40 +-1 |
|
||||
| 3 | 40 +-1 | 50 +-1 | 0 | 10 +-1 |
|
||||
| 4 | 30 +-1 | 40 +-1 | 70 +-1 | 0 |
|
||||
|
||||
|
||||
Scenario: Testbot - Travel time matrix based on segment durations
|
||||
Given the profile file
|
||||
"""
|
||||
local functions = require('testbot')
|
||||
functions.setup_testbot = functions.setup
|
||||
|
||||
functions.setup = function()
|
||||
local profile = functions.setup_testbot()
|
||||
profile.traffic_signal_penalty = 0
|
||||
profile.u_turn_penalty = 0
|
||||
return profile
|
||||
end
|
||||
|
||||
functions.process_segment = function(profile, segment)
|
||||
segment.weight = 2
|
||||
segment.duration = 11
|
||||
end
|
||||
|
||||
return functions
|
||||
"""
|
||||
|
||||
And the node map
|
||||
"""
|
||||
a-b-c-d
|
||||
.
|
||||
e
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes |
|
||||
| abcd |
|
||||
| ce |
|
||||
|
||||
When I request a travel time matrix I should get
|
||||
| | a | b | c | d | e |
|
||||
| a | 0 | 11 | 22 | 33 | 33 |
|
||||
| b | 11 | 0 | 11 | 22 | 22 |
|
||||
| c | 22 | 11 | 0 | 11 | 11 |
|
||||
| d | 33 | 22 | 11 | 0 | 22 |
|
||||
| e | 33 | 22 | 11 | 22 | 0 |
|
||||
|
||||
|
||||
Scenario: Testbot - Travel time matrix for alternative loop paths
|
||||
Given the profile file
|
||||
"""
|
||||
local functions = require('testbot')
|
||||
functions.setup_testbot = functions.setup
|
||||
|
||||
functions.setup = function()
|
||||
local profile = functions.setup_testbot()
|
||||
profile.traffic_signal_penalty = 0
|
||||
profile.u_turn_penalty = 0
|
||||
profile.weight_precision = 3
|
||||
return profile
|
||||
end
|
||||
|
||||
functions.process_segment = function(profile, segment)
|
||||
segment.weight = 777
|
||||
segment.duration = 3
|
||||
end
|
||||
|
||||
return functions
|
||||
"""
|
||||
And the node map
|
||||
"""
|
||||
a 2 1 b
|
||||
7 4
|
||||
8 3
|
||||
c 5 6 d
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | oneway |
|
||||
| ab | yes |
|
||||
| bd | yes |
|
||||
| dc | yes |
|
||||
| ca | yes |
|
||||
|
||||
When I request a travel time matrix I should get
|
||||
| | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
|
||||
| 1 | 0 | 11 | 3 | 2 | 6 | 5 | 8.9 | 7.9 |
|
||||
| 2 | 1 | 0 | 4 | 3 | 7 | 6 | 9.9 | 8.9 |
|
||||
| 3 | 9 | 8 | 0 | 11 | 3 | 2 | 5.9 | 4.9 |
|
||||
| 4 | 10 | 9 | 1 | 0 | 4 | 3 | 6.9 | 5.9 |
|
||||
| 5 | 6 | 5 | 9 | 8 | 0 | 11 | 2.9 | 1.9 |
|
||||
| 6 | 7 | 6 | 10 | 9 | 1 | 0 | 3.9 | 2.9 |
|
||||
| 7 | 3.1 | 2.1 | 6.1 | 5.1 | 9.1 | 8.1 | 0 | 11 |
|
||||
| 8 | 4.1 | 3.1 | 7.1 | 6.1 | 10.1 | 9.1 | 1 | 0 |
|
||||
|
||||
|
||||
Scenario: Testbot - Travel time matrix with ties
|
||||
Given the profile file
|
||||
"""
|
||||
local functions = require('testbot')
|
||||
functions.process_segment = function(profile, segment)
|
||||
segment.weight = 1
|
||||
segment.duration = 1
|
||||
end
|
||||
functions.process_turn = function(profile, turn)
|
||||
if turn.angle >= 0 then
|
||||
turn.duration = 16
|
||||
else
|
||||
turn.duration = 4
|
||||
end
|
||||
turn.weight = 0
|
||||
end
|
||||
return functions
|
||||
"""
|
||||
And the node map
|
||||
"""
|
||||
a b
|
||||
|
||||
c d
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes |
|
||||
| ab |
|
||||
| ac |
|
||||
| bd |
|
||||
| dc |
|
||||
|
||||
When I route I should get
|
||||
| from | to | route | distance | time | weight |
|
||||
| a | c | ac,ac | 200m | 5s | 5 |
|
||||
|
||||
When I request a travel time matrix I should get
|
||||
| | a | b | c | d |
|
||||
| a | 0 | 1 | 5 | 10 |
|
||||
|
||||
When I request a travel time matrix I should get
|
||||
| | a |
|
||||
| a | 0 |
|
||||
| b | 1 |
|
||||
| c | 15 |
|
||||
| d | 10 |
|
||||
|
||||
Scenario: Testbot - OneToMany vs ManyToOne
|
||||
Given the node map
|
||||
"""
|
||||
a b
|
||||
c
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | oneway |
|
||||
| ab | yes |
|
||||
| ac | |
|
||||
| bc | |
|
||||
|
||||
When I request a travel time matrix I should get
|
||||
| | a | b |
|
||||
| b | 24.1 | 0 |
|
||||
|
||||
When I request a travel time matrix I should get
|
||||
| | a |
|
||||
| a | 0 |
|
||||
| b | 24.1 |
|
||||
@@ -792,4 +792,4 @@ Feature: Basic Map Matching
|
||||
When I match I should get
|
||||
| trace | geometry | a:distance | a:duration | a:weight | duration |
|
||||
| 2345 | 1.00018,1,1.000315,1 | 15.013264 | 1.5 | 1.5 | 1.5 |
|
||||
| 4321 | 1.00027,1,1.000135,1 | 15.013264 | 1.5 | 1.5 | 1.5 |
|
||||
| 4321 | 1.00027,1,1.000135,1 | 15.013264 | 1.5 | 1.5 | 1.5 |
|
||||
@@ -106,40 +106,6 @@ Feature: Multi level routing
|
||||
| l | 144.7 | 60 |
|
||||
| o | 124.7 | 0 |
|
||||
|
||||
|
||||
When I request a travel distance matrix I should get
|
||||
| | a | f | l | o |
|
||||
| a | 0+-2 | 2287+-2 | 1443+-2 | 1243+-2 |
|
||||
| f | 2284+-2 | 0+-2 | 1241+-2 | 1443+-2 |
|
||||
| l | 1443+-2 | 1244+-2 | 0+-2 | 600+-2 |
|
||||
| o | 1243+-2 | 1444+-2 | 600+-2 | 0+-2 |
|
||||
|
||||
When I request a travel distance matrix I should get
|
||||
| | a | f | l | o |
|
||||
| a | 0 | 2287.2+-2 | 1443+-2 | 1243+-2 |
|
||||
|
||||
When I request a travel distance matrix I should get
|
||||
| | a |
|
||||
| a | 0 |
|
||||
| f | 2284.5+-2 |
|
||||
| l | 1443.1 |
|
||||
| o | 1243 |
|
||||
|
||||
When I request a travel distance matrix I should get
|
||||
| | a | f | l | o |
|
||||
| a | 0 | 2287+-2 | 1443+-2 | 1243+-2 |
|
||||
| o | 1243 | 1444+-2 | 600+-2 | 0+-2 |
|
||||
|
||||
|
||||
When I request a travel distance matrix I should get
|
||||
| | a | o |
|
||||
| a | 0+-2 | 1243+-2 |
|
||||
| f | 2284+-2 | 1443+-2 |
|
||||
| l | 1443+-2 | 600+-2 |
|
||||
| o | 1243+-2 | 0+-2 |
|
||||
|
||||
|
||||
|
||||
Scenario: Testbot - Multi level routing: horizontal road
|
||||
Given the node map
|
||||
"""
|
||||
|
||||
@@ -54,7 +54,7 @@ Feature: Traffic - speeds
|
||||
| a | d | ad,ad | 27 km/h | 1275.7,0 | 1 |
|
||||
| d | c | dc,dc | 36 km/h | 956.8,0 | 0 |
|
||||
| g | b | fb,fb | 36 km/h | 164.7,0 | 0 |
|
||||
| a | g | ad,df,fb,fb | 30 km/h | 1295.7,487.5,304.7,0 | 1:0:0 |
|
||||
| a | g | ad,df,fb,fb | 30 km/h | 1275.7,487.5,304.7,0 | 1:0:0 |
|
||||
|
||||
|
||||
Scenario: Weighting based on speed file weights, ETA based on file durations
|
||||
|
||||
@@ -21,8 +21,7 @@ struct CustomizationConfig final : storage::IOConfig
|
||||
".osrm.partition",
|
||||
".osrm.cells",
|
||||
".osrm.ebg_nodes",
|
||||
".osrm.properties",
|
||||
".osrm.enw"},
|
||||
".osrm.properties"},
|
||||
{},
|
||||
{".osrm.cell_metrics", ".osrm.mldgr"}),
|
||||
requested_num_threads(0)
|
||||
|
||||
@@ -16,109 +16,28 @@ namespace osrm
|
||||
namespace customizer
|
||||
{
|
||||
|
||||
struct EdgeBasedGraphEdgeData
|
||||
using EdgeBasedGraphEdgeData = partitioner::EdgeBasedGraphEdgeData;
|
||||
|
||||
struct MultiLevelEdgeBasedGraph
|
||||
: public partitioner::MultiLevelGraph<EdgeBasedGraphEdgeData, storage::Ownership::Container>
|
||||
{
|
||||
NodeID turn_id; // ID of the edge based node (node based edge)
|
||||
using Base =
|
||||
partitioner::MultiLevelGraph<EdgeBasedGraphEdgeData, storage::Ownership::Container>;
|
||||
using Base::Base;
|
||||
};
|
||||
|
||||
template <typename EdgeDataT, storage::Ownership Ownership> class MultiLevelGraph;
|
||||
|
||||
namespace serialization
|
||||
struct MultiLevelEdgeBasedGraphView
|
||||
: public partitioner::MultiLevelGraph<EdgeBasedGraphEdgeData, storage::Ownership::View>
|
||||
{
|
||||
template <typename EdgeDataT, storage::Ownership Ownership>
|
||||
void read(storage::tar::FileReader &reader,
|
||||
const std::string &name,
|
||||
MultiLevelGraph<EdgeDataT, Ownership> &graph);
|
||||
|
||||
template <typename EdgeDataT, storage::Ownership Ownership>
|
||||
void write(storage::tar::FileWriter &writer,
|
||||
const std::string &name,
|
||||
const MultiLevelGraph<EdgeDataT, Ownership> &graph);
|
||||
}
|
||||
|
||||
template <typename EdgeDataT, storage::Ownership Ownership>
|
||||
class MultiLevelGraph : public partitioner::MultiLevelGraph<EdgeDataT, Ownership>
|
||||
{
|
||||
private:
|
||||
using SuperT = partitioner::MultiLevelGraph<EdgeDataT, Ownership>;
|
||||
using PartitionerGraphT = partitioner::MultiLevelGraph<partitioner::EdgeBasedGraphEdgeData,
|
||||
storage::Ownership::Container>;
|
||||
template <typename T> using Vector = util::ViewOrVector<T, Ownership>;
|
||||
|
||||
public:
|
||||
using NodeArrayEntry = typename SuperT::NodeArrayEntry;
|
||||
using EdgeArrayEntry = typename SuperT::EdgeArrayEntry;
|
||||
using EdgeOffset = typename SuperT::EdgeOffset;
|
||||
|
||||
MultiLevelGraph() = default;
|
||||
MultiLevelGraph(MultiLevelGraph &&) = default;
|
||||
MultiLevelGraph(const MultiLevelGraph &) = default;
|
||||
MultiLevelGraph &operator=(MultiLevelGraph &&) = default;
|
||||
MultiLevelGraph &operator=(const MultiLevelGraph &) = default;
|
||||
|
||||
MultiLevelGraph(PartitionerGraphT &&graph,
|
||||
Vector<EdgeWeight> node_weights_,
|
||||
Vector<EdgeDuration> node_durations_)
|
||||
: node_weights(std::move(node_weights_)), node_durations(std::move(node_durations_))
|
||||
{
|
||||
util::ViewOrVector<PartitionerGraphT::EdgeArrayEntry, storage::Ownership::Container>
|
||||
original_edge_array;
|
||||
|
||||
std::tie(SuperT::node_array,
|
||||
original_edge_array,
|
||||
SuperT::node_to_edge_offset,
|
||||
SuperT::connectivity_checksum) = std::move(graph).data();
|
||||
|
||||
SuperT::edge_array.reserve(original_edge_array.size());
|
||||
for (const auto &edge : original_edge_array)
|
||||
{
|
||||
SuperT::edge_array.push_back({edge.target, {edge.data.turn_id}});
|
||||
is_forward_edge.push_back(edge.data.forward);
|
||||
is_backward_edge.push_back(edge.data.backward);
|
||||
}
|
||||
}
|
||||
|
||||
MultiLevelGraph(Vector<NodeArrayEntry> node_array_,
|
||||
Vector<EdgeArrayEntry> edge_array_,
|
||||
Vector<EdgeOffset> node_to_edge_offset_,
|
||||
Vector<EdgeWeight> node_weights_,
|
||||
Vector<EdgeDuration> node_durations_,
|
||||
Vector<bool> is_forward_edge_,
|
||||
Vector<bool> is_backward_edge_)
|
||||
: SuperT(std::move(node_array_), std::move(edge_array_), std::move(node_to_edge_offset_)),
|
||||
node_weights(std::move(node_weights_)), node_durations(std::move(node_durations_)),
|
||||
is_forward_edge(is_forward_edge_), is_backward_edge(is_backward_edge_)
|
||||
{
|
||||
}
|
||||
|
||||
EdgeWeight GetNodeWeight(NodeID node) const { return node_weights[node]; }
|
||||
|
||||
EdgeWeight GetNodeDuration(NodeID node) const { return node_durations[node]; }
|
||||
|
||||
bool IsForwardEdge(EdgeID edge) const { return is_forward_edge[edge]; }
|
||||
|
||||
bool IsBackwardEdge(EdgeID edge) const { return is_backward_edge[edge]; }
|
||||
|
||||
friend void
|
||||
serialization::read<EdgeDataT, Ownership>(storage::tar::FileReader &reader,
|
||||
const std::string &name,
|
||||
MultiLevelGraph<EdgeDataT, Ownership> &graph);
|
||||
friend void
|
||||
serialization::write<EdgeDataT, Ownership>(storage::tar::FileWriter &writer,
|
||||
const std::string &name,
|
||||
const MultiLevelGraph<EdgeDataT, Ownership> &graph);
|
||||
|
||||
protected:
|
||||
Vector<EdgeWeight> node_weights;
|
||||
Vector<EdgeDuration> node_durations;
|
||||
Vector<bool> is_forward_edge;
|
||||
Vector<bool> is_backward_edge;
|
||||
using Base = partitioner::MultiLevelGraph<EdgeBasedGraphEdgeData, storage::Ownership::View>;
|
||||
using Base::Base;
|
||||
};
|
||||
|
||||
using MultiLevelEdgeBasedGraph =
|
||||
MultiLevelGraph<EdgeBasedGraphEdgeData, storage::Ownership::Container>;
|
||||
using MultiLevelEdgeBasedGraphView =
|
||||
MultiLevelGraph<EdgeBasedGraphEdgeData, storage::Ownership::View>;
|
||||
struct StaticEdgeBasedGraphEdge : MultiLevelEdgeBasedGraph::InputEdge
|
||||
{
|
||||
using Base = MultiLevelEdgeBasedGraph::InputEdge;
|
||||
using Base::Base;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -73,39 +73,6 @@ writeCellMetrics(const boost::filesystem::path &path,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reads .osrm.mldgr file
|
||||
template <typename MultiLevelGraphT>
|
||||
inline void readGraph(const boost::filesystem::path &path,
|
||||
MultiLevelGraphT &graph,
|
||||
std::uint32_t &connectivity_checksum)
|
||||
{
|
||||
static_assert(std::is_same<customizer::MultiLevelEdgeBasedGraphView, MultiLevelGraphT>::value ||
|
||||
std::is_same<customizer::MultiLevelEdgeBasedGraph, MultiLevelGraphT>::value,
|
||||
"");
|
||||
|
||||
storage::tar::FileReader reader{path, storage::tar::FileReader::VerifyFingerprint};
|
||||
|
||||
reader.ReadInto("/mld/connectivity_checksum", connectivity_checksum);
|
||||
serialization::read(reader, "/mld/multilevelgraph", graph);
|
||||
}
|
||||
|
||||
// writes .osrm.mldgr file
|
||||
template <typename MultiLevelGraphT>
|
||||
inline void writeGraph(const boost::filesystem::path &path,
|
||||
const MultiLevelGraphT &graph,
|
||||
const std::uint32_t connectivity_checksum)
|
||||
{
|
||||
static_assert(std::is_same<customizer::MultiLevelEdgeBasedGraphView, MultiLevelGraphT>::value ||
|
||||
std::is_same<customizer::MultiLevelEdgeBasedGraph, MultiLevelGraphT>::value,
|
||||
"");
|
||||
|
||||
storage::tar::FileWriter writer{path, storage::tar::FileWriter::GenerateFingerprint};
|
||||
|
||||
writer.WriteElementCount64("/mld/connectivity_checksum", 1);
|
||||
writer.WriteFrom("/mld/connectivity_checksum", connectivity_checksum);
|
||||
serialization::write(writer, "/mld/multilevelgraph", graph);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#ifndef OSRM_CUSTOMIZER_SERIALIZATION_HPP
|
||||
#define OSRM_CUSTOMIZER_SERIALIZATION_HPP
|
||||
|
||||
#include "customizer/edge_based_graph.hpp"
|
||||
|
||||
#include "partitioner/cell_storage.hpp"
|
||||
|
||||
#include "storage/serialization.hpp"
|
||||
@@ -33,34 +31,6 @@ inline void write(storage::tar::FileWriter &writer,
|
||||
storage::serialization::write(writer, name + "/weights", metric.weights);
|
||||
storage::serialization::write(writer, name + "/durations", metric.durations);
|
||||
}
|
||||
|
||||
template <typename EdgeDataT, storage::Ownership Ownership>
|
||||
inline void read(storage::tar::FileReader &reader,
|
||||
const std::string &name,
|
||||
MultiLevelGraph<EdgeDataT, Ownership> &graph)
|
||||
{
|
||||
storage::serialization::read(reader, name + "/node_array", graph.node_array);
|
||||
storage::serialization::read(reader, name + "/node_weights", graph.node_weights);
|
||||
storage::serialization::read(reader, name + "/node_durations", graph.node_durations);
|
||||
storage::serialization::read(reader, name + "/edge_array", graph.edge_array);
|
||||
storage::serialization::read(reader, name + "/is_forward_edge", graph.is_forward_edge);
|
||||
storage::serialization::read(reader, name + "/is_backward_edge", graph.is_backward_edge);
|
||||
storage::serialization::read(reader, name + "/node_to_edge_offset", graph.node_to_edge_offset);
|
||||
}
|
||||
|
||||
template <typename EdgeDataT, storage::Ownership Ownership>
|
||||
inline void write(storage::tar::FileWriter &writer,
|
||||
const std::string &name,
|
||||
const MultiLevelGraph<EdgeDataT, Ownership> &graph)
|
||||
{
|
||||
storage::serialization::write(writer, name + "/node_array", graph.node_array);
|
||||
storage::serialization::write(writer, name + "/node_weights", graph.node_weights);
|
||||
storage::serialization::write(writer, name + "/node_durations", graph.node_durations);
|
||||
storage::serialization::write(writer, name + "/edge_array", graph.edge_array);
|
||||
storage::serialization::write(writer, name + "/is_forward_edge", graph.is_forward_edge);
|
||||
storage::serialization::write(writer, name + "/is_backward_edge", graph.is_backward_edge);
|
||||
storage::serialization::write(writer, name + "/node_to_edge_offset", graph.node_to_edge_offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,9 +50,6 @@ template <typename AlgorithmT> struct HasMapMatching final : std::false_type
|
||||
template <typename AlgorithmT> struct HasManyToManySearch final : std::false_type
|
||||
{
|
||||
};
|
||||
template <typename AlgorithmT> struct SupportsDistanceAnnotationType final : std::false_type
|
||||
{
|
||||
};
|
||||
template <typename AlgorithmT> struct HasGetTileTurns final : std::false_type
|
||||
{
|
||||
};
|
||||
@@ -76,9 +73,6 @@ template <> struct HasMapMatching<ch::Algorithm> final : std::true_type
|
||||
template <> struct HasManyToManySearch<ch::Algorithm> final : std::true_type
|
||||
{
|
||||
};
|
||||
template <> struct SupportsDistanceAnnotationType<ch::Algorithm> final : std::true_type
|
||||
{
|
||||
};
|
||||
template <> struct HasGetTileTurns<ch::Algorithm> final : std::true_type
|
||||
{
|
||||
};
|
||||
@@ -102,9 +96,6 @@ template <> struct HasMapMatching<mld::Algorithm> final : std::true_type
|
||||
template <> struct HasManyToManySearch<mld::Algorithm> final : std::true_type
|
||||
{
|
||||
};
|
||||
template <> struct SupportsDistanceAnnotationType<mld::Algorithm> final : std::false_type
|
||||
{
|
||||
};
|
||||
template <> struct HasGetTileTurns<mld::Algorithm> final : std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
@@ -36,10 +36,9 @@ class TableAPI final : public BaseAPI
|
||||
{
|
||||
}
|
||||
|
||||
virtual void
|
||||
MakeResponse(const std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>> &tables,
|
||||
const std::vector<PhantomNode> &phantoms,
|
||||
util::json::Object &response) const
|
||||
virtual void MakeResponse(const std::vector<EdgeWeight> &durations,
|
||||
const std::vector<PhantomNode> &phantoms,
|
||||
util::json::Object &response) const
|
||||
{
|
||||
auto number_of_sources = parameters.sources.size();
|
||||
auto number_of_destinations = parameters.destinations.size();
|
||||
@@ -65,18 +64,8 @@ class TableAPI final : public BaseAPI
|
||||
response.values["destinations"] = MakeWaypoints(phantoms, parameters.destinations);
|
||||
}
|
||||
|
||||
if (parameters.annotations & TableParameters::AnnotationsType::Duration)
|
||||
{
|
||||
response.values["durations"] =
|
||||
MakeDurationTable(tables.first, number_of_sources, number_of_destinations);
|
||||
}
|
||||
|
||||
if (parameters.annotations & TableParameters::AnnotationsType::Distance)
|
||||
{
|
||||
response.values["distances"] =
|
||||
MakeDistanceTable(tables.second, number_of_sources, number_of_destinations);
|
||||
}
|
||||
|
||||
response.values["durations"] =
|
||||
MakeTable(durations, number_of_sources, number_of_destinations);
|
||||
response.values["code"] = "Ok";
|
||||
}
|
||||
|
||||
@@ -108,9 +97,9 @@ class TableAPI final : public BaseAPI
|
||||
return json_waypoints;
|
||||
}
|
||||
|
||||
virtual util::json::Array MakeDurationTable(const std::vector<EdgeWeight> &values,
|
||||
std::size_t number_of_rows,
|
||||
std::size_t number_of_columns) const
|
||||
virtual util::json::Array MakeTable(const std::vector<EdgeWeight> &values,
|
||||
std::size_t number_of_rows,
|
||||
std::size_t number_of_columns) const
|
||||
{
|
||||
util::json::Array json_table;
|
||||
for (const auto row : util::irange<std::size_t>(0UL, number_of_rows))
|
||||
@@ -127,7 +116,6 @@ class TableAPI final : public BaseAPI
|
||||
{
|
||||
return util::json::Value(util::json::Null());
|
||||
}
|
||||
// division by 10 because the duration is in deciseconds (10s)
|
||||
return util::json::Value(util::json::Number(duration / 10.));
|
||||
});
|
||||
json_table.values.push_back(std::move(json_row));
|
||||
@@ -135,34 +123,6 @@ class TableAPI final : public BaseAPI
|
||||
return json_table;
|
||||
}
|
||||
|
||||
virtual util::json::Array MakeDistanceTable(const std::vector<EdgeDistance> &values,
|
||||
std::size_t number_of_rows,
|
||||
std::size_t number_of_columns) const
|
||||
{
|
||||
util::json::Array json_table;
|
||||
for (const auto row : util::irange<std::size_t>(0UL, number_of_rows))
|
||||
{
|
||||
util::json::Array json_row;
|
||||
auto row_begin_iterator = values.begin() + (row * number_of_columns);
|
||||
auto row_end_iterator = values.begin() + ((row + 1) * number_of_columns);
|
||||
json_row.values.resize(number_of_columns);
|
||||
std::transform(row_begin_iterator,
|
||||
row_end_iterator,
|
||||
json_row.values.begin(),
|
||||
[](const EdgeDistance distance) {
|
||||
if (distance == INVALID_EDGE_DISTANCE)
|
||||
{
|
||||
return util::json::Value(util::json::Null());
|
||||
}
|
||||
// round to single decimal place
|
||||
return util::json::Value(
|
||||
util::json::Number(std::round(distance * 10) / 10.));
|
||||
});
|
||||
json_table.values.push_back(std::move(json_row));
|
||||
}
|
||||
return json_table;
|
||||
}
|
||||
|
||||
const TableParameters ¶meters;
|
||||
};
|
||||
|
||||
|
||||
@@ -60,16 +60,6 @@ struct TableParameters : public BaseParameters
|
||||
std::vector<std::size_t> sources;
|
||||
std::vector<std::size_t> destinations;
|
||||
|
||||
enum class AnnotationsType
|
||||
{
|
||||
None = 0,
|
||||
Duration = 0x01,
|
||||
Distance = 0x02,
|
||||
All = Duration | Distance
|
||||
};
|
||||
|
||||
AnnotationsType annotations = AnnotationsType::Duration;
|
||||
|
||||
TableParameters() = default;
|
||||
template <typename... Args>
|
||||
TableParameters(std::vector<std::size_t> sources_,
|
||||
@@ -80,16 +70,6 @@ struct TableParameters : public BaseParameters
|
||||
{
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
TableParameters(std::vector<std::size_t> sources_,
|
||||
std::vector<std::size_t> destinations_,
|
||||
const AnnotationsType annotations_,
|
||||
Args... args_)
|
||||
: BaseParameters{std::forward<Args>(args_)...}, sources{std::move(sources_)},
|
||||
destinations{std::move(destinations_)}, annotations{annotations_}
|
||||
{
|
||||
}
|
||||
|
||||
bool IsValid() const
|
||||
{
|
||||
if (!BaseParameters::IsValid())
|
||||
@@ -99,7 +79,7 @@ struct TableParameters : public BaseParameters
|
||||
if (coordinates.size() < 2)
|
||||
return false;
|
||||
|
||||
// 1/ The user is able to specify duplicates in srcs and dsts, in that case it's their fault
|
||||
// 1/ The user is able to specify duplicates in srcs and dsts, in that case it's her fault
|
||||
|
||||
// 2/ len(srcs) and len(dsts) smaller or equal to len(locations)
|
||||
if (sources.size() > coordinates.size())
|
||||
@@ -120,26 +100,6 @@ struct TableParameters : public BaseParameters
|
||||
return true;
|
||||
}
|
||||
};
|
||||
inline bool operator&(TableParameters::AnnotationsType lhs, TableParameters::AnnotationsType rhs)
|
||||
{
|
||||
return static_cast<bool>(
|
||||
static_cast<std::underlying_type_t<TableParameters::AnnotationsType>>(lhs) &
|
||||
static_cast<std::underlying_type_t<TableParameters::AnnotationsType>>(rhs));
|
||||
}
|
||||
|
||||
inline TableParameters::AnnotationsType operator|(TableParameters::AnnotationsType lhs,
|
||||
TableParameters::AnnotationsType rhs)
|
||||
{
|
||||
return (TableParameters::AnnotationsType)(
|
||||
static_cast<std::underlying_type_t<TableParameters::AnnotationsType>>(lhs) |
|
||||
static_cast<std::underlying_type_t<TableParameters::AnnotationsType>>(rhs));
|
||||
}
|
||||
|
||||
inline TableParameters::AnnotationsType &operator|=(TableParameters::AnnotationsType &lhs,
|
||||
TableParameters::AnnotationsType rhs)
|
||||
{
|
||||
return lhs = lhs | rhs;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#define OSRM_ENGINE_DATAFACADE_ALGORITHM_DATAFACADE_HPP
|
||||
|
||||
#include "contractor/query_edge.hpp"
|
||||
#include "customizer/edge_based_graph.hpp"
|
||||
#include "extractor/edge_based_edge.hpp"
|
||||
#include "engine/algorithm.hpp"
|
||||
|
||||
@@ -60,7 +59,7 @@ template <> class AlgorithmDataFacade<CH>
|
||||
template <> class AlgorithmDataFacade<MLD>
|
||||
{
|
||||
public:
|
||||
using EdgeData = customizer::EdgeBasedGraphEdgeData;
|
||||
using EdgeData = extractor::EdgeBasedEdge::EdgeData;
|
||||
using EdgeRange = util::range<EdgeID>;
|
||||
|
||||
// search graph access
|
||||
@@ -72,20 +71,12 @@ template <> class AlgorithmDataFacade<MLD>
|
||||
|
||||
virtual unsigned GetOutDegree(const NodeID n) const = 0;
|
||||
|
||||
virtual EdgeRange GetAdjacentEdgeRange(const NodeID node) const = 0;
|
||||
|
||||
virtual EdgeWeight GetNodeWeight(const NodeID node) const = 0;
|
||||
|
||||
virtual EdgeWeight GetNodeDuration(const NodeID node) const = 0; // TODO: to be removed
|
||||
|
||||
virtual bool IsForwardEdge(EdgeID edge) const = 0;
|
||||
|
||||
virtual bool IsBackwardEdge(EdgeID edge) const = 0;
|
||||
|
||||
virtual NodeID GetTarget(const EdgeID e) const = 0;
|
||||
|
||||
virtual const EdgeData &GetEdgeData(const EdgeID e) const = 0;
|
||||
|
||||
virtual EdgeRange GetAdjacentEdgeRange(const NodeID node) const = 0;
|
||||
|
||||
virtual const partitioner::MultiLevelPartitionView &GetMultiLevelPartition() const = 0;
|
||||
|
||||
virtual const partitioner::CellStorageView &GetCellStorage() const = 0;
|
||||
|
||||
@@ -133,6 +133,7 @@ class ContiguousInternalMemoryDataFacadeBase : public BaseDataFacade
|
||||
using RTreeNode = SharedRTree::TreeNode;
|
||||
|
||||
extractor::ClassData exclude_mask;
|
||||
std::string m_timestamp;
|
||||
extractor::ProfileProperties *m_profile_properties;
|
||||
extractor::Datasources *m_datasources;
|
||||
|
||||
@@ -282,13 +283,13 @@ class ContiguousInternalMemoryDataFacadeBase : public BaseDataFacade
|
||||
return segment_data.GetReverseDatasources(id);
|
||||
}
|
||||
|
||||
TurnPenalty GetWeightPenaltyForEdgeID(const EdgeID id) const override final
|
||||
TurnPenalty GetWeightPenaltyForEdgeID(const unsigned id) const override final
|
||||
{
|
||||
BOOST_ASSERT(m_turn_weight_penalties.size() > id);
|
||||
return m_turn_weight_penalties[id];
|
||||
}
|
||||
|
||||
TurnPenalty GetDurationPenaltyForEdgeID(const EdgeID id) const override final
|
||||
TurnPenalty GetDurationPenaltyForEdgeID(const unsigned id) const override final
|
||||
{
|
||||
BOOST_ASSERT(m_turn_duration_penalties.size() > id);
|
||||
return m_turn_duration_penalties[id];
|
||||
@@ -621,6 +622,7 @@ class ContiguousInternalMemoryDataFacade<CH>
|
||||
const std::size_t exclude_index)
|
||||
: ContiguousInternalMemoryDataFacadeBase(allocator, metric_name, exclude_index),
|
||||
ContiguousInternalMemoryAlgorithmDataFacade<CH>(allocator, metric_name, exclude_index)
|
||||
|
||||
{
|
||||
}
|
||||
};
|
||||
@@ -682,31 +684,6 @@ template <> class ContiguousInternalMemoryAlgorithmDataFacade<MLD> : public Algo
|
||||
return query_graph.GetOutDegree(n);
|
||||
}
|
||||
|
||||
EdgeRange GetAdjacentEdgeRange(const NodeID node) const override final
|
||||
{
|
||||
return query_graph.GetAdjacentEdgeRange(node);
|
||||
}
|
||||
|
||||
EdgeWeight GetNodeWeight(const NodeID node) const override final
|
||||
{
|
||||
return query_graph.GetNodeWeight(node);
|
||||
}
|
||||
|
||||
EdgeDuration GetNodeDuration(const NodeID node) const override final
|
||||
{
|
||||
return query_graph.GetNodeDuration(node);
|
||||
}
|
||||
|
||||
bool IsForwardEdge(const NodeID node) const override final
|
||||
{
|
||||
return query_graph.IsForwardEdge(node);
|
||||
}
|
||||
|
||||
bool IsBackwardEdge(const NodeID node) const override final
|
||||
{
|
||||
return query_graph.IsBackwardEdge(node);
|
||||
}
|
||||
|
||||
NodeID GetTarget(const EdgeID e) const override final { return query_graph.GetTarget(e); }
|
||||
|
||||
const EdgeData &GetEdgeData(const EdgeID e) const override final
|
||||
@@ -714,6 +691,11 @@ template <> class ContiguousInternalMemoryAlgorithmDataFacade<MLD> : public Algo
|
||||
return query_graph.GetEdgeData(e);
|
||||
}
|
||||
|
||||
EdgeRange GetAdjacentEdgeRange(const NodeID node) const override final
|
||||
{
|
||||
return query_graph.GetAdjacentEdgeRange(node);
|
||||
}
|
||||
|
||||
EdgeRange GetBorderEdgeRange(const LevelID level, const NodeID node) const override final
|
||||
{
|
||||
return query_graph.GetBorderEdgeRange(level, node);
|
||||
@@ -738,6 +720,7 @@ class ContiguousInternalMemoryDataFacade<MLD> final
|
||||
const std::size_t exclude_index)
|
||||
: ContiguousInternalMemoryDataFacadeBase(allocator, metric_name, exclude_index),
|
||||
ContiguousInternalMemoryAlgorithmDataFacade<MLD>(allocator, metric_name, exclude_index)
|
||||
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
#include <boost/range/any_range.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include <string>
|
||||
@@ -86,9 +87,9 @@ class BaseDataFacade
|
||||
virtual NodeForwardRange GetUncompressedForwardGeometry(const EdgeID id) const = 0;
|
||||
virtual NodeReverseRange GetUncompressedReverseGeometry(const EdgeID id) const = 0;
|
||||
|
||||
virtual TurnPenalty GetWeightPenaltyForEdgeID(const EdgeID id) const = 0;
|
||||
virtual TurnPenalty GetWeightPenaltyForEdgeID(const unsigned id) const = 0;
|
||||
|
||||
virtual TurnPenalty GetDurationPenaltyForEdgeID(const EdgeID id) const = 0;
|
||||
virtual TurnPenalty GetDurationPenaltyForEdgeID(const unsigned id) const = 0;
|
||||
|
||||
// Gets the weight values for each segment in an uncompressed geometry.
|
||||
// Should always be 1 shorter than GetUncompressedGeometry
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
@@ -448,9 +447,6 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
|
||||
const auto forward_durations = datafacade.GetUncompressedForwardDurations(geometry_id);
|
||||
const auto reverse_durations = datafacade.GetUncompressedReverseDurations(geometry_id);
|
||||
|
||||
const auto forward_geometry = datafacade.GetUncompressedForwardGeometry(geometry_id);
|
||||
const auto reverse_geometry = datafacade.GetUncompressedReverseGeometry(geometry_id);
|
||||
|
||||
const auto forward_weight_offset =
|
||||
std::accumulate(forward_weights.begin(),
|
||||
forward_weights.begin() + data.fwd_segment_position,
|
||||
@@ -461,50 +457,26 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
|
||||
forward_durations.begin() + data.fwd_segment_position,
|
||||
EdgeDuration{0});
|
||||
|
||||
EdgeDistance forward_distance_offset = 0;
|
||||
for (auto current = forward_geometry.begin();
|
||||
current < forward_geometry.begin() + data.fwd_segment_position;
|
||||
++current)
|
||||
{
|
||||
forward_distance_offset += util::coordinate_calculation::fccApproximateDistance(
|
||||
datafacade.GetCoordinateOfNode(*current),
|
||||
datafacade.GetCoordinateOfNode(*std::next(current)));
|
||||
}
|
||||
EdgeWeight forward_weight = forward_weights[data.fwd_segment_position];
|
||||
EdgeDuration forward_duration = forward_durations[data.fwd_segment_position];
|
||||
|
||||
BOOST_ASSERT(data.fwd_segment_position <
|
||||
std::distance(forward_durations.begin(), forward_durations.end()));
|
||||
|
||||
EdgeWeight forward_weight = forward_weights[data.fwd_segment_position];
|
||||
EdgeDuration forward_duration = forward_durations[data.fwd_segment_position];
|
||||
EdgeDistance forward_distance = util::coordinate_calculation::fccApproximateDistance(
|
||||
datafacade.GetCoordinateOfNode(forward_geometry(data.fwd_segment_position)),
|
||||
point_on_segment);
|
||||
|
||||
const auto rev_segment_position = reverse_weights.size() - data.fwd_segment_position - 1;
|
||||
|
||||
const auto reverse_weight_offset = std::accumulate(
|
||||
reverse_weights.begin(), reverse_weights.begin() + rev_segment_position, EdgeWeight{0});
|
||||
const auto reverse_weight_offset =
|
||||
std::accumulate(reverse_weights.begin(),
|
||||
reverse_weights.end() - data.fwd_segment_position - 1,
|
||||
EdgeWeight{0});
|
||||
|
||||
const auto reverse_duration_offset =
|
||||
std::accumulate(reverse_durations.begin(),
|
||||
reverse_durations.begin() + rev_segment_position,
|
||||
reverse_durations.end() - data.fwd_segment_position - 1,
|
||||
EdgeDuration{0});
|
||||
|
||||
EdgeDistance reverse_distance_offset = 0;
|
||||
for (auto current = reverse_geometry.begin();
|
||||
current < reverse_geometry.begin() + rev_segment_position;
|
||||
++current)
|
||||
{
|
||||
reverse_distance_offset += util::coordinate_calculation::fccApproximateDistance(
|
||||
datafacade.GetCoordinateOfNode(*current),
|
||||
datafacade.GetCoordinateOfNode(*std::next(current)));
|
||||
}
|
||||
|
||||
EdgeWeight reverse_weight = reverse_weights[rev_segment_position];
|
||||
EdgeDuration reverse_duration = reverse_durations[rev_segment_position];
|
||||
EdgeDistance reverse_distance = util::coordinate_calculation::fccApproximateDistance(
|
||||
point_on_segment,
|
||||
datafacade.GetCoordinateOfNode(reverse_geometry(rev_segment_position)));
|
||||
EdgeWeight reverse_weight =
|
||||
reverse_weights[reverse_weights.size() - data.fwd_segment_position - 1];
|
||||
EdgeDuration reverse_duration =
|
||||
reverse_durations[reverse_durations.size() - data.fwd_segment_position - 1];
|
||||
|
||||
ratio = std::min(1.0, std::max(0.0, ratio));
|
||||
if (data.forward_segment_id.id != SPECIAL_SEGMENTID)
|
||||
@@ -538,10 +510,6 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
|
||||
reverse_weight,
|
||||
forward_weight_offset,
|
||||
reverse_weight_offset,
|
||||
forward_distance,
|
||||
reverse_distance,
|
||||
forward_distance_offset,
|
||||
reverse_distance_offset,
|
||||
forward_duration,
|
||||
reverse_duration,
|
||||
forward_duration_offset,
|
||||
@@ -692,7 +660,7 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
|
||||
const CoordinateList &coordinates;
|
||||
DataFacadeT &datafacade;
|
||||
};
|
||||
} // namespace engine
|
||||
} // namespace osrm
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -63,8 +63,8 @@ struct Hint
|
||||
friend std::ostream &operator<<(std::ostream &, const Hint &);
|
||||
};
|
||||
|
||||
static_assert(sizeof(Hint) == 80 + 4, "Hint is bigger than expected");
|
||||
constexpr std::size_t ENCODED_HINT_SIZE = 112;
|
||||
static_assert(sizeof(Hint) == 64 + 4, "Hint is bigger than expected");
|
||||
constexpr std::size_t ENCODED_HINT_SIZE = 92;
|
||||
static_assert(ENCODED_HINT_SIZE / 4 * 3 >= sizeof(Hint),
|
||||
"ENCODED_HINT_SIZE does not match size of Hint");
|
||||
}
|
||||
|
||||
@@ -47,13 +47,10 @@ struct PhantomNode
|
||||
: forward_segment_id{SPECIAL_SEGMENTID, false},
|
||||
reverse_segment_id{SPECIAL_SEGMENTID, false}, forward_weight(INVALID_EDGE_WEIGHT),
|
||||
reverse_weight(INVALID_EDGE_WEIGHT), forward_weight_offset(0), reverse_weight_offset(0),
|
||||
forward_distance(INVALID_EDGE_DISTANCE), reverse_distance(INVALID_EDGE_DISTANCE),
|
||||
forward_distance_offset(0), reverse_distance_offset(0),
|
||||
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}, bearing(0)
|
||||
|
||||
{
|
||||
}
|
||||
|
||||
@@ -81,30 +78,6 @@ struct PhantomNode
|
||||
return reverse_duration + reverse_duration_offset;
|
||||
}
|
||||
|
||||
// DO THIS FOR DISTANCE
|
||||
|
||||
EdgeDistance GetForwardDistance() const
|
||||
{
|
||||
// ..... <-- forward_distance
|
||||
// .... <-- offset
|
||||
// ......... <-- desired distance
|
||||
// x <-- this is PhantomNode.location
|
||||
// 0----1----2----3----4 <-- EdgeBasedGraph Node segments
|
||||
BOOST_ASSERT(forward_segment_id.enabled);
|
||||
return forward_distance + forward_distance_offset;
|
||||
}
|
||||
|
||||
EdgeDistance GetReverseDistance() const
|
||||
{
|
||||
// .......... <-- reverse_distance
|
||||
// ... <-- offset
|
||||
// ............. <-- desired distance
|
||||
// x <-- this is PhantomNode.location
|
||||
// 0----1----2----3----4 <-- EdgeBasedGraph Node segments
|
||||
BOOST_ASSERT(reverse_segment_id.enabled);
|
||||
return reverse_distance + reverse_distance_offset;
|
||||
}
|
||||
|
||||
bool IsBidirected() const { return forward_segment_id.enabled && reverse_segment_id.enabled; }
|
||||
|
||||
bool IsValid(const unsigned number_of_nodes) const
|
||||
@@ -115,8 +88,6 @@ struct PhantomNode
|
||||
(reverse_weight != INVALID_EDGE_WEIGHT)) &&
|
||||
((forward_duration != MAXIMAL_EDGE_DURATION) ||
|
||||
(reverse_duration != MAXIMAL_EDGE_DURATION)) &&
|
||||
((forward_distance != INVALID_EDGE_DISTANCE) ||
|
||||
(reverse_distance != INVALID_EDGE_DISTANCE)) &&
|
||||
(component.id != INVALID_COMPONENTID);
|
||||
}
|
||||
|
||||
@@ -159,10 +130,6 @@ struct PhantomNode
|
||||
EdgeWeight reverse_weight,
|
||||
EdgeWeight forward_weight_offset,
|
||||
EdgeWeight reverse_weight_offset,
|
||||
EdgeDistance forward_distance,
|
||||
EdgeDistance reverse_distance,
|
||||
EdgeDistance forward_distance_offset,
|
||||
EdgeDistance reverse_distance_offset,
|
||||
EdgeWeight forward_duration,
|
||||
EdgeWeight reverse_duration,
|
||||
EdgeWeight forward_duration_offset,
|
||||
@@ -177,9 +144,7 @@ struct PhantomNode
|
||||
: 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},
|
||||
reverse_weight_offset{reverse_weight_offset}, forward_distance{forward_distance},
|
||||
reverse_distance{reverse_distance}, forward_distance_offset{forward_distance_offset},
|
||||
reverse_distance_offset{reverse_distance_offset}, forward_duration{forward_duration},
|
||||
reverse_weight_offset{reverse_weight_offset}, forward_duration{forward_duration},
|
||||
reverse_duration{reverse_duration}, forward_duration_offset{forward_duration_offset},
|
||||
reverse_duration_offset{reverse_duration_offset},
|
||||
component{component.id, component.is_tiny}, location{location},
|
||||
@@ -197,17 +162,13 @@ struct PhantomNode
|
||||
EdgeWeight reverse_weight;
|
||||
EdgeWeight forward_weight_offset; // TODO: try to remove -> requires path unpacking changes
|
||||
EdgeWeight reverse_weight_offset; // TODO: try to remove -> requires path unpacking changes
|
||||
EdgeDistance forward_distance;
|
||||
EdgeDistance reverse_distance;
|
||||
EdgeDistance forward_distance_offset; // TODO: try to remove -> requires path unpacking changes
|
||||
EdgeDistance reverse_distance_offset; // TODO: try to remove -> requires path unpacking changes
|
||||
EdgeWeight forward_duration;
|
||||
EdgeWeight reverse_duration;
|
||||
EdgeWeight forward_duration_offset; // TODO: try to remove -> requires path unpacking changes
|
||||
EdgeWeight reverse_duration_offset; // TODO: try to remove -> requires path unpacking changes
|
||||
ComponentID component;
|
||||
|
||||
util::Coordinate location; // this is the coordinate of x
|
||||
util::Coordinate location;
|
||||
util::Coordinate input_location;
|
||||
unsigned short fwd_segment_position;
|
||||
// is phantom node valid to be used as source or target
|
||||
@@ -219,7 +180,7 @@ struct PhantomNode
|
||||
unsigned short bearing : 12;
|
||||
};
|
||||
|
||||
static_assert(sizeof(PhantomNode) == 80, "PhantomNode has more padding then expected");
|
||||
static_assert(sizeof(PhantomNode) == 64, "PhantomNode has more padding then expected");
|
||||
|
||||
using PhantomNodePair = std::pair<PhantomNode, PhantomNode>;
|
||||
|
||||
|
||||
@@ -30,12 +30,10 @@ class RoutingAlgorithmsInterface
|
||||
virtual InternalRouteResult
|
||||
DirectShortestPathSearch(const PhantomNodes &phantom_node_pair) const = 0;
|
||||
|
||||
virtual std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
|
||||
virtual std::vector<EdgeDuration>
|
||||
ManyToManySearch(const std::vector<PhantomNode> &phantom_nodes,
|
||||
const std::vector<std::size_t> &source_indices,
|
||||
const std::vector<std::size_t> &target_indices,
|
||||
const bool calculate_distance,
|
||||
const bool calculate_duration) const = 0;
|
||||
const std::vector<std::size_t> &target_indices) const = 0;
|
||||
|
||||
virtual routing_algorithms::SubMatchingList
|
||||
MapMatching(const routing_algorithms::CandidateLists &candidates_list,
|
||||
@@ -55,7 +53,6 @@ class RoutingAlgorithmsInterface
|
||||
virtual bool HasDirectShortestPathSearch() const = 0;
|
||||
virtual bool HasMapMatching() const = 0;
|
||||
virtual bool HasManyToManySearch() const = 0;
|
||||
virtual bool SupportsDistanceAnnotationType() const = 0;
|
||||
virtual bool HasGetTileTurns() const = 0;
|
||||
virtual bool HasExcludeFlags() const = 0;
|
||||
virtual bool IsValid() const = 0;
|
||||
@@ -84,12 +81,10 @@ template <typename Algorithm> class RoutingAlgorithms final : public RoutingAlgo
|
||||
InternalRouteResult
|
||||
DirectShortestPathSearch(const PhantomNodes &phantom_nodes) const final override;
|
||||
|
||||
virtual std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
|
||||
std::vector<EdgeDuration>
|
||||
ManyToManySearch(const std::vector<PhantomNode> &phantom_nodes,
|
||||
const std::vector<std::size_t> &source_indices,
|
||||
const std::vector<std::size_t> &target_indices,
|
||||
const bool calculate_distance,
|
||||
const bool calculate_duration) const final override;
|
||||
const std::vector<std::size_t> &target_indices) const final override;
|
||||
|
||||
routing_algorithms::SubMatchingList
|
||||
MapMatching(const routing_algorithms::CandidateLists &candidates_list,
|
||||
@@ -129,11 +124,6 @@ template <typename Algorithm> class RoutingAlgorithms final : public RoutingAlgo
|
||||
return routing_algorithms::HasManyToManySearch<Algorithm>::value;
|
||||
}
|
||||
|
||||
bool SupportsDistanceAnnotationType() const final override
|
||||
{
|
||||
return routing_algorithms::SupportsDistanceAnnotationType<Algorithm>::value;
|
||||
}
|
||||
|
||||
bool HasGetTileTurns() const final override
|
||||
{
|
||||
return routing_algorithms::HasGetTileTurns<Algorithm>::value;
|
||||
@@ -194,12 +184,10 @@ inline routing_algorithms::SubMatchingList RoutingAlgorithms<Algorithm>::MapMatc
|
||||
}
|
||||
|
||||
template <typename Algorithm>
|
||||
std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
|
||||
RoutingAlgorithms<Algorithm>::ManyToManySearch(const std::vector<PhantomNode> &phantom_nodes,
|
||||
const std::vector<std::size_t> &_source_indices,
|
||||
const std::vector<std::size_t> &_target_indices,
|
||||
const bool calculate_distance,
|
||||
const bool calculate_duration) const
|
||||
std::vector<EdgeDuration> RoutingAlgorithms<Algorithm>::ManyToManySearch(
|
||||
const std::vector<PhantomNode> &phantom_nodes,
|
||||
const std::vector<std::size_t> &_source_indices,
|
||||
const std::vector<std::size_t> &_target_indices) const
|
||||
{
|
||||
BOOST_ASSERT(!phantom_nodes.empty());
|
||||
|
||||
@@ -217,13 +205,8 @@ RoutingAlgorithms<Algorithm>::ManyToManySearch(const std::vector<PhantomNode> &p
|
||||
std::iota(target_indices.begin(), target_indices.end(), 0);
|
||||
}
|
||||
|
||||
return routing_algorithms::manyToManySearch(heaps,
|
||||
*facade,
|
||||
phantom_nodes,
|
||||
std::move(source_indices),
|
||||
std::move(target_indices),
|
||||
calculate_distance,
|
||||
calculate_duration);
|
||||
return routing_algorithms::manyToManySearch(
|
||||
heaps, *facade, phantom_nodes, std::move(source_indices), std::move(target_indices));
|
||||
}
|
||||
|
||||
template <typename Algorithm>
|
||||
|
||||
@@ -15,43 +15,23 @@ namespace engine
|
||||
{
|
||||
namespace routing_algorithms
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
struct NodeBucket
|
||||
{
|
||||
NodeID middle_node;
|
||||
NodeID parent_node;
|
||||
unsigned column_index : 31; // a column in the weight/duration matrix
|
||||
unsigned from_clique_arc : 1;
|
||||
unsigned column_index; // a column in the weight/duration matrix
|
||||
EdgeWeight weight;
|
||||
EdgeDuration duration;
|
||||
|
||||
NodeBucket(NodeID middle_node,
|
||||
NodeID parent_node,
|
||||
bool from_clique_arc,
|
||||
unsigned column_index,
|
||||
EdgeWeight weight,
|
||||
EdgeDuration duration)
|
||||
: middle_node(middle_node), parent_node(parent_node), column_index(column_index),
|
||||
from_clique_arc(from_clique_arc), weight(weight), duration(duration)
|
||||
{
|
||||
}
|
||||
|
||||
NodeBucket(NodeID middle_node,
|
||||
NodeID parent_node,
|
||||
unsigned column_index,
|
||||
EdgeWeight weight,
|
||||
EdgeDuration duration)
|
||||
: middle_node(middle_node), parent_node(parent_node), column_index(column_index),
|
||||
from_clique_arc(false), weight(weight), duration(duration)
|
||||
NodeBucket(NodeID middle_node, unsigned column_index, EdgeWeight weight, EdgeDuration duration)
|
||||
: middle_node(middle_node), column_index(column_index), weight(weight), duration(duration)
|
||||
{
|
||||
}
|
||||
|
||||
// partial order comparison
|
||||
bool operator<(const NodeBucket &rhs) const
|
||||
{
|
||||
return std::tie(middle_node, column_index) < std::tie(rhs.middle_node, rhs.column_index);
|
||||
}
|
||||
bool operator<(const NodeBucket &rhs) const { return middle_node < rhs.middle_node; }
|
||||
|
||||
// functor for equal_range
|
||||
struct Compare
|
||||
@@ -66,36 +46,15 @@ struct NodeBucket
|
||||
return lhs < rhs.middle_node;
|
||||
}
|
||||
};
|
||||
|
||||
// functor for equal_range
|
||||
struct ColumnCompare
|
||||
{
|
||||
unsigned column_idx;
|
||||
|
||||
ColumnCompare(unsigned column_idx) : column_idx(column_idx){};
|
||||
|
||||
bool operator()(const NodeBucket &lhs, const NodeID &rhs) const // lowerbound
|
||||
{
|
||||
return std::tie(lhs.middle_node, lhs.column_index) < std::tie(rhs, column_idx);
|
||||
}
|
||||
|
||||
bool operator()(const NodeID &lhs, const NodeBucket &rhs) const // upperbound
|
||||
{
|
||||
return std::tie(lhs, column_idx) < std::tie(rhs.middle_node, rhs.column_index);
|
||||
}
|
||||
};
|
||||
};
|
||||
} // namespace
|
||||
}
|
||||
|
||||
template <typename Algorithm>
|
||||
std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
|
||||
manyToManySearch(SearchEngineData<Algorithm> &engine_working_data,
|
||||
const DataFacade<Algorithm> &facade,
|
||||
const std::vector<PhantomNode> &phantom_nodes,
|
||||
const std::vector<std::size_t> &source_indices,
|
||||
const std::vector<std::size_t> &target_indices,
|
||||
const bool calculate_distance,
|
||||
const bool calculate_duration);
|
||||
std::vector<EdgeDuration> manyToManySearch(SearchEngineData<Algorithm> &engine_working_data,
|
||||
const DataFacade<Algorithm> &facade,
|
||||
const std::vector<PhantomNode> &phantom_nodes,
|
||||
const std::vector<std::size_t> &source_indices,
|
||||
const std::vector<std::size_t> &target_indices);
|
||||
|
||||
} // namespace routing_algorithms
|
||||
} // namespace engine
|
||||
|
||||
@@ -44,19 +44,50 @@ bool needsLoopBackwards(const PhantomNode &source_phantom, const PhantomNode &ta
|
||||
bool needsLoopForward(const PhantomNodes &phantoms);
|
||||
bool needsLoopBackwards(const PhantomNodes &phantoms);
|
||||
|
||||
namespace detail
|
||||
template <typename Heap>
|
||||
void insertNodesInHeaps(Heap &forward_heap, Heap &reverse_heap, const PhantomNodes &nodes)
|
||||
{
|
||||
template <typename Algorithm>
|
||||
void insertSourceInHeap(typename SearchEngineData<Algorithm>::ManyToManyQueryHeap &heap,
|
||||
const PhantomNode &phantom_node)
|
||||
const auto &source = nodes.source_phantom;
|
||||
if (source.IsValidForwardSource())
|
||||
{
|
||||
forward_heap.Insert(source.forward_segment_id.id,
|
||||
-source.GetForwardWeightPlusOffset(),
|
||||
source.forward_segment_id.id);
|
||||
}
|
||||
|
||||
if (source.IsValidReverseSource())
|
||||
{
|
||||
forward_heap.Insert(source.reverse_segment_id.id,
|
||||
-source.GetReverseWeightPlusOffset(),
|
||||
source.reverse_segment_id.id);
|
||||
}
|
||||
|
||||
const auto &target = nodes.target_phantom;
|
||||
if (target.IsValidForwardTarget())
|
||||
{
|
||||
reverse_heap.Insert(target.forward_segment_id.id,
|
||||
target.GetForwardWeightPlusOffset(),
|
||||
target.forward_segment_id.id);
|
||||
}
|
||||
|
||||
if (target.IsValidReverseTarget())
|
||||
{
|
||||
reverse_heap.Insert(target.reverse_segment_id.id,
|
||||
target.GetReverseWeightPlusOffset(),
|
||||
target.reverse_segment_id.id);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename ManyToManyQueryHeap>
|
||||
void insertSourceInHeap(ManyToManyQueryHeap &heap, const PhantomNode &phantom_node)
|
||||
{
|
||||
if (phantom_node.IsValidForwardTarget())
|
||||
if (phantom_node.IsValidForwardSource())
|
||||
{
|
||||
heap.Insert(phantom_node.forward_segment_id.id,
|
||||
-phantom_node.GetForwardWeightPlusOffset(),
|
||||
{phantom_node.forward_segment_id.id, -phantom_node.GetForwardDuration()});
|
||||
}
|
||||
if (phantom_node.IsValidReverseTarget())
|
||||
if (phantom_node.IsValidReverseSource())
|
||||
{
|
||||
heap.Insert(phantom_node.reverse_segment_id.id,
|
||||
-phantom_node.GetReverseWeightPlusOffset(),
|
||||
@@ -64,9 +95,8 @@ void insertSourceInHeap(typename SearchEngineData<Algorithm>::ManyToManyQueryHea
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Algorithm>
|
||||
void insertTargetInHeap(typename SearchEngineData<Algorithm>::ManyToManyQueryHeap &heap,
|
||||
const PhantomNode &phantom_node)
|
||||
template <typename ManyToManyQueryHeap>
|
||||
void insertTargetInHeap(ManyToManyQueryHeap &heap, const PhantomNode &phantom_node)
|
||||
{
|
||||
if (phantom_node.IsValidForwardTarget())
|
||||
{
|
||||
@@ -82,109 +112,6 @@ void insertTargetInHeap(typename SearchEngineData<Algorithm>::ManyToManyQueryHea
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Algorithm>
|
||||
void insertSourceInHeap(typename SearchEngineData<Algorithm>::QueryHeap &heap,
|
||||
const PhantomNode &phantom_node)
|
||||
{
|
||||
if (phantom_node.IsValidForwardSource())
|
||||
{
|
||||
heap.Insert(phantom_node.forward_segment_id.id,
|
||||
-phantom_node.GetForwardWeightPlusOffset(),
|
||||
phantom_node.forward_segment_id.id);
|
||||
}
|
||||
if (phantom_node.IsValidReverseSource())
|
||||
{
|
||||
heap.Insert(phantom_node.reverse_segment_id.id,
|
||||
-phantom_node.GetReverseWeightPlusOffset(),
|
||||
phantom_node.reverse_segment_id.id);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Algorithm>
|
||||
void insertTargetInHeap(typename SearchEngineData<Algorithm>::QueryHeap &heap,
|
||||
const PhantomNode &phantom_node)
|
||||
{
|
||||
if (phantom_node.IsValidForwardTarget())
|
||||
{
|
||||
heap.Insert(phantom_node.forward_segment_id.id,
|
||||
phantom_node.GetForwardWeightPlusOffset(),
|
||||
phantom_node.forward_segment_id.id);
|
||||
}
|
||||
if (phantom_node.IsValidReverseTarget())
|
||||
{
|
||||
heap.Insert(phantom_node.reverse_segment_id.id,
|
||||
phantom_node.GetReverseWeightPlusOffset(),
|
||||
phantom_node.reverse_segment_id.id);
|
||||
}
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
inline void insertTargetInHeap(typename SearchEngineData<mld::Algorithm>::ManyToManyQueryHeap &heap,
|
||||
const PhantomNode &phantom_node)
|
||||
{
|
||||
detail::insertTargetInHeap<mld::Algorithm>(heap, phantom_node);
|
||||
}
|
||||
inline void insertTargetInHeap(typename SearchEngineData<ch::Algorithm>::ManyToManyQueryHeap &heap,
|
||||
const PhantomNode &phantom_node)
|
||||
{
|
||||
detail::insertTargetInHeap<ch::Algorithm>(heap, phantom_node);
|
||||
}
|
||||
inline void insertTargetInHeap(typename SearchEngineData<mld::Algorithm>::QueryHeap &heap,
|
||||
const PhantomNode &phantom_node)
|
||||
{
|
||||
detail::insertTargetInHeap<mld::Algorithm>(heap, phantom_node);
|
||||
}
|
||||
inline void insertTargetInHeap(typename SearchEngineData<ch::Algorithm>::QueryHeap &heap,
|
||||
const PhantomNode &phantom_node)
|
||||
{
|
||||
detail::insertTargetInHeap<ch::Algorithm>(heap, phantom_node);
|
||||
}
|
||||
inline void insertSourceInHeap(typename SearchEngineData<mld::Algorithm>::ManyToManyQueryHeap &heap,
|
||||
const PhantomNode &phantom_node)
|
||||
{
|
||||
detail::insertSourceInHeap<mld::Algorithm>(heap, phantom_node);
|
||||
}
|
||||
inline void insertSourceInHeap(typename SearchEngineData<ch::Algorithm>::ManyToManyQueryHeap &heap,
|
||||
const PhantomNode &phantom_node)
|
||||
{
|
||||
detail::insertSourceInHeap<ch::Algorithm>(heap, phantom_node);
|
||||
}
|
||||
inline void insertSourceInHeap(typename SearchEngineData<mld::Algorithm>::QueryHeap &heap,
|
||||
const PhantomNode &phantom_node)
|
||||
{
|
||||
detail::insertSourceInHeap<mld::Algorithm>(heap, phantom_node);
|
||||
}
|
||||
inline void insertSourceInHeap(typename SearchEngineData<ch::Algorithm>::QueryHeap &heap,
|
||||
const PhantomNode &phantom_node)
|
||||
{
|
||||
detail::insertSourceInHeap<ch::Algorithm>(heap, phantom_node);
|
||||
}
|
||||
|
||||
template <typename Heap>
|
||||
void insertNodesInHeaps(Heap &forward_heap, Heap &reverse_heap, const PhantomNodes &nodes)
|
||||
{
|
||||
insertSourceInHeap(forward_heap, nodes.source_phantom);
|
||||
insertTargetInHeap(reverse_heap, nodes.target_phantom);
|
||||
}
|
||||
|
||||
template <typename Algorithm>
|
||||
void insertSourceInHeap(typename SearchEngineData<Algorithm>::ManyToManyQueryHeap &heap,
|
||||
const PhantomNode &phantom_node)
|
||||
{
|
||||
if (phantom_node.IsValidForwardSource())
|
||||
{
|
||||
heap.Insert(phantom_node.forward_segment_id.id,
|
||||
-phantom_node.GetForwardWeightPlusOffset(),
|
||||
{phantom_node.forward_segment_id.id, -phantom_node.GetForwardDuration()});
|
||||
}
|
||||
if (phantom_node.IsValidReverseSource())
|
||||
{
|
||||
heap.Insert(phantom_node.reverse_segment_id.id,
|
||||
-phantom_node.GetReverseWeightPlusOffset(),
|
||||
{phantom_node.reverse_segment_id.id, -phantom_node.GetReverseDuration()});
|
||||
}
|
||||
}
|
||||
|
||||
template <typename FacadeT>
|
||||
void annotatePath(const FacadeT &facade,
|
||||
const PhantomNodes &phantom_node_pair,
|
||||
@@ -254,7 +181,6 @@ void annotatePath(const FacadeT &facade,
|
||||
BOOST_ASSERT(datasource_vector.size() > 0);
|
||||
BOOST_ASSERT(weight_vector.size() + 1 == id_vector.size());
|
||||
BOOST_ASSERT(duration_vector.size() + 1 == id_vector.size());
|
||||
|
||||
const bool is_first_segment = unpacked_path.empty();
|
||||
|
||||
const std::size_t start_index =
|
||||
@@ -394,10 +320,58 @@ void annotatePath(const FacadeT &facade,
|
||||
}
|
||||
}
|
||||
|
||||
EdgeDistance adjustPathDistanceToPhantomNodes(const std::vector<NodeID> &path,
|
||||
const PhantomNode &source_phantom,
|
||||
const PhantomNode &target_phantom,
|
||||
const EdgeDistance distance);
|
||||
template <typename Algorithm>
|
||||
double getPathDistance(const DataFacade<Algorithm> &facade,
|
||||
const std::vector<PathData> unpacked_path,
|
||||
const PhantomNode &source_phantom,
|
||||
const PhantomNode &target_phantom)
|
||||
{
|
||||
using util::coordinate_calculation::detail::DEGREE_TO_RAD;
|
||||
using util::coordinate_calculation::detail::EARTH_RADIUS;
|
||||
|
||||
double distance = 0;
|
||||
double prev_lat =
|
||||
static_cast<double>(util::toFloating(source_phantom.location.lat)) * DEGREE_TO_RAD;
|
||||
double prev_lon =
|
||||
static_cast<double>(util::toFloating(source_phantom.location.lon)) * DEGREE_TO_RAD;
|
||||
double prev_cos = std::cos(prev_lat);
|
||||
for (const auto &p : unpacked_path)
|
||||
{
|
||||
const auto current_coordinate = facade.GetCoordinateOfNode(p.turn_via_node);
|
||||
|
||||
const double current_lat =
|
||||
static_cast<double>(util::toFloating(current_coordinate.lat)) * DEGREE_TO_RAD;
|
||||
const double current_lon =
|
||||
static_cast<double>(util::toFloating(current_coordinate.lon)) * DEGREE_TO_RAD;
|
||||
const double current_cos = std::cos(current_lat);
|
||||
|
||||
const double sin_dlon = std::sin((prev_lon - current_lon) / 2.0);
|
||||
const double sin_dlat = std::sin((prev_lat - current_lat) / 2.0);
|
||||
|
||||
const double aharv = sin_dlat * sin_dlat + prev_cos * current_cos * sin_dlon * sin_dlon;
|
||||
const double charv = 2. * std::atan2(std::sqrt(aharv), std::sqrt(1.0 - aharv));
|
||||
distance += EARTH_RADIUS * charv;
|
||||
|
||||
prev_lat = current_lat;
|
||||
prev_lon = current_lon;
|
||||
prev_cos = current_cos;
|
||||
}
|
||||
|
||||
const double current_lat =
|
||||
static_cast<double>(util::toFloating(target_phantom.location.lat)) * DEGREE_TO_RAD;
|
||||
const double current_lon =
|
||||
static_cast<double>(util::toFloating(target_phantom.location.lon)) * DEGREE_TO_RAD;
|
||||
const double current_cos = std::cos(current_lat);
|
||||
|
||||
const double sin_dlon = std::sin((prev_lon - current_lon) / 2.0);
|
||||
const double sin_dlat = std::sin((prev_lat - current_lat) / 2.0);
|
||||
|
||||
const double aharv = sin_dlat * sin_dlat + prev_cos * current_cos * sin_dlon * sin_dlon;
|
||||
const double charv = 2. * std::atan2(std::sqrt(aharv), std::sqrt(1.0 - aharv));
|
||||
distance += EARTH_RADIUS * charv;
|
||||
|
||||
return distance;
|
||||
}
|
||||
|
||||
template <typename AlgorithmT>
|
||||
InternalRouteResult extractRoute(const DataFacade<AlgorithmT> &facade,
|
||||
@@ -431,22 +405,6 @@ InternalRouteResult extractRoute(const DataFacade<AlgorithmT> &facade,
|
||||
return raw_route_data;
|
||||
}
|
||||
|
||||
template <typename FacadeT> EdgeDistance computeEdgeDistance(const FacadeT &facade, NodeID node_id)
|
||||
{
|
||||
const auto geometry_index = facade.GetGeometryIndex(node_id);
|
||||
|
||||
EdgeDistance total_distance = 0.0;
|
||||
|
||||
auto geometry_range = facade.GetUncompressedForwardGeometry(geometry_index.id);
|
||||
for (auto current = geometry_range.begin(); current < geometry_range.end() - 1; ++current)
|
||||
{
|
||||
total_distance += util::coordinate_calculation::fccApproximateDistance(
|
||||
facade.GetCoordinateOfNode(*current), facade.GetCoordinateOfNode(*std::next(current)));
|
||||
}
|
||||
|
||||
return total_distance;
|
||||
}
|
||||
|
||||
} // namespace routing_algorithms
|
||||
} // namespace engine
|
||||
} // namespace osrm
|
||||
|
||||
@@ -288,106 +288,6 @@ void unpackPath(const DataFacade<Algorithm> &facade,
|
||||
}
|
||||
}
|
||||
|
||||
template <typename BidirectionalIterator>
|
||||
EdgeDistance calculateEBGNodeAnnotations(const DataFacade<Algorithm> &facade,
|
||||
BidirectionalIterator packed_path_begin,
|
||||
BidirectionalIterator packed_path_end)
|
||||
{
|
||||
// Make sure we have at least something to unpack
|
||||
if (packed_path_begin == packed_path_end ||
|
||||
std::distance(packed_path_begin, packed_path_end) <= 1)
|
||||
return 0;
|
||||
|
||||
std::stack<std::tuple<NodeID, NodeID, bool>> recursion_stack;
|
||||
std::stack<EdgeDistance> distance_stack;
|
||||
// We have to push the path in reverse order onto the stack because it's LIFO.
|
||||
for (auto current = std::prev(packed_path_end); current > packed_path_begin;
|
||||
current = std::prev(current))
|
||||
{
|
||||
recursion_stack.emplace(*std::prev(current), *current, false);
|
||||
}
|
||||
|
||||
std::tuple<NodeID, NodeID, bool> edge;
|
||||
while (!recursion_stack.empty())
|
||||
{
|
||||
edge = recursion_stack.top();
|
||||
recursion_stack.pop();
|
||||
|
||||
// Have we processed the edge before? tells us if we have values in the durations stack that
|
||||
// we can add up
|
||||
if (!std::get<2>(edge))
|
||||
{ // haven't processed edge before, so process it in the body!
|
||||
|
||||
std::get<2>(edge) = true; // mark that this edge will now be processed
|
||||
|
||||
// Look for an edge on the forward CH graph (.forward)
|
||||
EdgeID smaller_edge_id =
|
||||
facade.FindSmallestEdge(std::get<0>(edge), std::get<1>(edge), [](const auto &data) {
|
||||
return data.forward;
|
||||
});
|
||||
|
||||
// If we didn't find one there, the we might be looking at a part of the path that
|
||||
// was found using the backward search. Here, we flip the node order (.second,
|
||||
// .first) and only consider edges with the `.backward` flag.
|
||||
if (SPECIAL_EDGEID == smaller_edge_id)
|
||||
{
|
||||
smaller_edge_id =
|
||||
facade.FindSmallestEdge(std::get<1>(edge),
|
||||
std::get<0>(edge),
|
||||
[](const auto &data) { return data.backward; });
|
||||
}
|
||||
|
||||
// If we didn't find anything *still*, then something is broken and someone has
|
||||
// called this function with bad values.
|
||||
BOOST_ASSERT_MSG(smaller_edge_id != SPECIAL_EDGEID, "Invalid smaller edge ID");
|
||||
|
||||
const auto &data = facade.GetEdgeData(smaller_edge_id);
|
||||
BOOST_ASSERT_MSG(data.weight != std::numeric_limits<EdgeWeight>::max(),
|
||||
"edge weight invalid");
|
||||
|
||||
// If the edge is a shortcut, we need to add the two halfs to the stack.
|
||||
if (data.shortcut)
|
||||
{ // unpack
|
||||
const NodeID middle_node_id = data.turn_id;
|
||||
// Note the order here - we're adding these to a stack, so we
|
||||
// want the first->middle to get visited before middle->second
|
||||
recursion_stack.emplace(edge);
|
||||
recursion_stack.emplace(middle_node_id, std::get<1>(edge), false);
|
||||
recursion_stack.emplace(std::get<0>(edge), middle_node_id, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// compute the duration here and put it onto the duration stack using method
|
||||
// similar to annotatePath but smaller
|
||||
EdgeDistance distance = computeEdgeDistance(facade, std::get<0>(edge));
|
||||
distance_stack.emplace(distance);
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // the edge has already been processed. this means that there are enough values in the
|
||||
// distances stack
|
||||
|
||||
BOOST_ASSERT_MSG(distance_stack.size() >= 2,
|
||||
"There are not enough (at least 2) values on the distance stack");
|
||||
EdgeDistance distance1 = distance_stack.top();
|
||||
distance_stack.pop();
|
||||
EdgeDistance distance2 = distance_stack.top();
|
||||
distance_stack.pop();
|
||||
EdgeDistance distance = distance1 + distance2;
|
||||
distance_stack.emplace(distance);
|
||||
}
|
||||
}
|
||||
|
||||
EdgeDistance total_distance = 0;
|
||||
while (!distance_stack.empty())
|
||||
{
|
||||
total_distance += distance_stack.top();
|
||||
distance_stack.pop();
|
||||
}
|
||||
|
||||
return total_distance;
|
||||
}
|
||||
|
||||
template <typename RandomIter, typename FacadeT>
|
||||
void unpackPath(const FacadeT &facade,
|
||||
RandomIter packed_path_begin,
|
||||
@@ -440,11 +340,6 @@ void retrievePackedPathFromSingleHeap(const SearchEngineData<Algorithm>::QueryHe
|
||||
const NodeID middle_node_id,
|
||||
std::vector<NodeID> &packed_path);
|
||||
|
||||
void retrievePackedPathFromSingleManyToManyHeap(
|
||||
const SearchEngineData<Algorithm>::ManyToManyQueryHeap &search_heap,
|
||||
const NodeID middle_node_id,
|
||||
std::vector<NodeID> &packed_path);
|
||||
|
||||
// assumes that heaps are already setup correctly.
|
||||
// ATTENTION: This only works if no additional offset is supplied next to the Phantom Node
|
||||
// Offsets.
|
||||
|
||||
@@ -54,7 +54,7 @@ inline bool checkParentCellRestriction(CellID, const PhantomNodes &) { return tr
|
||||
|
||||
// Restricted search (Args is LevelID, CellID):
|
||||
// * use the fixed level for queries
|
||||
// * check if the node cell is the same as the specified parent
|
||||
// * check if the node cell is the same as the specified parent onr
|
||||
template <typename MultiLevelPartition>
|
||||
inline LevelID getNodeQueryLevel(const MultiLevelPartition &, NodeID, LevelID level, CellID)
|
||||
{
|
||||
@@ -65,62 +65,8 @@ inline bool checkParentCellRestriction(CellID cell, LevelID, CellID parent)
|
||||
{
|
||||
return cell == parent;
|
||||
}
|
||||
|
||||
// Unrestricted search with a single phantom node (Args is const PhantomNode &):
|
||||
// * use partition.GetQueryLevel to find the node query level
|
||||
// * allow to traverse all cells
|
||||
template <typename MultiLevelPartition>
|
||||
inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition,
|
||||
const NodeID node,
|
||||
const PhantomNode &phantom_node)
|
||||
{
|
||||
auto highest_diffrent_level = [&partition, node](const SegmentID &phantom_node) {
|
||||
if (phantom_node.enabled)
|
||||
return partition.GetHighestDifferentLevel(phantom_node.id, node);
|
||||
return INVALID_LEVEL_ID;
|
||||
};
|
||||
|
||||
const auto node_level = std::min(highest_diffrent_level(phantom_node.forward_segment_id),
|
||||
highest_diffrent_level(phantom_node.reverse_segment_id));
|
||||
|
||||
return node_level;
|
||||
}
|
||||
|
||||
// Unrestricted search with a single phantom node and a vector of phantom nodes:
|
||||
// * use partition.GetQueryLevel to find the node query level
|
||||
// * allow to traverse all cells
|
||||
template <typename MultiLevelPartition>
|
||||
inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition,
|
||||
NodeID node,
|
||||
const std::vector<PhantomNode> &phantom_nodes,
|
||||
const std::size_t phantom_index,
|
||||
const std::vector<std::size_t> &phantom_indices)
|
||||
{
|
||||
auto min_level = [&partition, node](const PhantomNode &phantom_node) {
|
||||
const auto &forward_segment = phantom_node.forward_segment_id;
|
||||
const auto forward_level =
|
||||
forward_segment.enabled ? partition.GetHighestDifferentLevel(node, forward_segment.id)
|
||||
: INVALID_LEVEL_ID;
|
||||
|
||||
const auto &reverse_segment = phantom_node.reverse_segment_id;
|
||||
const auto reverse_level =
|
||||
reverse_segment.enabled ? partition.GetHighestDifferentLevel(node, reverse_segment.id)
|
||||
: INVALID_LEVEL_ID;
|
||||
|
||||
return std::min(forward_level, reverse_level);
|
||||
};
|
||||
|
||||
// Get minimum level over all phantoms of the highest different level with respect to node
|
||||
// This is equivalent to min_{∀ source, target} partition.GetQueryLevel(source, node, target)
|
||||
auto result = min_level(phantom_nodes[phantom_index]);
|
||||
for (const auto &index : phantom_indices)
|
||||
{
|
||||
result = std::min(result, min_level(phantom_nodes[index]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// Heaps only record for each node its predecessor ("parent") on the shortest path.
|
||||
// For re-constructing the actual path we need to trace back all parent "pointers".
|
||||
// In contrast to the CH code MLD needs to know the edges (with clique arc property).
|
||||
@@ -128,46 +74,6 @@ inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition,
|
||||
using PackedEdge = std::tuple</*from*/ NodeID, /*to*/ NodeID, /*from_clique_arc*/ bool>;
|
||||
using PackedPath = std::vector<PackedEdge>;
|
||||
|
||||
template <bool DIRECTION, typename OutIter>
|
||||
inline void retrievePackedPathFromSingleManyToManyHeap(
|
||||
const SearchEngineData<Algorithm>::ManyToManyQueryHeap &heap, const NodeID middle, OutIter out)
|
||||
{
|
||||
|
||||
NodeID current = middle;
|
||||
NodeID parent = heap.GetData(current).parent;
|
||||
|
||||
while (current != parent)
|
||||
{
|
||||
const auto &data = heap.GetData(current);
|
||||
|
||||
if (DIRECTION == FORWARD_DIRECTION)
|
||||
{
|
||||
*out = std::make_tuple(parent, current, data.from_clique_arc);
|
||||
++out;
|
||||
}
|
||||
else if (DIRECTION == REVERSE_DIRECTION)
|
||||
{
|
||||
*out = std::make_tuple(current, parent, data.from_clique_arc);
|
||||
++out;
|
||||
}
|
||||
|
||||
current = parent;
|
||||
parent = heap.GetData(parent).parent;
|
||||
}
|
||||
}
|
||||
|
||||
template <bool DIRECTION>
|
||||
inline PackedPath retrievePackedPathFromSingleManyToManyHeap(
|
||||
const SearchEngineData<Algorithm>::ManyToManyQueryHeap &heap, const NodeID middle)
|
||||
{
|
||||
|
||||
PackedPath packed_path;
|
||||
retrievePackedPathFromSingleManyToManyHeap<DIRECTION>(
|
||||
heap, middle, std::back_inserter(packed_path));
|
||||
|
||||
return packed_path;
|
||||
}
|
||||
|
||||
template <bool DIRECTION, typename OutIter>
|
||||
inline void retrievePackedPathFromSingleHeap(const SearchEngineData<Algorithm>::QueryHeap &heap,
|
||||
const NodeID middle,
|
||||
@@ -300,22 +206,15 @@ void relaxOutgoingEdges(const DataFacade<Algorithm> &facade,
|
||||
for (const auto edge : facade.GetBorderEdgeRange(level, node))
|
||||
{
|
||||
const auto &edge_data = facade.GetEdgeData(edge);
|
||||
|
||||
if ((DIRECTION == FORWARD_DIRECTION) ? facade.IsForwardEdge(edge)
|
||||
: facade.IsBackwardEdge(edge))
|
||||
if (DIRECTION == FORWARD_DIRECTION ? edge_data.forward : edge_data.backward)
|
||||
{
|
||||
const NodeID to = facade.GetTarget(edge);
|
||||
|
||||
if (!facade.ExcludeNode(to) &&
|
||||
checkParentCellRestriction(partition.GetCell(level + 1, to), args...))
|
||||
{
|
||||
const auto node_weight =
|
||||
facade.GetNodeWeight(DIRECTION == FORWARD_DIRECTION ? node : to);
|
||||
const auto turn_penalty = facade.GetWeightPenaltyForEdgeID(edge_data.turn_id);
|
||||
|
||||
// TODO: BOOST_ASSERT(edge_data.weight == node_weight + turn_penalty);
|
||||
|
||||
const EdgeWeight to_weight = weight + node_weight + turn_penalty;
|
||||
BOOST_ASSERT_MSG(edge_data.weight > 0, "edge_weight invalid");
|
||||
const EdgeWeight to_weight = weight + edge_data.weight;
|
||||
|
||||
if (!forward_heap.WasInserted(to))
|
||||
{
|
||||
@@ -390,27 +289,21 @@ UnpackedPath search(SearchEngineData<Algorithm> &engine_working_data,
|
||||
EdgeWeight weight_upper_bound,
|
||||
Args... args)
|
||||
{
|
||||
if (forward_heap.Empty() && reverse_heap.Empty())
|
||||
if (forward_heap.Empty() || reverse_heap.Empty())
|
||||
{
|
||||
return std::make_tuple(INVALID_EDGE_WEIGHT, std::vector<NodeID>(), std::vector<EdgeID>());
|
||||
}
|
||||
|
||||
const auto &partition = facade.GetMultiLevelPartition();
|
||||
|
||||
BOOST_ASSERT(forward_heap.Empty() || forward_heap.MinKey() < INVALID_EDGE_WEIGHT);
|
||||
BOOST_ASSERT(reverse_heap.Empty() || reverse_heap.MinKey() < INVALID_EDGE_WEIGHT);
|
||||
BOOST_ASSERT(!forward_heap.Empty() && forward_heap.MinKey() < INVALID_EDGE_WEIGHT);
|
||||
BOOST_ASSERT(!reverse_heap.Empty() && reverse_heap.MinKey() < INVALID_EDGE_WEIGHT);
|
||||
|
||||
// run two-Target Dijkstra routing step.
|
||||
NodeID middle = SPECIAL_NODEID;
|
||||
EdgeWeight weight = weight_upper_bound;
|
||||
|
||||
EdgeWeight forward_heap_min = 0;
|
||||
if (!forward_heap.Empty())
|
||||
forward_heap_min = forward_heap.MinKey();
|
||||
EdgeWeight reverse_heap_min = 0;
|
||||
if (!reverse_heap.Empty())
|
||||
reverse_heap_min = reverse_heap.MinKey();
|
||||
|
||||
EdgeWeight forward_heap_min = forward_heap.MinKey();
|
||||
EdgeWeight reverse_heap_min = reverse_heap.MinKey();
|
||||
while (forward_heap.Size() + reverse_heap.Size() > 0 &&
|
||||
forward_heap_min + reverse_heap_min < weight)
|
||||
{
|
||||
@@ -514,90 +407,6 @@ UnpackedPath search(SearchEngineData<Algorithm> &engine_working_data,
|
||||
return std::make_tuple(weight, std::move(unpacked_nodes), std::move(unpacked_edges));
|
||||
}
|
||||
|
||||
// With (s, middle, t) we trace back the paths middle -> s and middle -> t.
|
||||
// This gives us a packed path (node ids) from the base graph around s and t,
|
||||
// and overlay node ids otherwise. We then have to unpack the overlay clique
|
||||
// edges by recursively descending unpacking the path down to the base graph.
|
||||
|
||||
using UnpackedNodes = std::vector<NodeID>;
|
||||
using UnpackedEdges = std::vector<EdgeID>;
|
||||
using UnpackedPath = std::tuple<EdgeWeight, UnpackedNodes, UnpackedEdges>;
|
||||
|
||||
template <typename Algorithm, typename... Args>
|
||||
UnpackedPath
|
||||
unpackPathAndCalculateDistance(SearchEngineData<Algorithm> &engine_working_data,
|
||||
const DataFacade<Algorithm> &facade,
|
||||
typename SearchEngineData<Algorithm>::QueryHeap &forward_heap,
|
||||
typename SearchEngineData<Algorithm>::QueryHeap &reverse_heap,
|
||||
const bool force_loop_forward,
|
||||
const bool force_loop_reverse,
|
||||
EdgeWeight weight_upper_bound,
|
||||
PackedPath packed_path,
|
||||
NodeID middle,
|
||||
Args... args)
|
||||
{
|
||||
EdgeWeight weight = weight_upper_bound;
|
||||
const auto &partition = facade.GetMultiLevelPartition();
|
||||
const NodeID source_node = !packed_path.empty() ? std::get<0>(packed_path.front()) : middle;
|
||||
|
||||
// Unpack path
|
||||
std::vector<NodeID> unpacked_nodes;
|
||||
std::vector<EdgeID> unpacked_edges;
|
||||
unpacked_nodes.reserve(packed_path.size());
|
||||
unpacked_edges.reserve(packed_path.size());
|
||||
|
||||
unpacked_nodes.push_back(source_node);
|
||||
|
||||
for (auto const &packed_edge : packed_path)
|
||||
{
|
||||
NodeID source, target;
|
||||
bool overlay_edge;
|
||||
std::tie(source, target, overlay_edge) = packed_edge;
|
||||
if (!overlay_edge)
|
||||
{ // a base graph edge
|
||||
unpacked_nodes.push_back(target);
|
||||
unpacked_edges.push_back(facade.FindEdge(source, target));
|
||||
}
|
||||
else
|
||||
{ // an overlay graph edge
|
||||
LevelID level = getNodeQueryLevel(partition, source, args...);
|
||||
CellID parent_cell_id = partition.GetCell(level, source);
|
||||
BOOST_ASSERT(parent_cell_id == partition.GetCell(level, target));
|
||||
|
||||
LevelID sublevel = level - 1;
|
||||
|
||||
// Here heaps can be reused, let's go deeper!
|
||||
forward_heap.Clear();
|
||||
reverse_heap.Clear();
|
||||
forward_heap.Insert(source, 0, {source});
|
||||
reverse_heap.Insert(target, 0, {target});
|
||||
|
||||
// TODO: when structured bindings will be allowed change to
|
||||
// auto [subpath_weight, subpath_source, subpath_target, subpath] = ...
|
||||
EdgeWeight subpath_weight;
|
||||
std::vector<NodeID> subpath_nodes;
|
||||
std::vector<EdgeID> subpath_edges;
|
||||
std::tie(subpath_weight, subpath_nodes, subpath_edges) = search(engine_working_data,
|
||||
facade,
|
||||
forward_heap,
|
||||
reverse_heap,
|
||||
force_loop_forward,
|
||||
force_loop_reverse,
|
||||
weight_upper_bound,
|
||||
sublevel,
|
||||
parent_cell_id);
|
||||
BOOST_ASSERT(!subpath_edges.empty());
|
||||
BOOST_ASSERT(subpath_nodes.size() > 1);
|
||||
BOOST_ASSERT(subpath_nodes.front() == source);
|
||||
BOOST_ASSERT(subpath_nodes.back() == target);
|
||||
unpacked_nodes.insert(
|
||||
unpacked_nodes.end(), std::next(subpath_nodes.begin()), subpath_nodes.end());
|
||||
unpacked_edges.insert(unpacked_edges.end(), subpath_edges.begin(), subpath_edges.end());
|
||||
}
|
||||
}
|
||||
return std::make_tuple(weight, std::move(unpacked_nodes), std::move(unpacked_edges));
|
||||
}
|
||||
|
||||
// Alias to be compatible with the CH-based search
|
||||
template <typename Algorithm>
|
||||
inline void search(SearchEngineData<Algorithm> &engine_working_data,
|
||||
@@ -662,7 +471,11 @@ double getNetworkDistance(SearchEngineData<Algorithm> &engine_working_data,
|
||||
const PhantomNode &target_phantom,
|
||||
EdgeWeight weight_upper_bound = INVALID_EDGE_WEIGHT)
|
||||
{
|
||||
forward_heap.Clear();
|
||||
reverse_heap.Clear();
|
||||
|
||||
const PhantomNodes phantom_nodes{source_phantom, target_phantom};
|
||||
insertNodesInHeaps(forward_heap, reverse_heap, phantom_nodes);
|
||||
|
||||
EdgeWeight weight = INVALID_EDGE_WEIGHT;
|
||||
std::vector<NodeID> unpacked_nodes;
|
||||
@@ -681,22 +494,11 @@ double getNetworkDistance(SearchEngineData<Algorithm> &engine_working_data,
|
||||
return std::numeric_limits<double>::max();
|
||||
}
|
||||
|
||||
EdgeDistance distance = 0;
|
||||
std::vector<PathData> unpacked_path;
|
||||
|
||||
if (!unpacked_nodes.empty())
|
||||
{
|
||||
distance = std::accumulate(unpacked_nodes.begin(),
|
||||
std::prev(unpacked_nodes.end()),
|
||||
EdgeDistance{0},
|
||||
[&](const EdgeDistance distance, const auto node_id) {
|
||||
return distance + computeEdgeDistance(facade, node_id);
|
||||
});
|
||||
}
|
||||
annotatePath(facade, phantom_nodes, unpacked_nodes, unpacked_edges, unpacked_path);
|
||||
|
||||
distance = adjustPathDistanceToPhantomNodes(
|
||||
unpacked_nodes, phantom_nodes.source_phantom, phantom_nodes.target_phantom, distance);
|
||||
|
||||
return distance;
|
||||
return getPathDistance(facade, unpacked_path, source_phantom, target_phantom);
|
||||
}
|
||||
|
||||
} // namespace mld
|
||||
|
||||
@@ -91,7 +91,6 @@ class EdgeBasedGraphFactory
|
||||
void GetEdgeBasedNodeSegments(std::vector<EdgeBasedNodeSegment> &nodes);
|
||||
void GetStartPointMarkers(std::vector<bool> &node_is_startpoint);
|
||||
void GetEdgeBasedNodeWeights(std::vector<EdgeWeight> &output_node_weights);
|
||||
void GetEdgeBasedNodeDurations(std::vector<EdgeWeight> &output_node_durations);
|
||||
std::uint32_t GetConnectivityChecksum() const;
|
||||
|
||||
std::uint64_t GetNumberOfEdgeBasedNodes() const;
|
||||
@@ -118,7 +117,6 @@ class EdgeBasedGraphFactory
|
||||
//! node weights that indicate the length of the segment (node based) represented by the
|
||||
//! edge-based node
|
||||
std::vector<EdgeWeight> m_edge_based_node_weights;
|
||||
std::vector<EdgeDuration> m_edge_based_node_durations;
|
||||
|
||||
//! list of edge based nodes (compressed segments)
|
||||
std::vector<EdgeBasedNodeSegment> m_edge_based_node_segments;
|
||||
|
||||
@@ -87,7 +87,6 @@ class Extractor
|
||||
std::vector<EdgeBasedNodeSegment> &edge_based_node_segments,
|
||||
std::vector<bool> &node_is_startpoint,
|
||||
std::vector<EdgeWeight> &edge_based_node_weights,
|
||||
std::vector<EdgeDuration> &edge_based_node_durations,
|
||||
util::DeallocatingVector<EdgeBasedEdge> &edge_based_edge_list,
|
||||
std::uint32_t &connectivity_checksum);
|
||||
|
||||
|
||||
@@ -462,28 +462,14 @@ void readEdgeBasedNodeWeights(const boost::filesystem::path &path, NodeWeigtsVec
|
||||
storage::serialization::read(reader, "/extractor/edge_based_node_weights", weights);
|
||||
}
|
||||
|
||||
template <typename NodeWeigtsVectorT, typename NodeDurationsVectorT>
|
||||
void readEdgeBasedNodeWeightsDurations(const boost::filesystem::path &path,
|
||||
NodeWeigtsVectorT &weights,
|
||||
NodeDurationsVectorT &durations)
|
||||
{
|
||||
const auto fingerprint = storage::tar::FileReader::VerifyFingerprint;
|
||||
storage::tar::FileReader reader{path, fingerprint};
|
||||
|
||||
storage::serialization::read(reader, "/extractor/edge_based_node_weights", weights);
|
||||
storage::serialization::read(reader, "/extractor/edge_based_node_durations", durations);
|
||||
}
|
||||
|
||||
template <typename NodeWeigtsVectorT, typename NodeDurationsVectorT>
|
||||
void writeEdgeBasedNodeWeightsDurations(const boost::filesystem::path &path,
|
||||
const NodeWeigtsVectorT &weights,
|
||||
const NodeDurationsVectorT &durations)
|
||||
template <typename NodeWeigtsVectorT>
|
||||
void writeEdgeBasedNodeWeights(const boost::filesystem::path &path,
|
||||
const NodeWeigtsVectorT &weights)
|
||||
{
|
||||
const auto fingerprint = storage::tar::FileWriter::GenerateFingerprint;
|
||||
storage::tar::FileWriter writer{path, fingerprint};
|
||||
|
||||
storage::serialization::write(writer, "/extractor/edge_based_node_weights", weights);
|
||||
storage::serialization::write(writer, "/extractor/edge_based_node_durations", durations);
|
||||
}
|
||||
|
||||
template <typename RTreeT>
|
||||
|
||||
@@ -1064,46 +1064,6 @@ argumentsToTableParameter(const Nan::FunctionCallbackInfo<v8::Value> &args,
|
||||
}
|
||||
}
|
||||
|
||||
if (obj->Has(Nan::New("annotations").ToLocalChecked()))
|
||||
{
|
||||
v8::Local<v8::Value> annotations = obj->Get(Nan::New("annotations").ToLocalChecked());
|
||||
if (annotations.IsEmpty())
|
||||
return table_parameters_ptr();
|
||||
|
||||
if (!annotations->IsArray())
|
||||
{
|
||||
Nan::ThrowError(
|
||||
"Annotations must an array containing 'duration' or 'distance', or both");
|
||||
return table_parameters_ptr();
|
||||
}
|
||||
|
||||
params->annotations = osrm::TableParameters::AnnotationsType::None;
|
||||
|
||||
v8::Local<v8::Array> annotations_array = v8::Local<v8::Array>::Cast(annotations);
|
||||
for (std::size_t i = 0; i < annotations_array->Length(); ++i)
|
||||
{
|
||||
const Nan::Utf8String annotations_utf8str(annotations_array->Get(i));
|
||||
std::string annotations_str{*annotations_utf8str,
|
||||
*annotations_utf8str + annotations_utf8str.length()};
|
||||
|
||||
if (annotations_str == "duration")
|
||||
{
|
||||
params->annotations =
|
||||
params->annotations | osrm::TableParameters::AnnotationsType::Duration;
|
||||
}
|
||||
else if (annotations_str == "distance")
|
||||
{
|
||||
params->annotations =
|
||||
params->annotations | osrm::TableParameters::AnnotationsType::Distance;
|
||||
}
|
||||
else
|
||||
{
|
||||
Nan::ThrowError("this 'annotations' param is not supported");
|
||||
return table_parameters_ptr();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef OSRM_PARTITIONER_SERILIZATION_HPP
|
||||
#define OSRM_PARTITIONER_SERILIZATION_HPP
|
||||
|
||||
#include "customizer/edge_based_graph.hpp"
|
||||
|
||||
#include "partitioner/serialization.hpp"
|
||||
|
||||
#include "storage/io.hpp"
|
||||
@@ -12,6 +14,39 @@ namespace partitioner
|
||||
namespace files
|
||||
{
|
||||
|
||||
// reads .osrm.mldgr file
|
||||
template <typename MultiLevelGraphT>
|
||||
inline void readGraph(const boost::filesystem::path &path,
|
||||
MultiLevelGraphT &graph,
|
||||
std::uint32_t &connectivity_checksum)
|
||||
{
|
||||
static_assert(std::is_same<customizer::MultiLevelEdgeBasedGraphView, MultiLevelGraphT>::value ||
|
||||
std::is_same<customizer::MultiLevelEdgeBasedGraph, MultiLevelGraphT>::value,
|
||||
"");
|
||||
|
||||
storage::tar::FileReader reader{path, storage::tar::FileReader::VerifyFingerprint};
|
||||
|
||||
reader.ReadInto("/mld/connectivity_checksum", connectivity_checksum);
|
||||
serialization::read(reader, "/mld/multilevelgraph", graph);
|
||||
}
|
||||
|
||||
// writes .osrm.mldgr file
|
||||
template <typename MultiLevelGraphT>
|
||||
inline void writeGraph(const boost::filesystem::path &path,
|
||||
const MultiLevelGraphT &graph,
|
||||
const std::uint32_t connectivity_checksum)
|
||||
{
|
||||
static_assert(std::is_same<customizer::MultiLevelEdgeBasedGraphView, MultiLevelGraphT>::value ||
|
||||
std::is_same<customizer::MultiLevelEdgeBasedGraph, MultiLevelGraphT>::value,
|
||||
"");
|
||||
|
||||
storage::tar::FileWriter writer{path, storage::tar::FileWriter::GenerateFingerprint};
|
||||
|
||||
writer.WriteElementCount64("/mld/connectivity_checksum", 1);
|
||||
writer.WriteFrom("/mld/connectivity_checksum", connectivity_checksum);
|
||||
serialization::write(writer, "/mld/multilevelgraph", graph);
|
||||
}
|
||||
|
||||
// read .osrm.partition file
|
||||
template <typename MultiLevelPartitionT>
|
||||
inline void readPartition(const boost::filesystem::path &path, MultiLevelPartitionT &mlp)
|
||||
@@ -67,35 +102,6 @@ inline void writeCells(const boost::filesystem::path &path, CellStorageT &storag
|
||||
|
||||
serialization::write(writer, "/mld/cellstorage", storage);
|
||||
}
|
||||
|
||||
// reads .osrm.mldgr file
|
||||
template <typename MultiLevelGraphT>
|
||||
inline void readGraph(const boost::filesystem::path &path,
|
||||
MultiLevelGraphT &graph,
|
||||
std::uint32_t &connectivity_checksum)
|
||||
{
|
||||
static_assert(std::is_same<partitioner::MultiLevelEdgeBasedGraph, MultiLevelGraphT>::value, "");
|
||||
|
||||
storage::tar::FileReader reader{path, storage::tar::FileReader::VerifyFingerprint};
|
||||
|
||||
reader.ReadInto("/mld/connectivity_checksum", connectivity_checksum);
|
||||
serialization::read(reader, "/mld/multilevelgraph", graph);
|
||||
}
|
||||
|
||||
// writes .osrm.mldgr file
|
||||
template <typename MultiLevelGraphT>
|
||||
inline void writeGraph(const boost::filesystem::path &path,
|
||||
const MultiLevelGraphT &graph,
|
||||
const std::uint32_t connectivity_checksum)
|
||||
{
|
||||
static_assert(std::is_same<partitioner::MultiLevelEdgeBasedGraph, MultiLevelGraphT>::value, "");
|
||||
|
||||
storage::tar::FileWriter writer{path, storage::tar::FileWriter::GenerateFingerprint};
|
||||
|
||||
writer.WriteElementCount64("/mld/connectivity_checksum", 1);
|
||||
writer.WriteFrom("/mld/connectivity_checksum", connectivity_checksum);
|
||||
serialization::write(writer, "/mld/multilevelgraph", graph);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#ifndef OSRM_PARTITIONER_MULTI_LEVEL_GRAPH_HPP
|
||||
#define OSRM_PARTITIONER_MULTI_LEVEL_GRAPH_HPP
|
||||
|
||||
#include "partitioner/edge_based_graph.hpp"
|
||||
#include "partitioner/multi_level_partition.hpp"
|
||||
|
||||
#include "storage/shared_memory_ownership.hpp"
|
||||
@@ -43,8 +42,6 @@ class MultiLevelGraph : public util::StaticGraph<EdgeDataT, Ownership>
|
||||
template <typename T> using Vector = util::ViewOrVector<T, Ownership>;
|
||||
|
||||
public:
|
||||
using SuperT::SuperT;
|
||||
|
||||
// We limit each node to have 255 edges
|
||||
// this is very generous, we could probably pack this
|
||||
using EdgeOffset = std::uint8_t;
|
||||
@@ -149,14 +146,6 @@ class MultiLevelGraph : public util::StaticGraph<EdgeDataT, Ownership>
|
||||
return max_border_node_id;
|
||||
}
|
||||
|
||||
auto data() && // rvalue ref-qualifier is a safety-belt
|
||||
{
|
||||
return std::make_tuple(std::move(SuperT::node_array),
|
||||
std::move(SuperT::edge_array),
|
||||
std::move(node_to_edge_offset),
|
||||
connectivity_checksum);
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename ContainerT>
|
||||
auto GetHighestBorderLevel(const MultiLevelPartition &mlp, const ContainerT &edges) const
|
||||
@@ -229,13 +218,9 @@ class MultiLevelGraph : public util::StaticGraph<EdgeDataT, Ownership>
|
||||
const std::string &name,
|
||||
const MultiLevelGraph<EdgeDataT, Ownership> &graph);
|
||||
|
||||
protected:
|
||||
Vector<EdgeOffset> node_to_edge_offset;
|
||||
std::uint32_t connectivity_checksum;
|
||||
};
|
||||
|
||||
using MultiLevelEdgeBasedGraph =
|
||||
MultiLevelGraph<EdgeBasedGraphEdgeData, storage::Ownership::Container>;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace partitioner
|
||||
struct PartitionerConfig final : storage::IOConfig
|
||||
{
|
||||
PartitionerConfig()
|
||||
: IOConfig({".osrm", ".osrm.fileIndex", ".osrm.ebg_nodes", ".osrm.enw"},
|
||||
: IOConfig({".osrm", ".osrm.fileIndex", ".osrm.ebg_nodes"},
|
||||
{".osrm.hsgr", ".osrm.cnbg"},
|
||||
{".osrm.ebg",
|
||||
".osrm.cnbg",
|
||||
|
||||
@@ -19,6 +19,26 @@ namespace partitioner
|
||||
namespace serialization
|
||||
{
|
||||
|
||||
template <typename EdgeDataT, storage::Ownership Ownership>
|
||||
inline void read(storage::tar::FileReader &reader,
|
||||
const std::string &name,
|
||||
MultiLevelGraph<EdgeDataT, Ownership> &graph)
|
||||
{
|
||||
storage::serialization::read(reader, name + "/node_array", graph.node_array);
|
||||
storage::serialization::read(reader, name + "/edge_array", graph.edge_array);
|
||||
storage::serialization::read(reader, name + "/node_to_edge_offset", graph.node_to_edge_offset);
|
||||
}
|
||||
|
||||
template <typename EdgeDataT, storage::Ownership Ownership>
|
||||
inline void write(storage::tar::FileWriter &writer,
|
||||
const std::string &name,
|
||||
const MultiLevelGraph<EdgeDataT, Ownership> &graph)
|
||||
{
|
||||
storage::serialization::write(writer, name + "/node_array", graph.node_array);
|
||||
storage::serialization::write(writer, name + "/edge_array", graph.edge_array);
|
||||
storage::serialization::write(writer, name + "/node_to_edge_offset", graph.node_to_edge_offset);
|
||||
}
|
||||
|
||||
template <storage::Ownership Ownership>
|
||||
inline void read(storage::tar::FileReader &reader,
|
||||
const std::string &name,
|
||||
|
||||
@@ -22,11 +22,11 @@ namespace qi = boost::spirit::qi;
|
||||
|
||||
template <typename Iterator = std::string::iterator,
|
||||
typename Signature = void(engine::api::TableParameters &)>
|
||||
struct TableParametersGrammar : public BaseParametersGrammar<Iterator, Signature>
|
||||
struct TableParametersGrammar final : public BaseParametersGrammar<Iterator, Signature>
|
||||
{
|
||||
using BaseGrammar = BaseParametersGrammar<Iterator, Signature>;
|
||||
|
||||
TableParametersGrammar() : TableParametersGrammar(root_rule)
|
||||
TableParametersGrammar() : BaseGrammar(root_rule)
|
||||
{
|
||||
#ifdef BOOST_HAS_LONG_LONG
|
||||
if (std::is_same<std::size_t, unsigned long long>::value)
|
||||
@@ -51,35 +51,15 @@ struct TableParametersGrammar : public BaseParametersGrammar<Iterator, Signature
|
||||
table_rule = destinations_rule(qi::_r1) | sources_rule(qi::_r1);
|
||||
|
||||
root_rule = BaseGrammar::query_rule(qi::_r1) > -qi::lit(".json") >
|
||||
-('?' > (table_rule(qi::_r1) | base_rule(qi::_r1)) % '&');
|
||||
-('?' > (table_rule(qi::_r1) | BaseGrammar::base_rule(qi::_r1)) % '&');
|
||||
}
|
||||
|
||||
TableParametersGrammar(qi::rule<Iterator, Signature> &root_rule_) : BaseGrammar(root_rule_)
|
||||
{
|
||||
using AnnotationsType = engine::api::TableParameters::AnnotationsType;
|
||||
|
||||
annotations.add("duration", AnnotationsType::Duration)("distance",
|
||||
AnnotationsType::Distance);
|
||||
|
||||
annotations_list = annotations[qi::_val |= qi::_1] % ',';
|
||||
|
||||
base_rule = BaseGrammar::base_rule(qi::_r1) |
|
||||
(qi::lit("annotations=") >
|
||||
annotations_list[ph::bind(&engine::api::TableParameters::annotations,
|
||||
qi::_r1) = qi::_1]);
|
||||
}
|
||||
|
||||
protected:
|
||||
qi::rule<Iterator, Signature> base_rule;
|
||||
|
||||
private:
|
||||
qi::rule<Iterator, Signature> root_rule;
|
||||
qi::rule<Iterator, Signature> table_rule;
|
||||
qi::rule<Iterator, Signature> sources_rule;
|
||||
qi::rule<Iterator, Signature> destinations_rule;
|
||||
qi::rule<Iterator, std::size_t()> size_t_;
|
||||
qi::symbols<char, engine::api::TableParameters::AnnotationsType> annotations;
|
||||
qi::rule<Iterator, engine::api::TableParameters::AnnotationsType()> annotations_list;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,39 +46,33 @@ class SharedDataIndex
|
||||
|
||||
template <typename T> auto GetBlockPtr(const std::string &name) const
|
||||
{
|
||||
const auto ®ion = GetBlockRegion(name);
|
||||
const auto index_iter = block_to_region.find(name);
|
||||
const auto ®ion = regions[index_iter->second];
|
||||
return region.layout.GetBlockPtr<T>(region.memory_ptr, name);
|
||||
}
|
||||
|
||||
template <typename T> auto GetBlockPtr(const std::string &name)
|
||||
{
|
||||
const auto ®ion = GetBlockRegion(name);
|
||||
const auto index_iter = block_to_region.find(name);
|
||||
const auto ®ion = regions[index_iter->second];
|
||||
return region.layout.GetBlockPtr<T>(region.memory_ptr, name);
|
||||
}
|
||||
|
||||
std::size_t GetBlockEntries(const std::string &name) const
|
||||
{
|
||||
const auto ®ion = GetBlockRegion(name);
|
||||
const auto index_iter = block_to_region.find(name);
|
||||
const auto ®ion = regions[index_iter->second];
|
||||
return region.layout.GetBlockEntries(name);
|
||||
}
|
||||
|
||||
std::size_t GetBlockSize(const std::string &name) const
|
||||
{
|
||||
const auto ®ion = GetBlockRegion(name);
|
||||
const auto index_iter = block_to_region.find(name);
|
||||
const auto ®ion = regions[index_iter->second];
|
||||
return region.layout.GetBlockSize(name);
|
||||
}
|
||||
|
||||
private:
|
||||
const AllocatedRegion &GetBlockRegion(const std::string &name) const
|
||||
{
|
||||
const auto index_iter = block_to_region.find(name);
|
||||
if (index_iter == block_to_region.end())
|
||||
{
|
||||
throw util::exception("data block " + name + " not found " + SOURCE_REF);
|
||||
}
|
||||
return regions[index_iter->second];
|
||||
}
|
||||
|
||||
std::vector<AllocatedRegion> regions;
|
||||
std::unordered_map<std::string, std::uint32_t> block_to_region;
|
||||
};
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace serialization
|
||||
inline void read(io::BufferReader &reader, DataLayout &layout);
|
||||
|
||||
inline void write(io::BufferWriter &writer, const DataLayout &layout);
|
||||
} // namespace serialization
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
@@ -52,7 +52,7 @@ inline std::string trimName(const std::string &name_prefix, const std::string &n
|
||||
return name;
|
||||
}
|
||||
}
|
||||
} // namespace detail
|
||||
}
|
||||
|
||||
class DataLayout
|
||||
{
|
||||
@@ -165,7 +165,7 @@ struct SharedRegion
|
||||
static constexpr const int MAX_NAME_LENGTH = 254;
|
||||
|
||||
SharedRegion() : name{0}, timestamp{0} {}
|
||||
SharedRegion(const std::string &name_, std::uint64_t timestamp, std::uint16_t shm_key)
|
||||
SharedRegion(const std::string &name_, std::uint64_t timestamp, std::uint8_t shm_key)
|
||||
: name{0}, timestamp{timestamp}, shm_key{shm_key}
|
||||
{
|
||||
std::copy_n(name_.begin(), std::min<std::size_t>(MAX_NAME_LENGTH, name_.size()), name);
|
||||
@@ -175,14 +175,14 @@ struct SharedRegion
|
||||
|
||||
char name[MAX_NAME_LENGTH + 1];
|
||||
std::uint64_t timestamp;
|
||||
std::uint16_t shm_key;
|
||||
std::uint8_t shm_key;
|
||||
};
|
||||
|
||||
// Keeps a list of all shared regions in a fixed-sized struct
|
||||
// for fast access and deserialization.
|
||||
struct SharedRegionRegister
|
||||
{
|
||||
using RegionID = std::uint16_t;
|
||||
using RegionID = std::uint8_t;
|
||||
static constexpr const RegionID INVALID_REGION_ID = std::numeric_limits<RegionID>::max();
|
||||
using ShmKey = decltype(SharedRegion::shm_key);
|
||||
|
||||
@@ -250,11 +250,12 @@ struct SharedRegionRegister
|
||||
|
||||
void ReleaseKey(ShmKey key) { shm_key_in_use[key] = false; }
|
||||
|
||||
static constexpr const std::size_t MAX_SHARED_REGIONS = 512;
|
||||
static constexpr const std::uint8_t MAX_SHARED_REGIONS =
|
||||
std::numeric_limits<RegionID>::max() - 1;
|
||||
static_assert(MAX_SHARED_REGIONS < std::numeric_limits<RegionID>::max(),
|
||||
"Number of shared memory regions needs to be less than the region id size.");
|
||||
|
||||
static constexpr const std::size_t MAX_SHM_KEYS = MAX_SHARED_REGIONS * 2;
|
||||
static constexpr const std::uint8_t MAX_SHM_KEYS = std::numeric_limits<std::uint8_t>::max() - 1;
|
||||
|
||||
static constexpr const char *name = "osrm-region";
|
||||
|
||||
@@ -262,7 +263,7 @@ struct SharedRegionRegister
|
||||
std::array<SharedRegion, MAX_SHARED_REGIONS> regions;
|
||||
std::array<bool, MAX_SHM_KEYS> shm_key_in_use;
|
||||
};
|
||||
} // namespace storage
|
||||
} // namespace osrm
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* SHARED_DATA_TYPE_HPP */
|
||||
|
||||
@@ -34,10 +34,10 @@ namespace storage
|
||||
|
||||
struct OSRMLockFile
|
||||
{
|
||||
template <typename IdentifierT> boost::filesystem::path operator()(const IdentifierT &id)
|
||||
boost::filesystem::path operator()()
|
||||
{
|
||||
boost::filesystem::path temp_dir = boost::filesystem::temp_directory_path();
|
||||
boost::filesystem::path lock_file = temp_dir / ("osrm-" + std::to_string(id) + ".lock");
|
||||
boost::filesystem::path lock_file = temp_dir / "osrm.lock";
|
||||
return lock_file;
|
||||
}
|
||||
};
|
||||
@@ -93,7 +93,7 @@ class SharedMemory
|
||||
try
|
||||
{
|
||||
OSRMLockFile lock_file;
|
||||
boost::interprocess::xsi_key key(lock_file(id).string().c_str(), id);
|
||||
boost::interprocess::xsi_key key(lock_file().string().c_str(), id);
|
||||
result = RegionExists(key);
|
||||
}
|
||||
catch (...)
|
||||
@@ -106,7 +106,7 @@ class SharedMemory
|
||||
template <typename IdentifierT> static bool Remove(const IdentifierT id)
|
||||
{
|
||||
OSRMLockFile lock_file;
|
||||
boost::interprocess::xsi_key key(lock_file(id).string().c_str(), id);
|
||||
boost::interprocess::xsi_key key(lock_file().string().c_str(), id);
|
||||
return Remove(key);
|
||||
}
|
||||
|
||||
@@ -287,11 +287,10 @@ class SharedMemory
|
||||
template <typename IdentifierT, typename LockFileT = OSRMLockFile>
|
||||
std::unique_ptr<SharedMemory> makeSharedMemory(const IdentifierT &id, const uint64_t size = 0)
|
||||
{
|
||||
static_assert(sizeof(id) == sizeof(std::uint16_t), "Key type is not 16 bits");
|
||||
try
|
||||
{
|
||||
LockFileT lock_file;
|
||||
if (!boost::filesystem::exists(lock_file(id)))
|
||||
if (!boost::filesystem::exists(lock_file()))
|
||||
{
|
||||
if (0 == size)
|
||||
{
|
||||
@@ -299,10 +298,10 @@ std::unique_ptr<SharedMemory> makeSharedMemory(const IdentifierT &id, const uint
|
||||
}
|
||||
else
|
||||
{
|
||||
boost::filesystem::ofstream ofs(lock_file(id));
|
||||
boost::filesystem::ofstream ofs(lock_file());
|
||||
}
|
||||
}
|
||||
return std::make_unique<SharedMemory>(lock_file(id), id, size);
|
||||
return std::make_unique<SharedMemory>(lock_file(), id, size);
|
||||
}
|
||||
catch (const boost::interprocess::interprocess_exception &e)
|
||||
{
|
||||
@@ -311,7 +310,7 @@ std::unique_ptr<SharedMemory> makeSharedMemory(const IdentifierT &id, const uint
|
||||
throw util::exception(e.what() + SOURCE_REF);
|
||||
}
|
||||
}
|
||||
} // namespace storage
|
||||
} // namespace osrm
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SHARED_MEMORY_HPP
|
||||
|
||||
@@ -33,7 +33,7 @@ template <class Lock> class InvertedLock
|
||||
InvertedLock(Lock &lock) : lock(lock) { lock.unlock(); }
|
||||
~InvertedLock() { lock.lock(); }
|
||||
};
|
||||
} // namespace
|
||||
}
|
||||
|
||||
// The shared monitor implementation based on a semaphore and mutex
|
||||
template <typename Data> struct SharedMonitor
|
||||
@@ -146,7 +146,7 @@ template <typename Data> struct SharedMonitor
|
||||
// like two-turnstile reusable barrier or boost/interprocess/sync/spin/condition.hpp
|
||||
// fail if a waiter is killed.
|
||||
|
||||
static constexpr int buffer_size = 4096;
|
||||
static constexpr int buffer_size = 256;
|
||||
|
||||
struct InternalData
|
||||
{
|
||||
@@ -232,8 +232,8 @@ template <typename Data> struct SharedMonitor
|
||||
bi::shared_memory_object shmem;
|
||||
bi::mapped_region region;
|
||||
};
|
||||
} // namespace storage
|
||||
} // namespace osrm
|
||||
}
|
||||
}
|
||||
|
||||
#undef USE_BOOST_INTERPROCESS_CONDITION
|
||||
|
||||
|
||||
@@ -330,18 +330,9 @@ inline auto make_multi_level_graph_view(const SharedDataIndex &index, const std:
|
||||
index, name + "/edge_array");
|
||||
auto node_to_offset = make_vector_view<customizer::MultiLevelEdgeBasedGraphView::EdgeOffset>(
|
||||
index, name + "/node_to_edge_offset");
|
||||
auto node_weights = make_vector_view<EdgeWeight>(index, name + "/node_weights");
|
||||
auto node_durations = make_vector_view<EdgeDuration>(index, name + "/node_durations");
|
||||
auto is_forward_edge = make_vector_view<bool>(index, name + "/is_forward_edge");
|
||||
auto is_backward_edge = make_vector_view<bool>(index, name + "/is_backward_edge");
|
||||
|
||||
return customizer::MultiLevelEdgeBasedGraphView(std::move(node_list),
|
||||
std::move(edge_list),
|
||||
std::move(node_to_offset),
|
||||
std::move(node_weights),
|
||||
std::move(node_durations),
|
||||
std::move(is_forward_edge),
|
||||
std::move(is_backward_edge));
|
||||
return customizer::MultiLevelEdgeBasedGraphView(
|
||||
std::move(node_list), std::move(edge_list), std::move(node_to_offset));
|
||||
}
|
||||
|
||||
inline auto make_maneuver_overrides_views(const SharedDataIndex &index, const std::string &name)
|
||||
|
||||
@@ -17,15 +17,13 @@ class Updater
|
||||
public:
|
||||
Updater(UpdaterConfig config_) : config(std::move(config_)) {}
|
||||
|
||||
EdgeID
|
||||
LoadAndUpdateEdgeExpandedGraph(std::vector<extractor::EdgeBasedEdge> &edge_based_edge_list,
|
||||
std::vector<EdgeWeight> &node_weights,
|
||||
std::uint32_t &connectivity_checksum) const;
|
||||
using NumNodesAndEdges =
|
||||
std::tuple<EdgeID, std::vector<extractor::EdgeBasedEdge>, std::uint32_t>;
|
||||
NumNodesAndEdges LoadAndUpdateEdgeExpandedGraph() const;
|
||||
|
||||
EdgeID
|
||||
LoadAndUpdateEdgeExpandedGraph(std::vector<extractor::EdgeBasedEdge> &edge_based_edge_list,
|
||||
std::vector<EdgeWeight> &node_weights,
|
||||
std::vector<EdgeDuration> &node_durations, // TODO: to be deleted
|
||||
std::uint32_t &connectivity_checksum) const;
|
||||
|
||||
private:
|
||||
|
||||
@@ -53,8 +53,7 @@ struct UpdaterConfig final : storage::IOConfig
|
||||
".osrm.geometry",
|
||||
".osrm.fileIndex",
|
||||
".osrm.properties",
|
||||
".osrm.restrictions",
|
||||
".osrm.enw"},
|
||||
".osrm.restrictions"},
|
||||
{},
|
||||
{".osrm.datasource_names"}),
|
||||
valid_now(0)
|
||||
|
||||
@@ -23,6 +23,9 @@ namespace detail
|
||||
{
|
||||
const constexpr double DEGREE_TO_RAD = 0.017453292519943295769236907684886;
|
||||
const constexpr double RAD_TO_DEGREE = 1. / DEGREE_TO_RAD;
|
||||
// earth radius varies between 6,356.750-6,378.135 km (3,949.901-3,963.189mi)
|
||||
// The IUGG value for the equatorial radius is 6378.137 km (3963.19 miles)
|
||||
const constexpr long double EARTH_RADIUS = 6372797.560856;
|
||||
|
||||
inline double degToRad(const double degree)
|
||||
{
|
||||
@@ -40,9 +43,6 @@ inline double radToDeg(const double radian)
|
||||
//! Takes the squared euclidean distance of the input coordinates. Does not return meters!
|
||||
std::uint64_t squaredEuclideanDistance(const Coordinate lhs, const Coordinate rhs);
|
||||
|
||||
double fccApproximateDistance(const Coordinate first_coordinate,
|
||||
const Coordinate second_coordinate);
|
||||
|
||||
double haversineDistance(const Coordinate first_coordinate, const Coordinate second_coordinate);
|
||||
|
||||
double greatCircleDistance(const Coordinate first_coordinate, const Coordinate second_coordinate);
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace util
|
||||
inline std::ostream &operator<<(std::ostream &out, const Coordinate &coordinate)
|
||||
{
|
||||
out << std::setprecision(12) << "{" << toFloating(coordinate.lon) << ", "
|
||||
<< toFloating(coordinate.lat) << "}";
|
||||
<< toFloating(coordinate.lon) << "}";
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -413,11 +413,6 @@ template <typename EdgeDataT> class DynamicGraph
|
||||
util::inplacePermutation(node_array.begin(), node_array.end(), old_to_new_node);
|
||||
|
||||
// Build up edge permutation
|
||||
if (edge_list.size() >= std::numeric_limits<EdgeID>::max())
|
||||
{
|
||||
throw util::exception("There are too many edges, OSRM only supports 2^32" + SOURCE_REF);
|
||||
}
|
||||
|
||||
EdgeID new_edge_index = 0;
|
||||
std::vector<EdgeID> old_to_new_edge(edge_list.size(), SPECIAL_EDGEID);
|
||||
for (auto node : util::irange<NodeID>(0, number_of_nodes))
|
||||
@@ -426,6 +421,11 @@ template <typename EdgeDataT> class DynamicGraph
|
||||
// move all filled edges
|
||||
for (auto edge : GetAdjacentEdgeRange(node))
|
||||
{
|
||||
if (new_edge_index == std::numeric_limits<EdgeID>::max())
|
||||
{
|
||||
throw util::exception("There are too many edges, OSRM only supports 2^32" +
|
||||
SOURCE_REF);
|
||||
}
|
||||
edge_list[edge].target = old_to_new_node[edge_list[edge].target];
|
||||
BOOST_ASSERT(edge_list[edge].target != SPECIAL_NODEID);
|
||||
old_to_new_edge[edge] = new_edge_index++;
|
||||
|
||||
@@ -226,14 +226,12 @@ class QueryHeap
|
||||
Data &GetData(NodeID node)
|
||||
{
|
||||
const auto index = node_index.peek_index(node);
|
||||
BOOST_ASSERT((int)index >= 0 && (int)index < (int)inserted_nodes.size());
|
||||
return inserted_nodes[index].data;
|
||||
}
|
||||
|
||||
Data const &GetData(NodeID node) const
|
||||
{
|
||||
const auto index = node_index.peek_index(node);
|
||||
BOOST_ASSERT((int)index >= 0 && (int)index < (int)inserted_nodes.size());
|
||||
return inserted_nodes[index].data;
|
||||
}
|
||||
|
||||
|
||||
@@ -312,7 +312,7 @@ class StaticGraph
|
||||
});
|
||||
}
|
||||
|
||||
protected:
|
||||
// private:
|
||||
NodeIterator number_of_nodes;
|
||||
EdgeIterator number_of_edges;
|
||||
|
||||
|
||||
@@ -75,7 +75,6 @@ using NameID = std::uint32_t;
|
||||
using AnnotationID = std::uint32_t;
|
||||
using EdgeWeight = std::int32_t;
|
||||
using EdgeDuration = std::int32_t;
|
||||
using EdgeDistance = float;
|
||||
using SegmentWeight = std::uint32_t;
|
||||
using SegmentDuration = std::uint32_t;
|
||||
using TurnPenalty = std::int16_t; // turn penalty in 100ms units
|
||||
@@ -114,7 +113,6 @@ static const SegmentDuration MAX_SEGMENT_DURATION = INVALID_SEGMENT_DURATION - 1
|
||||
static const EdgeWeight INVALID_EDGE_WEIGHT = std::numeric_limits<EdgeWeight>::max();
|
||||
static const EdgeDuration MAXIMAL_EDGE_DURATION = std::numeric_limits<EdgeDuration>::max();
|
||||
static const TurnPenalty INVALID_TURN_PENALTY = std::numeric_limits<TurnPenalty>::max();
|
||||
static const EdgeDistance INVALID_EDGE_DISTANCE = std::numeric_limits<EdgeDistance>::max();
|
||||
|
||||
// FIXME the bitfields we use require a reduced maximal duration, this should be kept consistent
|
||||
// within the code base. For now we have to ensure that we don't case 30 bit to -1 and break any
|
||||
|
||||
@@ -9,6 +9,31 @@ namespace util
|
||||
{
|
||||
namespace vector_tile
|
||||
{
|
||||
|
||||
const constexpr std::uint32_t ID_TAG = 1;
|
||||
const constexpr std::uint32_t NAME_TAG = 1;
|
||||
const constexpr std::uint32_t FEATURE_TAG = 2;
|
||||
const constexpr std::uint32_t LAYER_TAG = 3;
|
||||
const constexpr std::uint32_t GEOMETRY_TAG = 3;
|
||||
const constexpr std::uint32_t KEY_TAG = 3;
|
||||
const constexpr std::uint32_t VARIANT_TAG = 4;
|
||||
const constexpr std::uint32_t EXTENT_TAG = 5;
|
||||
const constexpr std::uint32_t VERSION_TAG = 15;
|
||||
|
||||
const constexpr std::uint32_t FEATURE_ATTRIBUTES_TAG = 2;
|
||||
const constexpr std::uint32_t FEATURE_GEOMETRIES_TAG = 4;
|
||||
|
||||
const constexpr std::uint32_t GEOMETRY_TYPE_POINT = 1;
|
||||
const constexpr std::uint32_t GEOMETRY_TYPE_LINE = 2;
|
||||
|
||||
const constexpr std::uint32_t VARIANT_TYPE_STRING = 1;
|
||||
const constexpr std::uint32_t VARIANT_TYPE_FLOAT = 2;
|
||||
const constexpr std::uint32_t VARIANT_TYPE_DOUBLE = 3;
|
||||
|
||||
const constexpr std::uint32_t VARIANT_TYPE_UINT64 = 5;
|
||||
const constexpr std::uint32_t VARIANT_TYPE_SINT64 = 6;
|
||||
const constexpr std::uint32_t VARIANT_TYPE_BOOL = 7;
|
||||
|
||||
// Vector tiles are 4096 virtual pixels on each side
|
||||
const constexpr double EXTENT = 4096.0;
|
||||
const constexpr double BUFFER = 128.0;
|
||||
|
||||
+1
-7
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "osrm",
|
||||
"version": "5.18.0-latest.1",
|
||||
"version": "5.17.3",
|
||||
"private": false,
|
||||
"description": "The Open Source Routing Machine is a high performance routing engine written in C++14 designed to run on OpenStreetMap data.",
|
||||
"dependencies": {
|
||||
@@ -38,25 +38,19 @@
|
||||
"node": ">=4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"ansi-escape-sequences": "^4.0.0",
|
||||
"aws-sdk": "~2.0.31",
|
||||
"babel-plugin-transform-class-properties": "^6.24.1",
|
||||
"chalk": "^1.1.3",
|
||||
"command-line-args": "^5.0.2",
|
||||
"command-line-usage": "^5.0.4",
|
||||
"csv-stringify": "^3.0.0",
|
||||
"cucumber": "^1.2.1",
|
||||
"d3-queue": "^2.0.3",
|
||||
"docbox": "^1.0.6",
|
||||
"documentation": "^4.0.0-rc.1",
|
||||
"eslint": "^2.4.0",
|
||||
"faucet": "^0.0.1",
|
||||
"jsonpath": "^1.0.0",
|
||||
"node-timeout": "0.0.4",
|
||||
"polyline": "^0.2.0",
|
||||
"request": "^2.69.0",
|
||||
"tape": "^4.7.0",
|
||||
"turf": "^3.0.14",
|
||||
"xmlbuilder": "^4.2.1"
|
||||
},
|
||||
"bundleDependencies": [
|
||||
|
||||
+15
-18
@@ -38,10 +38,20 @@ function setup()
|
||||
mode.pushing_bike
|
||||
},
|
||||
|
||||
barrier_blacklist = Set {
|
||||
'yes',
|
||||
'wall',
|
||||
'fence'
|
||||
barrier_whitelist = Set {
|
||||
'sump_buster',
|
||||
'bus_trap',
|
||||
'cycle_barrier',
|
||||
'bollard',
|
||||
'entrance',
|
||||
'cattle_grid',
|
||||
'border_control',
|
||||
'toll_booth',
|
||||
'sally_port',
|
||||
'gate',
|
||||
'lift_gate',
|
||||
'no',
|
||||
'block'
|
||||
},
|
||||
|
||||
access_tag_whitelist = Set {
|
||||
@@ -183,16 +193,6 @@ function setup()
|
||||
sett = 10
|
||||
},
|
||||
|
||||
classes = Sequence {
|
||||
'ferry', 'tunnel'
|
||||
},
|
||||
|
||||
-- Which classes should be excludable
|
||||
-- This increases memory usage so its disabled by default.
|
||||
excludable = Sequence {
|
||||
-- Set {'ferry'}
|
||||
},
|
||||
|
||||
tracktype_speeds = {
|
||||
},
|
||||
|
||||
@@ -235,7 +235,7 @@ function process_node(profile, node, result)
|
||||
else
|
||||
local barrier = node:get_value_by_key("barrier")
|
||||
if barrier and "" ~= barrier then
|
||||
if profile.barrier_blacklist[barrier] then
|
||||
if not profile.barrier_whitelist[barrier] then
|
||||
result.barrier = true
|
||||
end
|
||||
end
|
||||
@@ -650,9 +650,6 @@ function process_way(profile, way, result)
|
||||
-- set name, ref and pronunciation
|
||||
WayHandlers.names,
|
||||
|
||||
-- set classes
|
||||
WayHandlers.classes,
|
||||
|
||||
-- set weight properties of the way
|
||||
WayHandlers.weights
|
||||
}
|
||||
|
||||
@@ -42,10 +42,6 @@ function setup()
|
||||
vehicle_height = 2.5, -- in meters, 2.5m is the height of van
|
||||
vehicle_width = 1.9, -- in meters, ways with narrow tag are considered narrower than 2.2m
|
||||
|
||||
-- Size of the vehicle, to be limited mostly by legal restriction of the way
|
||||
vehicle_length = 4.8, -- in meters, 4.8m is the length of large or familly car
|
||||
vehicle_weight = 3500, -- in kilograms
|
||||
|
||||
-- a list of suffixes to suppress in name change instructions. The suffixes also include common substrings of each other
|
||||
suffix_list = {
|
||||
'N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW', 'North', 'South', 'West', 'East', 'Nor', 'Sou', 'We', 'Ea'
|
||||
@@ -280,7 +276,6 @@ function setup()
|
||||
["de:rural"] = 100,
|
||||
["de:motorway"] = 0,
|
||||
["dk:rural"] = 80,
|
||||
["fr:rural"] = 80,
|
||||
["gb:nsl_single"] = (60*1609)/1000,
|
||||
["gb:nsl_dual"] = (70*1609)/1000,
|
||||
["gb:motorway"] = (70*1609)/1000,
|
||||
@@ -392,8 +387,6 @@ function process_way(profile, way, result, relations)
|
||||
WayHandlers.avoid_ways,
|
||||
WayHandlers.handle_height,
|
||||
WayHandlers.handle_width,
|
||||
WayHandlers.handle_length,
|
||||
WayHandlers.handle_weight,
|
||||
|
||||
-- determine access status by checking our hierarchy of
|
||||
-- access tags, e.g: motorcar, motor_vehicle, vehicle
|
||||
|
||||
+14
-5
@@ -24,10 +24,19 @@ function setup()
|
||||
default_speed = walking_speed,
|
||||
oneway_handling = 'specific', -- respect 'oneway:foot' but not 'oneway'
|
||||
|
||||
barrier_blacklist = Set {
|
||||
'yes',
|
||||
'wall',
|
||||
'fence'
|
||||
barrier_whitelist = Set {
|
||||
'cycle_barrier',
|
||||
'bollard',
|
||||
'entrance',
|
||||
'cattle_grid',
|
||||
'border_control',
|
||||
'toll_booth',
|
||||
'sally_port',
|
||||
'gate',
|
||||
'lift_gate',
|
||||
'no',
|
||||
'kerb',
|
||||
'block'
|
||||
},
|
||||
|
||||
access_tag_whitelist = Set {
|
||||
@@ -148,7 +157,7 @@ function process_node(profile, node, result)
|
||||
local bollard = node:get_value_by_key("bollard")
|
||||
local rising_bollard = bollard and "rising" == bollard
|
||||
|
||||
if profile.barrier_blacklist[barrier] and not rising_bollard then
|
||||
if not profile.barrier_whitelist[barrier] and not rising_bollard then
|
||||
result.barrier = true
|
||||
end
|
||||
end
|
||||
|
||||
+26
-20
@@ -5,7 +5,6 @@ Measure = {}
|
||||
-- measurements conversion constants
|
||||
local inch_to_meters = 0.0254
|
||||
local feet_to_inches = 12
|
||||
local pound_to_kilograms = 0.45359237
|
||||
|
||||
--- Parse string as a height in meters.
|
||||
--- according to http://wiki.openstreetmap.org/wiki/Key:maxheight
|
||||
@@ -26,19 +25,33 @@ function Measure.parse_value_meters(value)
|
||||
end
|
||||
end
|
||||
|
||||
--- Parse weight value in kilograms.
|
||||
--- according to https://wiki.openstreetmap.org/wiki/Key:maxweight
|
||||
--- according to http://wiki.openstreetmap.org/wiki/Map_Features/Units#Explicit_specifications
|
||||
local tonns_parse_patterns = Sequence {
|
||||
"%d+",
|
||||
"%d+.%d+",
|
||||
"%d+.%d+ ?t"
|
||||
}
|
||||
|
||||
local kg_parse_patterns = Sequence {
|
||||
"%d+ ?kg"
|
||||
}
|
||||
|
||||
--- Parse weight value in kilograms
|
||||
function Measure.parse_value_kilograms(value)
|
||||
local n = tonumber(value:gsub(",", "."):match("%d+%.?%d*"))
|
||||
if n then
|
||||
if string.match(value, "lbs") then
|
||||
n = n * pound_to_kilograms
|
||||
elseif string.match(value, "kg") then
|
||||
-- n = n
|
||||
else -- Default, metric tons
|
||||
n = n * 1000
|
||||
-- try to parse kilograms
|
||||
for i, templ in ipairs(kg_parse_patterns) do
|
||||
m = string.match(value, templ)
|
||||
if m then
|
||||
return tonumber(m)
|
||||
end
|
||||
end
|
||||
|
||||
-- try to parse tonns
|
||||
for i, templ in ipairs(tonns_parse_patterns) do
|
||||
m = string.match(value, templ)
|
||||
if m then
|
||||
return tonumber(m) * 1000
|
||||
end
|
||||
return n
|
||||
end
|
||||
end
|
||||
|
||||
@@ -70,14 +83,7 @@ function Measure.get_max_width(raw_value)
|
||||
end
|
||||
end
|
||||
|
||||
--- Get maxlength of specified way in meters.
|
||||
function Measure.get_max_length(raw_value)
|
||||
if raw_value then
|
||||
return Measure.parse_value_meters(raw_value)
|
||||
end
|
||||
end
|
||||
|
||||
--- Get maxweight of specified way in kilogramms.
|
||||
--- Get maxweight of specified way in kilogramms
|
||||
function Measure.get_max_weight(raw_value)
|
||||
if raw_value then
|
||||
return Measure.parse_value_kilograms(raw_value)
|
||||
|
||||
@@ -306,46 +306,37 @@ end
|
||||
|
||||
-- add class information
|
||||
function WayHandlers.classes(profile,way,result,data)
|
||||
if not profile.classes then
|
||||
return
|
||||
end
|
||||
|
||||
local allowed_classes = Set {}
|
||||
for k, v in pairs(profile.classes) do
|
||||
allowed_classes[v] = true
|
||||
end
|
||||
|
||||
local forward_toll, backward_toll = Tags.get_forward_backward_by_key(way, data, "toll")
|
||||
local forward_route, backward_route = Tags.get_forward_backward_by_key(way, data, "route")
|
||||
local tunnel = way:get_value_by_key("tunnel")
|
||||
|
||||
if allowed_classes["tunnel"] and tunnel and tunnel ~= "no" then
|
||||
if tunnel and tunnel ~= "no" then
|
||||
result.forward_classes["tunnel"] = true
|
||||
result.backward_classes["tunnel"] = true
|
||||
end
|
||||
|
||||
if allowed_classes["toll"] and forward_toll == "yes" then
|
||||
if forward_toll == "yes" then
|
||||
result.forward_classes["toll"] = true
|
||||
end
|
||||
if allowed_classes["toll"] and backward_toll == "yes" then
|
||||
if backward_toll == "yes" then
|
||||
result.backward_classes["toll"] = true
|
||||
end
|
||||
|
||||
if allowed_classes["ferry"] and forward_route == "ferry" then
|
||||
if forward_route == "ferry" then
|
||||
result.forward_classes["ferry"] = true
|
||||
end
|
||||
if allowed_classes["ferry"] and backward_route == "ferry" then
|
||||
if backward_route == "ferry" then
|
||||
result.backward_classes["ferry"] = true
|
||||
end
|
||||
|
||||
if allowed_classes["restricted"] and result.forward_restricted then
|
||||
if result.forward_restricted then
|
||||
result.forward_classes["restricted"] = true
|
||||
end
|
||||
if allowed_classes["restricted"] and result.backward_restricted then
|
||||
if result.backward_restricted then
|
||||
result.backward_classes["restricted"] = true
|
||||
end
|
||||
|
||||
if allowed_classes["motorway"] and (data.highway == "motorway" or data.highway == "motorway_link") then
|
||||
if data.highway == "motorway" or data.highway == "motorway_link" then
|
||||
result.forward_classes["motorway"] = true
|
||||
result.backward_classes["motorway"] = true
|
||||
end
|
||||
@@ -511,38 +502,6 @@ function WayHandlers.handle_width(profile,way,result,data)
|
||||
end
|
||||
end
|
||||
|
||||
-- handle maxweight tags
|
||||
function WayHandlers.handle_weight(profile,way,result,data)
|
||||
local keys = Sequence { 'maxweight' }
|
||||
local forward, backward = Tags.get_forward_backward_by_set(way,data,keys)
|
||||
forward = Measure.get_max_weight(forward)
|
||||
backward = Measure.get_max_weight(backward)
|
||||
|
||||
if forward and forward < profile.vehicle_weight then
|
||||
result.forward_mode = mode.inaccessible
|
||||
end
|
||||
|
||||
if backward and backward < profile.vehicle_weight then
|
||||
result.backward_mode = mode.inaccessible
|
||||
end
|
||||
end
|
||||
|
||||
-- handle maxlength tags
|
||||
function WayHandlers.handle_length(profile,way,result,data)
|
||||
local keys = Sequence { 'maxlength' }
|
||||
local forward, backward = Tags.get_forward_backward_by_set(way,data,keys)
|
||||
forward = Measure.get_max_length(forward)
|
||||
backward = Measure.get_max_length(backward)
|
||||
|
||||
if forward and forward < profile.vehicle_length then
|
||||
result.forward_mode = mode.inaccessible
|
||||
end
|
||||
|
||||
if backward and backward < profile.vehicle_length then
|
||||
result.backward_mode = mode.inaccessible
|
||||
end
|
||||
end
|
||||
|
||||
-- handle oneways tags
|
||||
function WayHandlers.oneway(profile,way,result,data)
|
||||
if not profile.oneway_handling then
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
-- Secondary road: 18km/h = 18000m/3600s = 100m/20s
|
||||
-- Tertiary road: 12km/h = 12000m/3600s = 100m/30s
|
||||
|
||||
api_version = 4
|
||||
api_version = 3
|
||||
|
||||
function setup()
|
||||
return {
|
||||
@@ -14,7 +14,7 @@ function setup()
|
||||
max_speed_for_map_matching = 30/3.6, --km -> m/s
|
||||
weight_name = 'duration',
|
||||
process_call_tagless_node = false,
|
||||
u_turn_penalty = 20,
|
||||
uturn_penalty = 20,
|
||||
traffic_light_penalty = 7, -- seconds
|
||||
use_turn_restrictions = true
|
||||
},
|
||||
@@ -32,7 +32,7 @@ function setup()
|
||||
primary = 36,
|
||||
secondary = 18,
|
||||
tertiary = 12,
|
||||
steps = 6
|
||||
steps = 6,
|
||||
}
|
||||
}
|
||||
end
|
||||
@@ -128,9 +128,9 @@ function process_way (profile, way, result)
|
||||
end
|
||||
|
||||
function process_turn (profile, turn)
|
||||
if turn.is_u_turn then
|
||||
turn.duration = turn.duration + profile.properties.u_turn_penalty
|
||||
turn.weight = turn.weight + profile.properties.u_turn_penalty
|
||||
if turn.direction_modifier == direction_modifier.uturn then
|
||||
turn.duration = profile.properties.uturn_penalty
|
||||
turn.weight = profile.properties.uturn_penalty
|
||||
end
|
||||
if turn.has_traffic_light then
|
||||
turn.duration = turn.duration + profile.properties.traffic_light_penalty
|
||||
|
||||
@@ -9,8 +9,6 @@ lonlat = lambda x: (coord2float(x['lon']['__value']), coord2float(x['lat']['__va
|
||||
|
||||
def call(this, method, *args):
|
||||
"""Call this.method(args)"""
|
||||
if (str(this) == '<optimized out>'):
|
||||
raise BaseException('"this" is optimized out')
|
||||
command = '(*({})({})).{}({})'.format(this.type.target().pointer(), this.address, method, ','.join((str(x) for x in args)))
|
||||
return gdb.parse_and_eval(command)
|
||||
|
||||
@@ -236,6 +234,7 @@ class SVGPrinter (gdb.Command):
|
||||
mld_facade = facade.cast(gdb.lookup_type('osrm::engine::datafacade::ContiguousInternalMemoryAlgorithmDataFacade<osrm::engine::routing_algorithms::mld::Algorithm>'))
|
||||
mld_partition = mld_facade['mld_partition']
|
||||
mld_levels = call(mld_partition, 'GetNumberOfLevels')
|
||||
print (mld_level, mld_levels)
|
||||
if mld_level < mld_levels:
|
||||
sentinel_node = call(mld_partition['partition'], 'size') - 1 # GetSentinelNode
|
||||
number_of_cells = call(mld_partition, 'GetCell', mld_level, sentinel_node) # GetNumberOfCells
|
||||
@@ -273,7 +272,6 @@ class SVGPrinter (gdb.Command):
|
||||
for node in nodes:
|
||||
geometry_id = call(facade, 'GetGeometryIndex', node)
|
||||
direction = 'forward' if geometry_id['forward'] else 'reverse'
|
||||
print (geometry_id, direction)
|
||||
geometry = SVGPrinter.getByGeometryId(facade, geometry_id, 'Geometry')
|
||||
weights = SVGPrinter.getByGeometryId(facade, geometry_id, 'Weights')
|
||||
|
||||
|
||||
+45
-60
@@ -10,12 +10,13 @@ const clu = require('command-line-usage');
|
||||
const ansi = require('ansi-escape-sequences');
|
||||
const turf = require('turf');
|
||||
const jp = require('jsonpath');
|
||||
const csv_stringify = require('csv-stringify/lib/sync');
|
||||
|
||||
const run_query = (query_options, filters, callback) => {
|
||||
let tic = () => 0.;
|
||||
http.request(query_options, function (res) {
|
||||
let body = '', ttfb = tic();
|
||||
if (res.statusCode != 200)
|
||||
return callback(query_options.path, res.statusCode, ttfb);
|
||||
|
||||
res.setEncoding('utf8');
|
||||
res.on('data', function (chunk) {
|
||||
@@ -34,51 +35,27 @@ const run_query = (query_options, filters, callback) => {
|
||||
}).end();
|
||||
};
|
||||
|
||||
function generate_points(polygon, number, coordinates_number, max_distance) {
|
||||
function generate_points(polygon, number) {
|
||||
let query_points = [];
|
||||
while (query_points.length < number) {
|
||||
let points = [];
|
||||
|
||||
while(points.length < coordinates_number) {
|
||||
let chunk = turf
|
||||
.random('points', coordinates_number, { bbox: turf.bbox(polygon)})
|
||||
.features
|
||||
.map(x => x.geometry.coordinates)
|
||||
.filter(pt => turf.inside(pt, polygon));
|
||||
|
||||
if (max_distance > 0)
|
||||
{
|
||||
chunk.forEach(pt => {
|
||||
if (points.length == 0)
|
||||
{
|
||||
points.push(pt);
|
||||
}
|
||||
else
|
||||
{
|
||||
let distance = turf.distance(pt, points[points.length-1], 'meters');
|
||||
if (distance < max_distance)
|
||||
{
|
||||
points.push(pt);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
points = points.concat(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
query_points.push(points);
|
||||
var chunk = turf
|
||||
.random('points', number, { bbox: turf.bbox(polygon)})
|
||||
.features
|
||||
.map(x => x.geometry.coordinates)
|
||||
.filter(pt => turf.inside(pt, polygon));
|
||||
query_points = query_points.concat(chunk);
|
||||
}
|
||||
|
||||
return query_points;
|
||||
return query_points.slice(0, number);
|
||||
}
|
||||
|
||||
function generate_queries(options, query_points) {
|
||||
let queries = query_points.map(points => {
|
||||
return options.path.replace(/{}/g, x => points.pop().join(','));
|
||||
});
|
||||
function generate_queries(options, query_points, coordinates_number) {
|
||||
let queries = [];
|
||||
for (let chunk = 0; chunk < query_points.length; chunk += coordinates_number)
|
||||
{
|
||||
let points = query_points.slice(chunk, chunk + coordinates_number);
|
||||
let query = options.path.replace(/{}/g, x => points.pop().join(','));
|
||||
queries.push(query);
|
||||
}
|
||||
return queries;
|
||||
}
|
||||
|
||||
@@ -96,22 +73,27 @@ function BoundingBox(x) {
|
||||
}
|
||||
const optionsList = [
|
||||
{name: 'help', alias: 'h', type: Boolean, description: 'Display this usage guide.', defaultValue: false},
|
||||
{name: 'server', alias: 's', type: ServerDetails, defaultValue: ServerDetails('localhost:5000'), description: 'OSRM routing server', typeLabel: '{underline hostname[:port]}'},
|
||||
{name: 'path', alias: 'p', type: String, defaultValue: '/route/v1/driving/{};{}', description: 'OSRM query path with \\{\\} coordinate placeholders, default /route/v2/driving/\\{\\};\\{\\}', typeLabel: '{underline path}'},
|
||||
{name: 'filter', alias: 'f', type: String, defaultValue: ['$.routes[0].weight'], multiple: true, description: 'JSONPath filters, default "$.routes[0].weight"', typeLabel: '{underline filter}'},
|
||||
{name: 'bounding-box', alias: 'b', type: BoundingBox, defaultValue: BoundingBox('5.86442,47.2654,15.0508,55.1478'), multiple: true, description: 'queries bounding box, default "5.86442,47.2654,15.0508,55.1478"', typeLabel: '{underline west,south,east,north}'},
|
||||
{name: 'max-sockets', alias: 'm', type: Number, defaultValue: 1, description: 'how many concurrent sockets the agent can have open per origin, default 1', typeLabel: '{underline number}'},
|
||||
{name: 'number', alias: 'n', type: Number, defaultValue: 10, description: 'number of query points, default 10', typeLabel: '{underline number}'},
|
||||
{name: 'max-distance', alias: 'd', type: Number, defaultValue: -1, description: 'maximal distance between coordinates', typeLabel: '{underline number}'},
|
||||
{name: 'queries-files', alias: 'q', type: String, description: 'CSV file with queries in the first row', typeLabel: '{underline file}'},
|
||||
];
|
||||
{name: 'server', alias: 's', type: ServerDetails, defaultValue: ServerDetails('localhost:5000'),
|
||||
description: 'OSRM routing server', typeLabel: '[underline]{hostname[:port]}'},
|
||||
{name: 'path', alias: 'p', type: String, defaultValue: '/route/v1/driving/{};{}',
|
||||
description: 'OSRM query path with {} coordinate placeholders, default /route/v1/driving/{};{}', typeLabel: '[underline]{path}'},
|
||||
{name: 'filter', alias: 'f', type: String, defaultValue: ['$.routes[0].weight'], multiple: true,
|
||||
description: 'JSONPath filters, default "$.routes[0].weight"', typeLabel: '[underline]{filter}'},
|
||||
{name: 'bounding-box', alias: 'b', type: BoundingBox, defaultValue: BoundingBox('5.86442,47.2654,15.0508,55.1478'), multiple: true,
|
||||
description: 'queries bounding box, default "5.86442,47.2654,15.0508,55.1478"', typeLabel: '[underline]{west,south,east,north}'},
|
||||
{name: 'max-sockets', alias: 'm', type: Number, defaultValue: 1,
|
||||
description: 'how many concurrent sockets the agent can have open per origin, default 1', typeLabel: '[underline]{number}'},
|
||||
{name: 'number', alias: 'n', type: Number, defaultValue: 10,
|
||||
description: 'number of query points, default 10', typeLabel: '[underline]{number}'},
|
||||
{name: 'queries-files', alias: 'q', type: String,
|
||||
description: 'CSV file with queries in the first row', typeLabel: '[underline]{file}'}];
|
||||
const options = cla(optionsList);
|
||||
if (options.help) {
|
||||
const banner =`\
|
||||
____ _______ __ ______ __ ___ ___ _________
|
||||
/ __ \\\\/ __/ _ \\\\/ |/ / _ \\\\/ / / / |/ / |/ / __/ _ \\\\
|
||||
/ /_/ /\\\\ \\\\/ , _/ /|_/ / , _/ /_/ / / / _// , _/
|
||||
\\\\____/___/_/|_/_/ /_/_/|_|\\\\____/_/|_/_/|_/___/_/|_|`;
|
||||
const banner =
|
||||
String.raw` ____ _______ __ ___ ___ __ ___ ___ _________ ` + '\n' +
|
||||
String.raw` / __ \/ __/ _ \/ |/ / / _ \/ / / / |/ / |/ / __/ _ \ ` + '\n' +
|
||||
String.raw`/ /_/ /\ \/ , _/ /|_/ / / , _/ /_/ / / / _// , _/ ` + '\n' +
|
||||
String.raw`\____/___/_/|_/_/ /_/ /_/|_|\____/_/|_/_/|_/___/_/|_| `;
|
||||
const usage = clu([
|
||||
{ content: ansi.format(banner, 'green'), raw: true },
|
||||
{ header: 'Run OSRM queries and collect results'/*, content: 'Generates something [italic]{very} important.'*/ },
|
||||
@@ -132,8 +114,8 @@ if (options.hasOwnProperty('queries-files')) {
|
||||
} else {
|
||||
const polygon = options['bounding-box'].map(x => x.poly).reduce((x,y) => turf.union(x, y));
|
||||
const coordinates_number = (options.path.match(/{}/g) || []).length;
|
||||
const query_points = generate_points(polygon, options.number, coordinates_number, options['max-distance']);
|
||||
queries = generate_queries(options, query_points);
|
||||
const query_points = generate_points(polygon, coordinates_number * options.number);
|
||||
queries = generate_queries(options, query_points, coordinates_number);
|
||||
}
|
||||
queries = queries.map(q => { return {hostname: options.server.hostname, port: options.server.port, path: q}; });
|
||||
|
||||
@@ -141,8 +123,11 @@ queries = queries.map(q => { return {hostname: options.server.hostname, port: op
|
||||
http.globalAgent.maxSockets = options['max-sockets'];
|
||||
queries.map(query => {
|
||||
run_query(query, options.filter, (query, code, ttfb, total, results) => {
|
||||
let data = results ? JSON.stringify(results[0]) : '';
|
||||
let record = [[query, code, ttfb, total, data]];
|
||||
process.stdout.write(csv_stringify(record));
|
||||
let str = `"${query}",${code}`;
|
||||
if (ttfb !== undefined) str += `,${ttfb}`;
|
||||
if (total !== undefined) str += `,${total}`;
|
||||
if (typeof results === 'object' && results.length > 0)
|
||||
str += ',' + results.map(x => isNaN(x) ? '"' + JSON.stringify(x).replace(/\n/g, ';').replace(/"/g, "'") + '"' : Number(x)).join(',');
|
||||
console.log(str);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,50 +9,79 @@ set -o nounset
|
||||
# structure will be lost.
|
||||
# http://git.661346.n2.nabble.com/subtree-merges-lose-prefix-after-rebase-td7332850.html
|
||||
|
||||
OSMIUM_PATH="osmcode/libosmium"
|
||||
OSMIUM_TAG=v2.14.0
|
||||
OSMIUM_REPO="https://github.com/osmcode/libosmium.git"
|
||||
OSMIUM_TAG=v2.13.1
|
||||
|
||||
VARIANT_PATH="mapbox/variant"
|
||||
VARIANT_REPO="https://github.com/mapbox/variant.git"
|
||||
VARIANT_TAG=v1.1.3
|
||||
|
||||
SOL_PATH="ThePhD/sol2"
|
||||
SOL_REPO="https://github.com/ThePhD/sol2.git"
|
||||
SOL_TAG=v2.17.5
|
||||
|
||||
RAPIDJSON_PATH="Tencent/rapidjson"
|
||||
RAPIDJSON_REPO="https://github.com/miloyip/rapidjson.git"
|
||||
RAPIDJSON_TAG=v1.1.0
|
||||
|
||||
MICROTAR_PATH="rxi/microtar"
|
||||
MICROTAR_REPO="https://github.com/rxi/microtar"
|
||||
MICROTAR_TAG=v0.1.0
|
||||
|
||||
PROTOZERO_PATH="mapbox/protozero"
|
||||
PROTOZERO_TAG=v1.6.2
|
||||
VARIANT_LATEST=$(curl "https://api.github.com/repos/mapbox/variant/releases/latest" | jq ".tag_name")
|
||||
OSMIUM_LATEST=$(curl "https://api.github.com/repos/osmcode/libosmium/releases/latest" | jq ".tag_name")
|
||||
SOL_LATEST=$(curl "https://api.github.com/repos/ThePhD/sol2/releases/latest" | jq ".tag_name")
|
||||
RAPIDJSON_LATEST=$(curl "https://api.github.com/repos/miloyip/rapidjson/releases/latest" | jq ".tag_name")
|
||||
MICROTAR_LATEST=$(curl "https://api.github.com/repos/rxi/microtar/releases/latest" | jq ".tag_name")
|
||||
|
||||
VTZERO_PATH="mapbox/vtzero"
|
||||
VTZERO_TAG=v1.0.1
|
||||
echo "Latest osmium release is $OSMIUM_LATEST, pulling in \"$OSMIUM_TAG\""
|
||||
echo "Latest variant release is $VARIANT_LATEST, pulling in \"$VARIANT_TAG\""
|
||||
echo "Latest sol2 release is $SOL_LATEST, pulling in \"$SOL_TAG\""
|
||||
echo "Latest rapidjson release is $RAPIDJSON_LATEST, pulling in \"$RAPIDJSON_TAG\""
|
||||
echo "Latest microtar release is $MICROTAR_LATEST, pulling in \"$MICROTAR_TAG\""
|
||||
|
||||
function update_subtree () {
|
||||
name=${1^^}
|
||||
path=$(tmpvar=${name}_PATH && echo ${!tmpvar})
|
||||
tag=$(tmpvar=${name}_TAG && echo ${!tmpvar})
|
||||
dir=$(basename $path)
|
||||
repo="https://github.com/${path}.git"
|
||||
latest=$(curl -s "https://api.github.com/repos/${path}/releases/latest" | jq ".tag_name")
|
||||
read -p "Update osmium (y/n) " ok
|
||||
if [[ $ok =~ [yY] ]]
|
||||
then
|
||||
if [ -d "third_party/libosmium" ]; then
|
||||
git subtree pull -P third_party/libosmium/ $OSMIUM_REPO $OSMIUM_TAG --squash
|
||||
else
|
||||
git subtree add -P third_party/libosmium/ $OSMIUM_REPO $OSMIUM_TAG --squash
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Latest $1 release is ${latest}, pulling in \"${tag}\""
|
||||
read -p "Update variant (y/n) " ok
|
||||
if [[ $ok =~ [yY] ]]
|
||||
then
|
||||
if [ -d "third_party/variant" ]; then
|
||||
git subtree pull -P third_party/variant/ $VARIANT_REPO $VARIANT_TAG --squash
|
||||
else
|
||||
git subtree add -P third_party/variant/ $VARIANT_REPO $VARIANT_TAG --squash
|
||||
fi
|
||||
fi
|
||||
|
||||
read -p "Update ${1} (y/n) " ok
|
||||
read -p "Update sol2 (y/n) " ok
|
||||
if [[ $ok =~ [yY] ]]
|
||||
then
|
||||
if [ -d "third_party/sol2" ]; then
|
||||
git subtree pull -P third_party/sol2/sol2/ $SOL_REPO $SOL_TAG --squash
|
||||
else
|
||||
git subtree add -P third_party/sol2/sol2/ $SOL_REPO $SOL_TAG --squash
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ $ok =~ [yY] ]]
|
||||
then
|
||||
if [ -d "third_party/$dir" ]; then
|
||||
git subtree pull -P third_party/$dir ${repo} ${tag} --squash
|
||||
else
|
||||
git subtree add -P third_party/$dir ${repo} ${tag} --squash
|
||||
fi
|
||||
fi
|
||||
}
|
||||
read -p "Update rapidjson (y/n) " ok
|
||||
if [[ $ok =~ [yY] ]]
|
||||
then
|
||||
if [ -d "third_party/rapidjson" ]; then
|
||||
git subtree pull -P third_party/rapidjson/ $RAPIDJSON_REPO $RAPIDJSON_TAG --squash
|
||||
else
|
||||
git subtree add -P third_party/rapidjson/ $RAPIDJSON_REPO $RAPIDJSON_TAG --squash
|
||||
fi
|
||||
fi
|
||||
|
||||
## Update dependencies
|
||||
for dep in osmium variant sol rapidjson microtar protozero vtzero ; do
|
||||
update_subtree $dep
|
||||
done
|
||||
read -p "Update microtar (y/n) " ok
|
||||
if [[ $ok =~ [yY] ]]
|
||||
then
|
||||
if [ -d "third_party/microtar" ]; then
|
||||
git subtree pull -P third_party/microtar/ $MICROTAR_REPO $MICROTAR_TAG --squash
|
||||
else
|
||||
git subtree add -P third_party/microtar/ $MICROTAR_REPO $MICROTAR_TAG --squash
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -75,12 +75,6 @@ int Contractor::Run()
|
||||
EdgeID number_of_edge_based_nodes = updater.LoadAndUpdateEdgeExpandedGraph(
|
||||
edge_based_edge_list, node_weights, connectivity_checksum);
|
||||
|
||||
// Convert node weights for oneway streets to INVALID_EDGE_WEIGHT
|
||||
for (auto &weight : node_weights)
|
||||
{
|
||||
weight = (weight & 0x80000000) ? INVALID_EDGE_WEIGHT : weight;
|
||||
}
|
||||
|
||||
// Contracting the edge-expanded graph
|
||||
|
||||
TIMER_START(contraction);
|
||||
|
||||
@@ -74,28 +74,24 @@ void printUnreachableStatistics(const Partition &partition,
|
||||
|
||||
auto LoadAndUpdateEdgeExpandedGraph(const CustomizationConfig &config,
|
||||
const partitioner::MultiLevelPartition &mlp,
|
||||
std::vector<EdgeWeight> &node_weights,
|
||||
std::vector<EdgeDuration> &node_durations,
|
||||
std::uint32_t &connectivity_checksum)
|
||||
{
|
||||
updater::Updater updater(config.updater_config);
|
||||
|
||||
EdgeID num_nodes;
|
||||
std::vector<extractor::EdgeBasedEdge> edge_based_edge_list;
|
||||
EdgeID num_nodes = updater.LoadAndUpdateEdgeExpandedGraph(
|
||||
edge_based_edge_list, node_weights, node_durations, connectivity_checksum);
|
||||
std::tie(num_nodes, edge_based_edge_list, connectivity_checksum) =
|
||||
updater.LoadAndUpdateEdgeExpandedGraph();
|
||||
|
||||
auto directed = partitioner::splitBidirectionalEdges(edge_based_edge_list);
|
||||
|
||||
auto tidied = partitioner::prepareEdgesForUsageInGraph<
|
||||
typename partitioner::MultiLevelEdgeBasedGraph::InputEdge>(std::move(directed));
|
||||
|
||||
auto edge_based_graph =
|
||||
partitioner::MultiLevelEdgeBasedGraph(mlp, num_nodes, std::move(tidied));
|
||||
auto tidied =
|
||||
partitioner::prepareEdgesForUsageInGraph<StaticEdgeBasedGraphEdge>(std::move(directed));
|
||||
auto edge_based_graph = customizer::MultiLevelEdgeBasedGraph(mlp, num_nodes, std::move(tidied));
|
||||
|
||||
return edge_based_graph;
|
||||
}
|
||||
|
||||
std::vector<CellMetric> customizeFilteredMetrics(const partitioner::MultiLevelEdgeBasedGraph &graph,
|
||||
std::vector<CellMetric> customizeFilteredMetrics(const MultiLevelEdgeBasedGraph &graph,
|
||||
const partitioner::CellStorage &storage,
|
||||
const CellCustomizer &customizer,
|
||||
const std::vector<std::vector<bool>> &node_filters)
|
||||
@@ -123,13 +119,8 @@ int Customizer::Run(const CustomizationConfig &config)
|
||||
partitioner::MultiLevelPartition mlp;
|
||||
partitioner::files::readPartition(config.GetPath(".osrm.partition"), mlp);
|
||||
|
||||
std::vector<EdgeWeight> node_weights;
|
||||
std::vector<EdgeDuration> node_durations; // TODO: to be removed later
|
||||
std::uint32_t connectivity_checksum = 0;
|
||||
auto graph = LoadAndUpdateEdgeExpandedGraph(
|
||||
config, mlp, node_weights, node_durations, connectivity_checksum);
|
||||
BOOST_ASSERT(graph.GetNumberOfNodes() == node_weights.size());
|
||||
std::for_each(node_weights.begin(), node_weights.end(), [](auto &w) { w &= 0x7fffffff; });
|
||||
auto graph = LoadAndUpdateEdgeExpandedGraph(config, mlp, connectivity_checksum);
|
||||
util::Log() << "Loaded edge based graph: " << graph.GetNumberOfEdges() << " edges, "
|
||||
<< graph.GetNumberOfNodes() << " nodes";
|
||||
|
||||
@@ -166,10 +157,7 @@ int Customizer::Run(const CustomizationConfig &config)
|
||||
util::Log() << "MLD customization writing took " << TIMER_SEC(writing_mld_data) << " seconds";
|
||||
|
||||
TIMER_START(writing_graph);
|
||||
MultiLevelEdgeBasedGraph shaved_graph{
|
||||
std::move(graph), std::move(node_weights), std::move(node_durations)};
|
||||
customizer::files::writeGraph(
|
||||
config.GetPath(".osrm.mldgr"), shaved_graph, connectivity_checksum);
|
||||
partitioner::files::writeGraph(config.GetPath(".osrm.mldgr"), graph, connectivity_checksum);
|
||||
TIMER_STOP(writing_graph);
|
||||
util::Log() << "Graph writing took " << TIMER_SEC(writing_graph) << " seconds";
|
||||
|
||||
|
||||
@@ -621,7 +621,6 @@ RouteSteps collapseSegregatedTurnInstructions(RouteSteps steps)
|
||||
// else if the current step is segregated and the next step is not segregated
|
||||
// and the next step is not a roundabout then combine with turn adjustment
|
||||
else if (curr_step->is_segregated && !next_step->is_segregated &&
|
||||
!hasRoundaboutType(curr_step->maneuver.instruction) &&
|
||||
!hasRoundaboutType(next_step->maneuver.instruction))
|
||||
{
|
||||
// Determine if u-turn
|
||||
|
||||
@@ -81,21 +81,16 @@ Status TablePlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms,
|
||||
}
|
||||
|
||||
auto snapped_phantoms = SnapPhantomNodes(phantom_nodes);
|
||||
auto result_table =
|
||||
algorithms.ManyToManySearch(snapped_phantoms, params.sources, params.destinations);
|
||||
|
||||
bool request_distance = params.annotations & api::TableParameters::AnnotationsType::Distance;
|
||||
bool request_duration = params.annotations & api::TableParameters::AnnotationsType::Duration;
|
||||
|
||||
auto result_tables_pair = algorithms.ManyToManySearch(
|
||||
snapped_phantoms, params.sources, params.destinations, request_distance, request_duration);
|
||||
|
||||
if ((request_duration && result_tables_pair.first.empty()) ||
|
||||
(request_distance && result_tables_pair.second.empty()))
|
||||
if (result_table.empty())
|
||||
{
|
||||
return Error("NoTable", "No table found", result);
|
||||
}
|
||||
|
||||
api::TableAPI table_api{facade, params};
|
||||
table_api.MakeResponse(result_tables_pair, snapped_phantoms, result);
|
||||
table_api.MakeResponse(result_table, snapped_phantoms, result);
|
||||
|
||||
return Status::Ok;
|
||||
}
|
||||
|
||||
+540
-212
@@ -1,6 +1,5 @@
|
||||
#include "guidance/turn_instruction.hpp"
|
||||
|
||||
#include "engine/plugins/plugin_base.hpp"
|
||||
#include "engine/plugins/plugin_base.hpp"
|
||||
#include "engine/plugins/tile.hpp"
|
||||
|
||||
@@ -9,19 +8,21 @@
|
||||
#include "util/vector_tile.hpp"
|
||||
#include "util/web_mercator.hpp"
|
||||
|
||||
#include "engine/api/json_factory.hpp"
|
||||
|
||||
#include <boost/geometry.hpp>
|
||||
#include <boost/geometry/geometries/geometries.hpp>
|
||||
#include <boost/geometry/geometries/point_xy.hpp>
|
||||
#include <boost/geometry/multi/geometries/multi_linestring.hpp>
|
||||
|
||||
#include <vtzero/builder.hpp>
|
||||
#include <vtzero/geometry.hpp>
|
||||
#include <vtzero/index.hpp>
|
||||
#include <protozero/pbf_writer.hpp>
|
||||
#include <protozero/varint.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -40,7 +41,45 @@ constexpr const static int MIN_ZOOM_FOR_TURNS = 15;
|
||||
namespace
|
||||
{
|
||||
|
||||
// Creates an indexed lookup table for values - used to encoded the vector tile
|
||||
// which uses a lookup table and index pointers for encoding
|
||||
template <typename T> struct ValueIndexer
|
||||
{
|
||||
private:
|
||||
std::vector<T> used_values;
|
||||
std::unordered_map<T, std::size_t> value_offsets;
|
||||
|
||||
public:
|
||||
std::size_t add(const T &value)
|
||||
{
|
||||
const auto found = value_offsets.find(value);
|
||||
std::size_t offset;
|
||||
|
||||
if (found == value_offsets.end())
|
||||
{
|
||||
used_values.push_back(value);
|
||||
offset = used_values.size() - 1;
|
||||
value_offsets[value] = offset;
|
||||
}
|
||||
else
|
||||
{
|
||||
offset = found->second;
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
std::size_t indexOf(const T &value) { return value_offsets[value]; }
|
||||
|
||||
const std::vector<T> &values() { return used_values; }
|
||||
|
||||
std::size_t size() const { return used_values.size(); }
|
||||
};
|
||||
|
||||
using RTreeLeaf = datafacade::BaseDataFacade::RTreeLeaf;
|
||||
// TODO: Port all this encoding logic to https://github.com/mapbox/vector-tile, which wasn't
|
||||
// available when this code was originally written.
|
||||
|
||||
// Simple container class for WGS84 coordinates
|
||||
template <typename T> struct Point final
|
||||
{
|
||||
@@ -93,15 +132,58 @@ const static box_t clip_box(point_t(-util::vector_tile::BUFFER, -util::vector_ti
|
||||
point_t(util::vector_tile::EXTENT + util::vector_tile::BUFFER,
|
||||
util::vector_tile::EXTENT + util::vector_tile::BUFFER));
|
||||
|
||||
/**
|
||||
* Return the x1,y1,x2,y2 pixel coordinates of a line in a given
|
||||
* tile.
|
||||
*
|
||||
* @param start the first coordinate of the line
|
||||
* @param target the last coordinate of the line
|
||||
* @param tile_bbox the boundaries of the tile, in mercator coordinates
|
||||
* @return a FixedLine with coordinates relative to the tile_bbox.
|
||||
*/
|
||||
// from mapnik-vector-tile
|
||||
// Encodes a linestring using protobuf zigzag encoding
|
||||
inline bool encodeLinestring(const FixedLine &line,
|
||||
protozero::packed_field_uint32 &geometry,
|
||||
std::int32_t &start_x,
|
||||
std::int32_t &start_y)
|
||||
{
|
||||
const std::size_t line_size = line.size();
|
||||
if (line_size < 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const unsigned lineto_count = static_cast<const unsigned>(line_size) - 1;
|
||||
|
||||
auto pt = line.begin();
|
||||
const constexpr int MOVETO_COMMAND = 9;
|
||||
geometry.add_element(MOVETO_COMMAND); // move_to | (1 << 3)
|
||||
geometry.add_element(protozero::encode_zigzag32(pt->x - start_x));
|
||||
geometry.add_element(protozero::encode_zigzag32(pt->y - start_y));
|
||||
start_x = pt->x;
|
||||
start_y = pt->y;
|
||||
// This means LINETO repeated N times
|
||||
// See: https://github.com/mapbox/vector-tile-spec/tree/master/2.1#example-command-integers
|
||||
geometry.add_element((lineto_count << 3u) | 2u);
|
||||
// Now that we've issued the LINETO REPEAT N command, we append
|
||||
// N coordinate pairs immediately after the command.
|
||||
for (++pt; pt != line.end(); ++pt)
|
||||
{
|
||||
const std::int32_t dx = pt->x - start_x;
|
||||
const std::int32_t dy = pt->y - start_y;
|
||||
geometry.add_element(protozero::encode_zigzag32(dx));
|
||||
geometry.add_element(protozero::encode_zigzag32(dy));
|
||||
start_x = pt->x;
|
||||
start_y = pt->y;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// from mapnik-vctor-tile
|
||||
// Encodes a point
|
||||
inline void encodePoint(const FixedPoint &pt, protozero::packed_field_uint32 &geometry)
|
||||
{
|
||||
const constexpr int MOVETO_COMMAND = 9;
|
||||
geometry.add_element(MOVETO_COMMAND);
|
||||
const std::int32_t dx = pt.x;
|
||||
const std::int32_t dy = pt.y;
|
||||
// Manual zigzag encoding.
|
||||
geometry.add_element(protozero::encode_zigzag32(dx));
|
||||
geometry.add_element(protozero::encode_zigzag32(dy));
|
||||
}
|
||||
|
||||
linestring_t floatLineToTileLine(const FloatLine &geo_line, const BBox &tile_bbox)
|
||||
{
|
||||
linestring_t unclipped_line;
|
||||
@@ -279,144 +361,6 @@ std::vector<NodeID> getSegregatedNodes(const DataFacadeBase &facade,
|
||||
return result;
|
||||
}
|
||||
|
||||
struct SpeedLayer : public vtzero::layer_builder
|
||||
{
|
||||
|
||||
vtzero::value_index_small_uint uint_index;
|
||||
vtzero::value_index<vtzero::double_value_type, float, std::unordered_map> double_index;
|
||||
vtzero::value_index_internal<std::unordered_map> string_index;
|
||||
vtzero::value_index_bool bool_index;
|
||||
|
||||
vtzero::index_value key_speed;
|
||||
vtzero::index_value key_is_small;
|
||||
vtzero::index_value key_datasource;
|
||||
vtzero::index_value key_weight;
|
||||
vtzero::index_value key_duration;
|
||||
vtzero::index_value key_name;
|
||||
vtzero::index_value key_rate;
|
||||
|
||||
SpeedLayer(vtzero::tile_builder &tile)
|
||||
: layer_builder(tile, "speeds"), uint_index(*this), double_index(*this),
|
||||
string_index(*this), bool_index(*this), key_speed(add_key_without_dup_check("speed")),
|
||||
key_is_small(add_key_without_dup_check("is_small")),
|
||||
key_datasource(add_key_without_dup_check("datasource")),
|
||||
key_weight(add_key_without_dup_check("weight")),
|
||||
key_duration(add_key_without_dup_check("duration")),
|
||||
key_name(add_key_without_dup_check("name")), key_rate(add_key_without_dup_check("rate"))
|
||||
{
|
||||
}
|
||||
|
||||
}; // struct SpeedLayer
|
||||
|
||||
class SpeedLayerFeatureBuilder : public vtzero::linestring_feature_builder
|
||||
{
|
||||
|
||||
SpeedLayer &m_layer;
|
||||
|
||||
public:
|
||||
SpeedLayerFeatureBuilder(SpeedLayer &layer, uint64_t id)
|
||||
: vtzero::linestring_feature_builder(layer), m_layer(layer)
|
||||
{
|
||||
set_id(id);
|
||||
}
|
||||
|
||||
void set_speed(unsigned int value)
|
||||
{
|
||||
add_property(m_layer.key_speed, m_layer.uint_index(std::min(value, 127u)));
|
||||
}
|
||||
|
||||
void set_is_small(bool value) { add_property(m_layer.key_is_small, m_layer.bool_index(value)); }
|
||||
|
||||
void set_datasource(const std::string &value)
|
||||
{
|
||||
add_property(m_layer.key_datasource,
|
||||
m_layer.string_index(vtzero::encoded_property_value{value}));
|
||||
}
|
||||
|
||||
void set_weight(double value) { add_property(m_layer.key_weight, m_layer.double_index(value)); }
|
||||
|
||||
void set_duration(double value)
|
||||
{
|
||||
add_property(m_layer.key_duration, m_layer.double_index(value));
|
||||
}
|
||||
|
||||
void set_name(const boost::string_ref &value)
|
||||
{
|
||||
add_property(
|
||||
m_layer.key_name,
|
||||
m_layer.string_index(vtzero::encoded_property_value{value.data(), value.size()}));
|
||||
}
|
||||
|
||||
void set_rate(double value) { add_property(m_layer.key_rate, m_layer.double_index(value)); }
|
||||
|
||||
}; // class SpeedLayerFeatureBuilder
|
||||
|
||||
struct TurnsLayer : public vtzero::layer_builder
|
||||
{
|
||||
|
||||
vtzero::value_index<vtzero::sint_value_type, int, std::unordered_map> int_index;
|
||||
vtzero::value_index<vtzero::float_value_type, float, std::unordered_map> float_index;
|
||||
vtzero::value_index_internal<std::unordered_map> string_index;
|
||||
|
||||
vtzero::index_value key_bearing_in;
|
||||
vtzero::index_value key_turn_angle;
|
||||
vtzero::index_value key_cost;
|
||||
vtzero::index_value key_weight;
|
||||
vtzero::index_value key_turn_type;
|
||||
vtzero::index_value key_turn_modifier;
|
||||
|
||||
TurnsLayer(vtzero::tile_builder &tile)
|
||||
: layer_builder(tile, "turns"), int_index(*this), float_index(*this), string_index(*this),
|
||||
key_bearing_in(add_key_without_dup_check("bearing_in")),
|
||||
key_turn_angle(add_key_without_dup_check("turn_angle")),
|
||||
key_cost(add_key_without_dup_check("cost")),
|
||||
key_weight(add_key_without_dup_check("weight")),
|
||||
key_turn_type(add_key_without_dup_check("type")),
|
||||
key_turn_modifier(add_key_without_dup_check("modifier"))
|
||||
{
|
||||
}
|
||||
|
||||
}; // struct TurnsLayer
|
||||
|
||||
class TurnsLayerFeatureBuilder : public vtzero::point_feature_builder
|
||||
{
|
||||
|
||||
TurnsLayer &m_layer;
|
||||
|
||||
public:
|
||||
TurnsLayerFeatureBuilder(TurnsLayer &layer, uint64_t id)
|
||||
: vtzero::point_feature_builder(layer), m_layer(layer)
|
||||
{
|
||||
set_id(id);
|
||||
}
|
||||
|
||||
void set_bearing_in(int value)
|
||||
{
|
||||
add_property(m_layer.key_bearing_in, m_layer.int_index(value));
|
||||
}
|
||||
|
||||
void set_turn_angle(int value)
|
||||
{
|
||||
add_property(m_layer.key_turn_angle, m_layer.int_index(value));
|
||||
}
|
||||
|
||||
void set_cost(float value) { add_property(m_layer.key_cost, m_layer.float_index(value)); }
|
||||
|
||||
void set_weight(float value) { add_property(m_layer.key_weight, m_layer.float_index(value)); }
|
||||
|
||||
void set_turn(osrm::guidance::TurnInstruction value)
|
||||
{
|
||||
const auto type = osrm::guidance::internalInstructionTypeToString(value.type);
|
||||
const auto modifier = osrm::guidance::instructionModifierToString(value.direction_modifier);
|
||||
add_property(
|
||||
m_layer.key_turn_type,
|
||||
m_layer.string_index(vtzero::encoded_property_value{type.data(), type.size()}));
|
||||
add_property(
|
||||
m_layer.key_turn_modifier,
|
||||
m_layer.string_index(vtzero::encoded_property_value{modifier.data(), modifier.size()}));
|
||||
}
|
||||
}; // class TurnsLayerFeatureBuilder
|
||||
|
||||
void encodeVectorTile(const DataFacadeBase &facade,
|
||||
unsigned x,
|
||||
unsigned y,
|
||||
@@ -427,25 +371,117 @@ void encodeVectorTile(const DataFacadeBase &facade,
|
||||
const std::vector<NodeID> &segregated_nodes,
|
||||
std::string &pbf_buffer)
|
||||
{
|
||||
vtzero::tile_builder tile;
|
||||
|
||||
std::uint8_t max_datasource_id = 0;
|
||||
|
||||
// Vector tiles encode properties on features as indexes into a layer-specific
|
||||
// lookup table. These ValueIndexer's act as memoizers for values as we discover
|
||||
// them during edge explioration, and are then used to generate the lookup
|
||||
// tables for each tile layer.
|
||||
ValueIndexer<int> line_int_index;
|
||||
ValueIndexer<util::StringView> line_string_index;
|
||||
ValueIndexer<int> point_int_index;
|
||||
ValueIndexer<float> point_float_index;
|
||||
ValueIndexer<std::string> point_string_index;
|
||||
|
||||
const auto get_geometry_id = [&facade](auto edge) {
|
||||
return facade.GetGeometryIndex(edge.forward_segment_id.id).id;
|
||||
};
|
||||
|
||||
// Vector tiles encode feature properties as indexes into a lookup table. So, we need
|
||||
// to "pre-loop" over all the edges to create the lookup tables. Once we have those, we
|
||||
// can then encode the features, and we'll know the indexes that feature properties
|
||||
// need to refer to.
|
||||
for (const auto &edge_index : sorted_edge_indexes)
|
||||
{
|
||||
const auto &edge = edges[edge_index];
|
||||
|
||||
const auto geometry_id = get_geometry_id(edge);
|
||||
const auto forward_datasource_range = facade.GetUncompressedForwardDatasources(geometry_id);
|
||||
const auto reverse_datasource_range = facade.GetUncompressedReverseDatasources(geometry_id);
|
||||
|
||||
BOOST_ASSERT(edge.fwd_segment_position < forward_datasource_range.size());
|
||||
const auto forward_datasource = forward_datasource_range(edge.fwd_segment_position);
|
||||
BOOST_ASSERT(edge.fwd_segment_position < reverse_datasource_range.size());
|
||||
const auto reverse_datasource = reverse_datasource_range(reverse_datasource_range.size() -
|
||||
edge.fwd_segment_position - 1);
|
||||
|
||||
// Keep track of the highest datasource seen so that we don't write unnecessary
|
||||
// data to the layer attribute values
|
||||
max_datasource_id = std::max(max_datasource_id, forward_datasource);
|
||||
max_datasource_id = std::max(max_datasource_id, reverse_datasource);
|
||||
}
|
||||
|
||||
// Convert tile coordinates into mercator coordinates
|
||||
double min_mercator_lon, min_mercator_lat, max_mercator_lon, max_mercator_lat;
|
||||
util::web_mercator::xyzToMercator(
|
||||
x, y, z, min_mercator_lon, min_mercator_lat, max_mercator_lon, max_mercator_lat);
|
||||
const BBox tile_bbox{min_mercator_lon, min_mercator_lat, max_mercator_lon, max_mercator_lat};
|
||||
|
||||
// XXX leaving in some superfluous scopes to make diff easier to read.
|
||||
// Protobuf serializes blocks when objects go out of scope, hence
|
||||
// the extra scoping below.
|
||||
protozero::pbf_writer tile_writer{pbf_buffer};
|
||||
{
|
||||
{
|
||||
// Add a layer object to the PBF stream. 3=='layer' from the vector tile spec
|
||||
// (2.1)
|
||||
protozero::pbf_writer line_layer_writer(tile_writer, util::vector_tile::LAYER_TAG);
|
||||
// TODO: don't write a layer if there are no features
|
||||
|
||||
line_layer_writer.add_uint32(util::vector_tile::VERSION_TAG, 2); // version
|
||||
// Field 1 is the "layer name" field, it's a string
|
||||
line_layer_writer.add_string(util::vector_tile::NAME_TAG, "speeds"); // name
|
||||
// Field 5 is the tile extent. It's a uint32 and should be set to 4096
|
||||
// for normal vector tiles.
|
||||
line_layer_writer.add_uint32(util::vector_tile::EXTENT_TAG,
|
||||
util::vector_tile::EXTENT); // extent
|
||||
|
||||
// Because we need to know the indexes into the vector tile lookup table,
|
||||
// we need to do an initial pass over the data and create the complete
|
||||
// index of used values.
|
||||
for (const auto &edge_index : sorted_edge_indexes)
|
||||
{
|
||||
const auto &edge = edges[edge_index];
|
||||
const auto geometry_id = get_geometry_id(edge);
|
||||
|
||||
// Get coordinates for start/end nodes of segment (NodeIDs u and v)
|
||||
const auto a = facade.GetCoordinateOfNode(edge.u);
|
||||
const auto b = facade.GetCoordinateOfNode(edge.v);
|
||||
// Calculate the length in meters
|
||||
const double length = osrm::util::coordinate_calculation::haversineDistance(a, b);
|
||||
|
||||
// Weight values
|
||||
const auto forward_weight_range = facade.GetUncompressedForwardWeights(geometry_id);
|
||||
const auto reverse_weight_range = facade.GetUncompressedReverseWeights(geometry_id);
|
||||
const auto forward_weight = forward_weight_range[edge.fwd_segment_position];
|
||||
const auto reverse_weight = reverse_weight_range[reverse_weight_range.size() -
|
||||
edge.fwd_segment_position - 1];
|
||||
line_int_index.add(forward_weight);
|
||||
line_int_index.add(reverse_weight);
|
||||
|
||||
std::uint32_t forward_rate =
|
||||
static_cast<std::uint32_t>(round(length / forward_weight * 10.));
|
||||
std::uint32_t reverse_rate =
|
||||
static_cast<std::uint32_t>(round(length / reverse_weight * 10.));
|
||||
|
||||
line_int_index.add(forward_rate);
|
||||
line_int_index.add(reverse_rate);
|
||||
|
||||
// Duration values
|
||||
const auto forward_duration_range =
|
||||
facade.GetUncompressedForwardDurations(geometry_id);
|
||||
const auto reverse_duration_range =
|
||||
facade.GetUncompressedReverseDurations(geometry_id);
|
||||
const auto forward_duration = forward_duration_range[edge.fwd_segment_position];
|
||||
const auto reverse_duration = reverse_duration_range[reverse_duration_range.size() -
|
||||
edge.fwd_segment_position - 1];
|
||||
|
||||
line_int_index.add(forward_duration);
|
||||
line_int_index.add(reverse_duration);
|
||||
}
|
||||
|
||||
// Begin the layer features block
|
||||
{
|
||||
SpeedLayer speeds_layer{tile};
|
||||
|
||||
// Each feature gets a unique id, starting at 1
|
||||
unsigned id = 1;
|
||||
for (const auto &edge_index : sorted_edge_indexes)
|
||||
@@ -480,6 +516,7 @@ void encodeVectorTile(const DataFacadeBase &facade,
|
||||
const auto reverse_duration =
|
||||
reverse_duration_range[reverse_duration_range.size() -
|
||||
edge.fwd_segment_position - 1];
|
||||
|
||||
const auto forward_datasource_idx =
|
||||
forward_datasource_range(edge.fwd_segment_position);
|
||||
const auto reverse_datasource_idx = reverse_datasource_range(
|
||||
@@ -489,10 +526,84 @@ void encodeVectorTile(const DataFacadeBase &facade,
|
||||
const auto name_id = facade.GetNameIndex(edge.forward_segment_id.id);
|
||||
auto name = facade.GetNameForID(name_id);
|
||||
|
||||
line_string_index.add(name);
|
||||
|
||||
const auto encode_tile_line = [&line_layer_writer,
|
||||
&component_id,
|
||||
&id,
|
||||
&max_datasource_id,
|
||||
&line_int_index](
|
||||
const FixedLine &tile_line,
|
||||
const std::uint32_t speed_kmh_idx,
|
||||
const std::uint32_t rate_idx,
|
||||
const std::size_t weight_idx,
|
||||
const std::size_t duration_idx,
|
||||
const DatasourceID datasource_idx,
|
||||
const std::size_t name_idx,
|
||||
std::int32_t &start_x,
|
||||
std::int32_t &start_y) {
|
||||
// Here, we save the two attributes for our feature: the speed and
|
||||
// the is_small boolean. We only serve up speeds from 0-139, so all we
|
||||
// do is save the first
|
||||
protozero::pbf_writer feature_writer(line_layer_writer,
|
||||
util::vector_tile::FEATURE_TAG);
|
||||
// Field 3 is the "geometry type" field. Value 2 is "line"
|
||||
feature_writer.add_enum(
|
||||
util::vector_tile::GEOMETRY_TAG,
|
||||
util::vector_tile::GEOMETRY_TYPE_LINE); // geometry type
|
||||
// Field 1 for the feature is the "id" field.
|
||||
feature_writer.add_uint64(util::vector_tile::ID_TAG, id++); // id
|
||||
{
|
||||
// When adding attributes to a feature, we have to write
|
||||
// pairs of numbers. The first value is the index in the
|
||||
// keys array (written later), and the second value is the
|
||||
// index into the "values" array (also written later). We're
|
||||
// not writing the actual speed or bool value here, we're saving
|
||||
// an index into the "values" array. This means many features
|
||||
// can share the same value data, leading to smaller tiles.
|
||||
protozero::packed_field_uint32 field(
|
||||
feature_writer, util::vector_tile::FEATURE_ATTRIBUTES_TAG);
|
||||
|
||||
field.add_element(0); // "speed" tag key offset
|
||||
field.add_element(std::min(
|
||||
speed_kmh_idx, 127u)); // save the speed value, capped at 127
|
||||
field.add_element(1); // "is_small" tag key offset
|
||||
field.add_element(
|
||||
128 + (component_id.is_tiny ? 0 : 1)); // is_small feature offset
|
||||
field.add_element(2); // "datasource" tag key offset
|
||||
field.add_element(130 + datasource_idx); // datasource value offset
|
||||
field.add_element(3); // "weight" tag key offset
|
||||
field.add_element(130 + max_datasource_id + 1 +
|
||||
weight_idx); // weight value offset
|
||||
field.add_element(4); // "duration" tag key offset
|
||||
field.add_element(130 + max_datasource_id + 1 +
|
||||
duration_idx); // duration value offset
|
||||
field.add_element(5); // "name" tag key offset
|
||||
|
||||
field.add_element(130 + max_datasource_id + 1 +
|
||||
line_int_index.values().size() + name_idx);
|
||||
|
||||
field.add_element(6); // rate tag key offset
|
||||
field.add_element(130 + max_datasource_id + 1 + rate_idx);
|
||||
}
|
||||
{
|
||||
|
||||
// Encode the geometry for the feature
|
||||
protozero::packed_field_uint32 geometry(
|
||||
feature_writer, util::vector_tile::FEATURE_GEOMETRIES_TAG);
|
||||
encodeLinestring(tile_line, geometry, start_x, start_y);
|
||||
}
|
||||
};
|
||||
|
||||
// If this is a valid forward edge, go ahead and add it to the tile
|
||||
if (forward_duration != 0 && edge.forward_segment_id.enabled)
|
||||
{
|
||||
std::int32_t start_x = 0;
|
||||
std::int32_t start_y = 0;
|
||||
|
||||
// Calculate the speed for this line
|
||||
// Speeds are looked up in a simple 1:1 table, so the speed value == lookup
|
||||
// table index
|
||||
std::uint32_t speed_kmh_idx =
|
||||
static_cast<std::uint32_t>(round(length / forward_duration * 10 * 3.6));
|
||||
|
||||
@@ -505,19 +616,15 @@ void encodeVectorTile(const DataFacadeBase &facade,
|
||||
auto tile_line = coordinatesToTileLine(a, b, tile_bbox);
|
||||
if (!tile_line.empty())
|
||||
{
|
||||
SpeedLayerFeatureBuilder fbuilder{speeds_layer, id};
|
||||
fbuilder.add_linestring_from_container(tile_line);
|
||||
|
||||
fbuilder.set_speed(speed_kmh_idx);
|
||||
fbuilder.set_is_small(component_id.is_tiny);
|
||||
fbuilder.set_datasource(
|
||||
facade.GetDatasourceName(forward_datasource_idx).to_string());
|
||||
fbuilder.set_weight(forward_weight / 10.0);
|
||||
fbuilder.set_duration(forward_duration / 10.0);
|
||||
fbuilder.set_name(name);
|
||||
fbuilder.set_rate(forward_rate / 10.0);
|
||||
|
||||
fbuilder.commit();
|
||||
encode_tile_line(tile_line,
|
||||
speed_kmh_idx,
|
||||
line_int_index.indexOf(forward_rate),
|
||||
line_int_index.indexOf(forward_weight),
|
||||
line_int_index.indexOf(forward_duration),
|
||||
forward_datasource_idx,
|
||||
line_string_index.indexOf(name),
|
||||
start_x,
|
||||
start_y);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -525,7 +632,12 @@ void encodeVectorTile(const DataFacadeBase &facade,
|
||||
// properties
|
||||
if (reverse_duration != 0 && edge.reverse_segment_id.enabled)
|
||||
{
|
||||
std::int32_t start_x = 0;
|
||||
std::int32_t start_y = 0;
|
||||
|
||||
// Calculate the speed for this line
|
||||
// Speeds are looked up in a simple 1:1 table, so the speed value == lookup
|
||||
// table index
|
||||
std::uint32_t speed_kmh_idx =
|
||||
static_cast<std::uint32_t>(round(length / reverse_duration * 10 * 3.6));
|
||||
|
||||
@@ -538,52 +650,232 @@ void encodeVectorTile(const DataFacadeBase &facade,
|
||||
auto tile_line = coordinatesToTileLine(b, a, tile_bbox);
|
||||
if (!tile_line.empty())
|
||||
{
|
||||
SpeedLayerFeatureBuilder fbuilder{speeds_layer, id};
|
||||
fbuilder.add_linestring_from_container(tile_line);
|
||||
|
||||
fbuilder.set_speed(speed_kmh_idx);
|
||||
fbuilder.set_is_small(component_id.is_tiny);
|
||||
fbuilder.set_datasource(
|
||||
facade.GetDatasourceName(reverse_datasource_idx).to_string());
|
||||
fbuilder.set_weight(reverse_weight / 10.0);
|
||||
fbuilder.set_duration(reverse_duration / 10.0);
|
||||
fbuilder.set_name(name);
|
||||
fbuilder.set_rate(reverse_rate / 10.0);
|
||||
|
||||
fbuilder.commit();
|
||||
encode_tile_line(tile_line,
|
||||
speed_kmh_idx,
|
||||
line_int_index.indexOf(reverse_rate),
|
||||
line_int_index.indexOf(reverse_weight),
|
||||
line_int_index.indexOf(reverse_duration),
|
||||
reverse_datasource_idx,
|
||||
line_string_index.indexOf(name),
|
||||
start_x,
|
||||
start_y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Field id 3 is the "keys" attribute
|
||||
// We need two "key" fields, these are referred to with 0 and 1 (their array
|
||||
// indexes) earlier
|
||||
line_layer_writer.add_string(util::vector_tile::KEY_TAG, "speed");
|
||||
line_layer_writer.add_string(util::vector_tile::KEY_TAG, "is_small");
|
||||
line_layer_writer.add_string(util::vector_tile::KEY_TAG, "datasource");
|
||||
line_layer_writer.add_string(util::vector_tile::KEY_TAG, "weight");
|
||||
line_layer_writer.add_string(util::vector_tile::KEY_TAG, "duration");
|
||||
line_layer_writer.add_string(util::vector_tile::KEY_TAG, "name");
|
||||
line_layer_writer.add_string(util::vector_tile::KEY_TAG, "rate");
|
||||
|
||||
// Now, we write out the possible speed value arrays and possible is_tiny
|
||||
// values. Field type 4 is the "values" field. It's a variable type field,
|
||||
// so requires a two-step write (create the field, then write its value)
|
||||
for (std::size_t i = 0; i < 128; i++)
|
||||
{
|
||||
// Writing field type 4 == variant type
|
||||
protozero::pbf_writer values_writer(line_layer_writer,
|
||||
util::vector_tile::VARIANT_TAG);
|
||||
// Attribute value 5 == uint64 type
|
||||
values_writer.add_uint64(util::vector_tile::VARIANT_TYPE_UINT64, i);
|
||||
}
|
||||
{
|
||||
protozero::pbf_writer values_writer(line_layer_writer,
|
||||
util::vector_tile::VARIANT_TAG);
|
||||
// Attribute value 7 == bool type
|
||||
values_writer.add_bool(util::vector_tile::VARIANT_TYPE_BOOL, true);
|
||||
}
|
||||
{
|
||||
protozero::pbf_writer values_writer(line_layer_writer,
|
||||
util::vector_tile::VARIANT_TAG);
|
||||
// Attribute value 7 == bool type
|
||||
values_writer.add_bool(util::vector_tile::VARIANT_TYPE_BOOL, false);
|
||||
}
|
||||
for (std::size_t i = 0; i <= max_datasource_id; i++)
|
||||
{
|
||||
// Writing field type 4 == variant type
|
||||
protozero::pbf_writer values_writer(line_layer_writer,
|
||||
util::vector_tile::VARIANT_TAG);
|
||||
// Attribute value 1 == string type
|
||||
values_writer.add_string(util::vector_tile::VARIANT_TYPE_STRING,
|
||||
facade.GetDatasourceName(i).to_string());
|
||||
}
|
||||
for (auto value : line_int_index.values())
|
||||
{
|
||||
// Writing field type 4 == variant type
|
||||
protozero::pbf_writer values_writer(line_layer_writer,
|
||||
util::vector_tile::VARIANT_TAG);
|
||||
// Attribute value 2 == float type
|
||||
// Durations come out of OSRM in integer deciseconds, so we convert them
|
||||
// to seconds with a simple /10 for display
|
||||
values_writer.add_double(util::vector_tile::VARIANT_TYPE_DOUBLE, value / 10.);
|
||||
}
|
||||
|
||||
for (const auto &name : line_string_index.values())
|
||||
{
|
||||
// Writing field type 4 == variant type
|
||||
protozero::pbf_writer values_writer(line_layer_writer,
|
||||
util::vector_tile::VARIANT_TAG);
|
||||
// Attribute value 1 == string type
|
||||
values_writer.add_string(
|
||||
util::vector_tile::VARIANT_TYPE_STRING, name.data(), name.size());
|
||||
}
|
||||
}
|
||||
|
||||
// Only add the turn layer to the tile if it has some features (we sometimes won't
|
||||
// for tiles of z<16, and tiles that don't show any intersections)
|
||||
if (!all_turn_data.empty())
|
||||
{
|
||||
TurnsLayer turns_layer{tile};
|
||||
uint64_t id = 0;
|
||||
for (const auto &turn_data : all_turn_data)
|
||||
|
||||
struct EncodedTurnData
|
||||
{
|
||||
const auto tile_point = coordinatesToTilePoint(turn_data.coordinate, tile_bbox);
|
||||
if (boost::geometry::within(point_t(tile_point.x, tile_point.y), clip_box))
|
||||
util::Coordinate coordinate;
|
||||
std::size_t angle_index;
|
||||
std::size_t turn_index;
|
||||
std::size_t duration_index;
|
||||
std::size_t weight_index;
|
||||
std::size_t turntype_index;
|
||||
std::size_t turnmodifier_index;
|
||||
};
|
||||
// we need to pre-encode all values here because we need the full offsets later
|
||||
// for encoding the actual features.
|
||||
std::vector<EncodedTurnData> encoded_turn_data(all_turn_data.size());
|
||||
std::transform(
|
||||
all_turn_data.begin(),
|
||||
all_turn_data.end(),
|
||||
encoded_turn_data.begin(),
|
||||
[&](const routing_algorithms::TurnData &t) {
|
||||
auto angle_idx = point_int_index.add(t.in_angle);
|
||||
auto turn_idx = point_int_index.add(t.turn_angle);
|
||||
auto duration_idx =
|
||||
point_float_index.add(t.duration / 10.0); // Note conversion to float here
|
||||
auto weight_idx =
|
||||
point_float_index.add(t.weight / 10.0); // Note conversion to float here
|
||||
|
||||
auto turntype_idx = point_string_index.add(
|
||||
osrm::guidance::internalInstructionTypeToString(t.turn_instruction.type));
|
||||
auto turnmodifier_idx =
|
||||
point_string_index.add(osrm::guidance::instructionModifierToString(
|
||||
t.turn_instruction.direction_modifier));
|
||||
return EncodedTurnData{t.coordinate,
|
||||
angle_idx,
|
||||
turn_idx,
|
||||
duration_idx,
|
||||
weight_idx,
|
||||
turntype_idx,
|
||||
turnmodifier_idx};
|
||||
});
|
||||
|
||||
// Now write the points layer for turn penalty data:
|
||||
// Add a layer object to the PBF stream. 3=='layer' from the vector tile spec
|
||||
// (2.1)
|
||||
protozero::pbf_writer point_layer_writer(tile_writer, util::vector_tile::LAYER_TAG);
|
||||
point_layer_writer.add_uint32(util::vector_tile::VERSION_TAG, 2); // version
|
||||
point_layer_writer.add_string(util::vector_tile::NAME_TAG, "turns"); // name
|
||||
point_layer_writer.add_uint32(util::vector_tile::EXTENT_TAG,
|
||||
util::vector_tile::EXTENT); // extent
|
||||
|
||||
// Begin writing the set of point features
|
||||
{
|
||||
// Start each features with an ID starting at 1
|
||||
int id = 1;
|
||||
|
||||
// Helper function to encode a new point feature on a vector tile.
|
||||
const auto encode_tile_point = [&](const FixedPoint &tile_point,
|
||||
const auto &point_turn_data) {
|
||||
protozero::pbf_writer feature_writer(point_layer_writer,
|
||||
util::vector_tile::FEATURE_TAG);
|
||||
// Field 3 is the "geometry type" field. Value 1 is "point"
|
||||
feature_writer.add_enum(
|
||||
util::vector_tile::GEOMETRY_TAG,
|
||||
util::vector_tile::GEOMETRY_TYPE_POINT); // geometry type
|
||||
feature_writer.add_uint64(util::vector_tile::ID_TAG, id++); // id
|
||||
{
|
||||
// Write out the 4 properties we want on the feature. These
|
||||
// refer to indexes in the properties lookup table, which we
|
||||
// add to the tile after we add all features.
|
||||
protozero::packed_field_uint32 field(
|
||||
feature_writer, util::vector_tile::FEATURE_ATTRIBUTES_TAG);
|
||||
field.add_element(0); // "bearing_in" tag key offset
|
||||
field.add_element(point_turn_data.angle_index);
|
||||
field.add_element(1); // "turn_angle" tag key offset
|
||||
field.add_element(point_turn_data.turn_index);
|
||||
field.add_element(2); // "cost" tag key offset
|
||||
field.add_element(point_int_index.size() + point_turn_data.duration_index);
|
||||
field.add_element(3); // "weight" tag key offset
|
||||
field.add_element(point_int_index.size() + point_turn_data.weight_index);
|
||||
field.add_element(4); // "type" tag key offset
|
||||
field.add_element(point_int_index.size() + point_float_index.size() +
|
||||
point_turn_data.turntype_index);
|
||||
field.add_element(5); // "modifier" tag key offset
|
||||
field.add_element(point_int_index.size() + point_float_index.size() +
|
||||
point_turn_data.turnmodifier_index);
|
||||
}
|
||||
{
|
||||
// Add the geometry as the last field in this feature
|
||||
protozero::packed_field_uint32 geometry(
|
||||
feature_writer, util::vector_tile::FEATURE_GEOMETRIES_TAG);
|
||||
encodePoint(tile_point, geometry);
|
||||
}
|
||||
};
|
||||
|
||||
// Loop over all the turns we found and add them as features to the layer
|
||||
for (const auto &turndata : encoded_turn_data)
|
||||
{
|
||||
TurnsLayerFeatureBuilder fbuilder{turns_layer, ++id};
|
||||
fbuilder.add_point(tile_point);
|
||||
|
||||
fbuilder.set_bearing_in(turn_data.in_angle);
|
||||
fbuilder.set_turn_angle(turn_data.turn_angle);
|
||||
fbuilder.set_cost(turn_data.duration / 10.0);
|
||||
fbuilder.set_weight(turn_data.weight / 10.0);
|
||||
fbuilder.set_turn(turn_data.turn_instruction);
|
||||
|
||||
fbuilder.commit();
|
||||
const auto tile_point = coordinatesToTilePoint(turndata.coordinate, tile_bbox);
|
||||
if (!boost::geometry::within(point_t(tile_point.x, tile_point.y), clip_box))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
encode_tile_point(tile_point, turndata);
|
||||
}
|
||||
}
|
||||
|
||||
// Add the names of the three attributes we added to all the turn penalty
|
||||
// features previously. The indexes used there refer to these keys.
|
||||
point_layer_writer.add_string(util::vector_tile::KEY_TAG, "bearing_in");
|
||||
point_layer_writer.add_string(util::vector_tile::KEY_TAG, "turn_angle");
|
||||
point_layer_writer.add_string(util::vector_tile::KEY_TAG, "cost");
|
||||
point_layer_writer.add_string(util::vector_tile::KEY_TAG, "weight");
|
||||
point_layer_writer.add_string(util::vector_tile::KEY_TAG, "type");
|
||||
point_layer_writer.add_string(util::vector_tile::KEY_TAG, "modifier");
|
||||
|
||||
// Now, save the lists of integers and floats that our features refer to.
|
||||
for (const auto &value : point_int_index.values())
|
||||
{
|
||||
protozero::pbf_writer values_writer(point_layer_writer,
|
||||
util::vector_tile::VARIANT_TAG);
|
||||
values_writer.add_sint64(util::vector_tile::VARIANT_TYPE_SINT64, value);
|
||||
}
|
||||
for (const auto &value : point_float_index.values())
|
||||
{
|
||||
protozero::pbf_writer values_writer(point_layer_writer,
|
||||
util::vector_tile::VARIANT_TAG);
|
||||
values_writer.add_float(util::vector_tile::VARIANT_TYPE_FLOAT, value);
|
||||
}
|
||||
for (const auto &value : point_string_index.values())
|
||||
{
|
||||
protozero::pbf_writer values_writer(point_layer_writer,
|
||||
util::vector_tile::VARIANT_TAG);
|
||||
values_writer.add_string(util::vector_tile::VARIANT_TYPE_STRING, value);
|
||||
}
|
||||
}
|
||||
|
||||
// OSM Node tile layer
|
||||
{
|
||||
protozero::pbf_writer point_layer_writer(tile_writer, util::vector_tile::LAYER_TAG);
|
||||
point_layer_writer.add_uint32(util::vector_tile::VERSION_TAG, 2); // version
|
||||
point_layer_writer.add_string(util::vector_tile::NAME_TAG, "osmnodes"); // name
|
||||
point_layer_writer.add_uint32(util::vector_tile::EXTENT_TAG,
|
||||
util::vector_tile::EXTENT); // extent
|
||||
|
||||
std::vector<NodeID> internal_nodes;
|
||||
internal_nodes.reserve(edges.size() * 2);
|
||||
for (const auto &edge : edges)
|
||||
@@ -595,8 +887,6 @@ void encodeVectorTile(const DataFacadeBase &facade,
|
||||
auto new_end = std::unique(internal_nodes.begin(), internal_nodes.end());
|
||||
internal_nodes.resize(new_end - internal_nodes.begin());
|
||||
|
||||
vtzero::layer_builder osmnodes_layer{tile, "osmnodes"};
|
||||
|
||||
for (const auto &internal_node : internal_nodes)
|
||||
{
|
||||
const auto coord = facade.GetCoordinateOfNode(internal_node);
|
||||
@@ -605,19 +895,32 @@ void encodeVectorTile(const DataFacadeBase &facade,
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
vtzero::point_feature_builder fbuilder{osmnodes_layer};
|
||||
fbuilder.set_id(
|
||||
static_cast<OSMNodeID::value_type>(facade.GetOSMNodeIDOfNode(internal_node)));
|
||||
fbuilder.add_point(tile_point);
|
||||
fbuilder.commit();
|
||||
protozero::pbf_writer feature_writer(point_layer_writer,
|
||||
util::vector_tile::FEATURE_TAG);
|
||||
// Field 3 is the "geometry type" field. Value 1 is "point"
|
||||
feature_writer.add_enum(util::vector_tile::GEOMETRY_TAG,
|
||||
util::vector_tile::GEOMETRY_TYPE_POINT); // geometry type
|
||||
const auto osmid =
|
||||
static_cast<OSMNodeID::value_type>(facade.GetOSMNodeIDOfNode(internal_node));
|
||||
feature_writer.add_uint64(util::vector_tile::ID_TAG, osmid); // id
|
||||
// There are no additional properties, just the ID and the geometry
|
||||
{
|
||||
// Add the geometry as the last field in this feature
|
||||
protozero::packed_field_uint32 geometry(
|
||||
feature_writer, util::vector_tile::FEATURE_GEOMETRIES_TAG);
|
||||
encodePoint(tile_point, geometry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Internal nodes tile layer
|
||||
{
|
||||
vtzero::layer_builder internal_nodes_layer{tile, "internal-nodes"};
|
||||
protozero::pbf_writer line_layer_writer(tile_writer, util::vector_tile::LAYER_TAG);
|
||||
line_layer_writer.add_uint32(util::vector_tile::VERSION_TAG, 2); // version
|
||||
line_layer_writer.add_string(util::vector_tile::NAME_TAG, "internal-nodes"); // name
|
||||
line_layer_writer.add_uint32(util::vector_tile::EXTENT_TAG,
|
||||
util::vector_tile::EXTENT); // extent
|
||||
|
||||
unsigned id = 0;
|
||||
for (auto edgeNodeID : segregated_nodes)
|
||||
{
|
||||
auto const geomIndex = facade.GetGeometryIndex(edgeNodeID);
|
||||
@@ -634,21 +937,46 @@ void encodeVectorTile(const DataFacadeBase &facade,
|
||||
points.push_back(facade.GetCoordinateOfNode(nodeID));
|
||||
}
|
||||
|
||||
const auto encode_tile_line = [&line_layer_writer, &id](
|
||||
const FixedLine &tile_line, std::int32_t &start_x, std::int32_t &start_y) {
|
||||
|
||||
protozero::pbf_writer feature_writer(line_layer_writer,
|
||||
util::vector_tile::FEATURE_TAG);
|
||||
|
||||
feature_writer.add_enum(util::vector_tile::GEOMETRY_TAG,
|
||||
util::vector_tile::GEOMETRY_TYPE_LINE); // geometry type
|
||||
|
||||
feature_writer.add_uint64(util::vector_tile::ID_TAG, id++); // id
|
||||
{
|
||||
|
||||
protozero::packed_field_uint32 field(
|
||||
feature_writer, util::vector_tile::FEATURE_ATTRIBUTES_TAG);
|
||||
}
|
||||
{
|
||||
|
||||
// Encode the geometry for the feature
|
||||
protozero::packed_field_uint32 geometry(
|
||||
feature_writer, util::vector_tile::FEATURE_GEOMETRIES_TAG);
|
||||
encodeLinestring(tile_line, geometry, start_x, start_y);
|
||||
}
|
||||
};
|
||||
|
||||
std::int32_t start_x = 0;
|
||||
std::int32_t start_y = 0;
|
||||
|
||||
auto tile_lines = coordinatesToTileLine(points, tile_bbox);
|
||||
if (!tile_lines.empty())
|
||||
{
|
||||
vtzero::linestring_feature_builder fbuilder{internal_nodes_layer};
|
||||
for (auto const &tile_line : tile_lines)
|
||||
for (auto const &tl : tile_lines)
|
||||
{
|
||||
fbuilder.add_linestring_from_container(tile_line);
|
||||
encode_tile_line(tl, start_x, start_y);
|
||||
}
|
||||
fbuilder.commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tile.serialize(pbf_buffer);
|
||||
// protozero serializes data during object destructors, so once the scope closes,
|
||||
// our result buffer will have all the tile data encoded into it.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+19
-24
@@ -131,7 +131,7 @@ void ManipulateTableForFSE(const std::size_t source_id,
|
||||
result_table.SetValue(destination_id, i, INVALID_EDGE_WEIGHT);
|
||||
}
|
||||
|
||||
// set destination->source to zero so roundtrip treats source and
|
||||
// set destination->source to zero so rountrip treats source and
|
||||
// destination as one location
|
||||
result_table.SetValue(destination_id, source_id, 0);
|
||||
|
||||
@@ -216,66 +216,61 @@ Status TripPlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms,
|
||||
BOOST_ASSERT(snapped_phantoms.size() == number_of_locations);
|
||||
|
||||
// compute the duration table of all phantom nodes
|
||||
auto result_duration_table = util::DistTableWrapper<EdgeWeight>(
|
||||
algorithms
|
||||
.ManyToManySearch(
|
||||
snapped_phantoms, {}, {}, /*requestDistance*/ false, /*requestDuration*/ true)
|
||||
.first,
|
||||
number_of_locations);
|
||||
auto result_table = util::DistTableWrapper<EdgeWeight>(
|
||||
algorithms.ManyToManySearch(snapped_phantoms, {}, {}), number_of_locations);
|
||||
|
||||
if (result_duration_table.size() == 0)
|
||||
if (result_table.size() == 0)
|
||||
{
|
||||
return Status::Error;
|
||||
}
|
||||
|
||||
const constexpr std::size_t BF_MAX_FEASABLE = 10;
|
||||
BOOST_ASSERT_MSG(result_duration_table.size() == number_of_locations * number_of_locations,
|
||||
BOOST_ASSERT_MSG(result_table.size() == number_of_locations * number_of_locations,
|
||||
"Distance Table has wrong size");
|
||||
|
||||
if (!IsStronglyConnectedComponent(result_duration_table))
|
||||
if (!IsStronglyConnectedComponent(result_table))
|
||||
{
|
||||
return Error("NoTrips", "No trip visiting all destinations possible.", json_result);
|
||||
}
|
||||
|
||||
if (fixed_start && fixed_end)
|
||||
{
|
||||
ManipulateTableForFSE(source_id, destination_id, result_duration_table);
|
||||
ManipulateTableForFSE(source_id, destination_id, result_table);
|
||||
}
|
||||
|
||||
std::vector<NodeID> duration_trip;
|
||||
duration_trip.reserve(number_of_locations);
|
||||
std::vector<NodeID> trip;
|
||||
trip.reserve(number_of_locations);
|
||||
// get an optimized order in which the destinations should be visited
|
||||
if (number_of_locations < BF_MAX_FEASABLE)
|
||||
{
|
||||
duration_trip = trip::BruteForceTrip(number_of_locations, result_duration_table);
|
||||
trip = trip::BruteForceTrip(number_of_locations, result_table);
|
||||
}
|
||||
else
|
||||
{
|
||||
duration_trip = trip::FarthestInsertionTrip(number_of_locations, result_duration_table);
|
||||
trip = trip::FarthestInsertionTrip(number_of_locations, result_table);
|
||||
}
|
||||
|
||||
// rotate result such that roundtrip starts at node with index 0
|
||||
// thist first if covers scenarios: !fixed_end || fixed_start || (fixed_start && fixed_end)
|
||||
if (!fixed_end || fixed_start)
|
||||
{
|
||||
auto desired_start_index = std::find(std::begin(duration_trip), std::end(duration_trip), 0);
|
||||
BOOST_ASSERT(desired_start_index != std::end(duration_trip));
|
||||
std::rotate(std::begin(duration_trip), desired_start_index, std::end(duration_trip));
|
||||
auto desired_start_index = std::find(std::begin(trip), std::end(trip), 0);
|
||||
BOOST_ASSERT(desired_start_index != std::end(trip));
|
||||
std::rotate(std::begin(trip), desired_start_index, std::end(trip));
|
||||
}
|
||||
else if (fixed_end && !fixed_start && parameters.roundtrip)
|
||||
{
|
||||
auto desired_start_index =
|
||||
std::find(std::begin(duration_trip), std::end(duration_trip), destination_id);
|
||||
BOOST_ASSERT(desired_start_index != std::end(duration_trip));
|
||||
std::rotate(std::begin(duration_trip), desired_start_index, std::end(duration_trip));
|
||||
auto desired_start_index = std::find(std::begin(trip), std::end(trip), destination_id);
|
||||
BOOST_ASSERT(desired_start_index != std::end(trip));
|
||||
std::rotate(std::begin(trip), desired_start_index, std::end(trip));
|
||||
}
|
||||
|
||||
// get the route when visiting all destinations in optimized order
|
||||
InternalRouteResult route =
|
||||
ComputeRoute(algorithms, snapped_phantoms, duration_trip, parameters.roundtrip);
|
||||
ComputeRoute(algorithms, snapped_phantoms, trip, parameters.roundtrip);
|
||||
|
||||
// get api response
|
||||
const std::vector<std::vector<NodeID>> trips = {duration_trip};
|
||||
const std::vector<std::vector<NodeID>> trips = {trip};
|
||||
const std::vector<InternalRouteResult> routes = {route};
|
||||
api::TripAPI trip_api{facade, parameters};
|
||||
trip_api.MakeResponse(trips, routes, snapped_phantoms, json_result);
|
||||
|
||||
@@ -470,10 +470,10 @@ RandIt filterUnpackedPathsBySharing(RandIt first,
|
||||
if (shortest_path.edges.empty())
|
||||
return last;
|
||||
|
||||
std::unordered_set<NodeID> nodes;
|
||||
nodes.reserve(size * shortest_path.nodes.size() * (1.25));
|
||||
std::unordered_set<EdgeID> edges;
|
||||
edges.reserve(size * shortest_path.edges.size() * (1.25));
|
||||
|
||||
nodes.insert(begin(shortest_path.nodes), end(shortest_path.nodes));
|
||||
edges.insert(begin(shortest_path.edges), end(shortest_path.edges));
|
||||
|
||||
const auto over_sharing_limit = [&](auto &unpacked) {
|
||||
if (unpacked.edges.empty())
|
||||
@@ -482,20 +482,20 @@ RandIt filterUnpackedPathsBySharing(RandIt first,
|
||||
}
|
||||
|
||||
EdgeWeight total_duration = 0;
|
||||
const auto add_if_seen = [&](const EdgeWeight duration, const NodeID node) {
|
||||
auto node_duration = facade.GetNodeDuration(node);
|
||||
total_duration += node_duration;
|
||||
if (nodes.count(node) > 0)
|
||||
const auto add_if_seen = [&](const EdgeWeight duration, const EdgeID edge) {
|
||||
auto edge_duration = facade.GetEdgeData(edge).duration;
|
||||
total_duration += edge_duration;
|
||||
if (edges.count(edge) > 0)
|
||||
{
|
||||
return duration + node_duration;
|
||||
return duration + edge_duration;
|
||||
}
|
||||
return duration;
|
||||
};
|
||||
|
||||
const auto shared_duration = std::accumulate(
|
||||
begin(unpacked.nodes), end(unpacked.nodes), EdgeDuration{0}, add_if_seen);
|
||||
const auto shared_weight =
|
||||
std::accumulate(begin(unpacked.edges), end(unpacked.edges), EdgeWeight{0}, add_if_seen);
|
||||
|
||||
unpacked.sharing = shared_duration / static_cast<double>(total_duration);
|
||||
unpacked.sharing = shared_weight / static_cast<double>(total_duration);
|
||||
BOOST_ASSERT(unpacked.sharing >= 0.);
|
||||
BOOST_ASSERT(unpacked.sharing <= 1.);
|
||||
|
||||
@@ -505,7 +505,7 @@ RandIt filterUnpackedPathsBySharing(RandIt first,
|
||||
}
|
||||
else
|
||||
{
|
||||
nodes.insert(begin(unpacked.nodes), end(unpacked.nodes));
|
||||
edges.insert(begin(unpacked.edges), end(unpacked.edges));
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "engine/routing_algorithms/direct_shortest_path.hpp"
|
||||
|
||||
#include "engine/routing_algorithms/routing_base.hpp"
|
||||
#include "engine/routing_algorithms/routing_base_ch.hpp"
|
||||
#include "engine/routing_algorithms/routing_base_mld.hpp"
|
||||
|
||||
@@ -60,8 +60,8 @@ void relaxOutgoingEdges(const DataFacade<Algorithm> &facade,
|
||||
if (DIRECTION == FORWARD_DIRECTION ? data.forward : data.backward)
|
||||
{
|
||||
const NodeID to = facade.GetTarget(edge);
|
||||
const auto edge_weight = data.weight;
|
||||
|
||||
const auto edge_weight = data.weight;
|
||||
const auto edge_duration = data.duration;
|
||||
|
||||
BOOST_ASSERT_MSG(edge_weight > 0, "edge_weight invalid");
|
||||
@@ -85,13 +85,12 @@ void relaxOutgoingEdges(const DataFacade<Algorithm> &facade,
|
||||
}
|
||||
|
||||
void forwardRoutingStep(const DataFacade<Algorithm> &facade,
|
||||
const std::size_t row_index,
|
||||
const std::size_t number_of_targets,
|
||||
const unsigned row_idx,
|
||||
const unsigned number_of_targets,
|
||||
typename SearchEngineData<Algorithm>::ManyToManyQueryHeap &query_heap,
|
||||
const std::vector<NodeBucket> &search_space_with_buckets,
|
||||
std::vector<EdgeWeight> &weights_table,
|
||||
std::vector<EdgeDuration> &durations_table,
|
||||
std::vector<NodeID> &middle_nodes_table,
|
||||
const PhantomNode &phantom_node)
|
||||
{
|
||||
const auto node = query_heap.DeleteMin();
|
||||
@@ -106,12 +105,12 @@ void forwardRoutingStep(const DataFacade<Algorithm> &facade,
|
||||
for (const auto ¤t_bucket : boost::make_iterator_range(bucket_list))
|
||||
{
|
||||
// Get target id from bucket entry
|
||||
const auto column_index = current_bucket.column_index;
|
||||
const auto column_idx = current_bucket.column_index;
|
||||
const auto target_weight = current_bucket.weight;
|
||||
const auto target_duration = current_bucket.duration;
|
||||
|
||||
auto ¤t_weight = weights_table[row_index * number_of_targets + column_index];
|
||||
auto ¤t_duration = durations_table[row_index * number_of_targets + column_index];
|
||||
auto ¤t_weight = weights_table[row_idx * number_of_targets + column_idx];
|
||||
auto ¤t_duration = durations_table[row_idx * number_of_targets + column_idx];
|
||||
|
||||
// Check if new weight is better
|
||||
auto new_weight = source_weight + target_weight;
|
||||
@@ -123,14 +122,12 @@ void forwardRoutingStep(const DataFacade<Algorithm> &facade,
|
||||
{
|
||||
current_weight = std::min(current_weight, new_weight);
|
||||
current_duration = std::min(current_duration, new_duration);
|
||||
middle_nodes_table[row_index * number_of_targets + column_index] = node;
|
||||
}
|
||||
}
|
||||
else if (std::tie(new_weight, new_duration) < std::tie(current_weight, current_duration))
|
||||
{
|
||||
current_weight = new_weight;
|
||||
current_duration = new_duration;
|
||||
middle_nodes_table[row_index * number_of_targets + column_index] = node;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,7 +136,7 @@ void forwardRoutingStep(const DataFacade<Algorithm> &facade,
|
||||
}
|
||||
|
||||
void backwardRoutingStep(const DataFacade<Algorithm> &facade,
|
||||
const unsigned column_index,
|
||||
const unsigned column_idx,
|
||||
typename SearchEngineData<Algorithm>::ManyToManyQueryHeap &query_heap,
|
||||
std::vector<NodeBucket> &search_space_with_buckets,
|
||||
const PhantomNode &phantom_node)
|
||||
@@ -147,11 +144,9 @@ void backwardRoutingStep(const DataFacade<Algorithm> &facade,
|
||||
const auto node = query_heap.DeleteMin();
|
||||
const auto target_weight = query_heap.GetKey(node);
|
||||
const auto target_duration = query_heap.GetData(node).duration;
|
||||
const auto parent = query_heap.GetData(node).parent;
|
||||
|
||||
// Store settled nodes in search space bucket
|
||||
search_space_with_buckets.emplace_back(
|
||||
node, parent, column_index, target_weight, target_duration);
|
||||
search_space_with_buckets.emplace_back(node, column_idx, target_weight, target_duration);
|
||||
|
||||
relaxOutgoingEdges<REVERSE_DIRECTION>(
|
||||
facade, node, target_weight, target_duration, query_heap, phantom_node);
|
||||
@@ -159,125 +154,26 @@ void backwardRoutingStep(const DataFacade<Algorithm> &facade,
|
||||
|
||||
} // namespace ch
|
||||
|
||||
void retrievePackedPathFromSearchSpace(const NodeID middle_node_id,
|
||||
const unsigned column_index,
|
||||
const std::vector<NodeBucket> &search_space_with_buckets,
|
||||
std::vector<NodeID> &packed_leg)
|
||||
{
|
||||
auto bucket_list = std::equal_range(search_space_with_buckets.begin(),
|
||||
search_space_with_buckets.end(),
|
||||
middle_node_id,
|
||||
NodeBucket::ColumnCompare(column_index));
|
||||
|
||||
NodeID current_node_id = middle_node_id;
|
||||
|
||||
BOOST_ASSERT_MSG(std::distance(bucket_list.first, bucket_list.second) == 1,
|
||||
"The pointers are not pointing to the same element.");
|
||||
|
||||
while (bucket_list.first->parent_node != current_node_id &&
|
||||
bucket_list.first != search_space_with_buckets.end())
|
||||
{
|
||||
current_node_id = bucket_list.first->parent_node;
|
||||
|
||||
packed_leg.emplace_back(current_node_id);
|
||||
|
||||
bucket_list = std::equal_range(search_space_with_buckets.begin(),
|
||||
search_space_with_buckets.end(),
|
||||
current_node_id,
|
||||
NodeBucket::ColumnCompare(column_index));
|
||||
}
|
||||
}
|
||||
|
||||
void calculateDistances(typename SearchEngineData<ch::Algorithm>::ManyToManyQueryHeap &query_heap,
|
||||
const DataFacade<ch::Algorithm> &facade,
|
||||
const std::vector<PhantomNode> &phantom_nodes,
|
||||
const std::vector<std::size_t> &target_indices,
|
||||
const std::size_t row_index,
|
||||
const std::size_t source_index,
|
||||
const PhantomNode &source_phantom,
|
||||
const std::size_t number_of_targets,
|
||||
const std::vector<NodeBucket> &search_space_with_buckets,
|
||||
std::vector<EdgeDistance> &distances_table,
|
||||
const std::vector<NodeID> &middle_nodes_table)
|
||||
{
|
||||
std::vector<NodeID> packed_leg;
|
||||
|
||||
for (auto column_index : util::irange<std::size_t>(0, number_of_targets))
|
||||
{
|
||||
const auto target_index = target_indices[column_index];
|
||||
const auto &target_phantom = phantom_nodes[target_index];
|
||||
|
||||
if (source_index == target_index)
|
||||
{
|
||||
distances_table[row_index * number_of_targets + column_index] = 0.0;
|
||||
continue;
|
||||
}
|
||||
|
||||
NodeID middle_node_id = middle_nodes_table[row_index * number_of_targets + column_index];
|
||||
|
||||
if (middle_node_id == SPECIAL_NODEID) // takes care of one-ways
|
||||
{
|
||||
distances_table[row_index * number_of_targets + column_index] = INVALID_EDGE_DISTANCE;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Step 1: Find path from source to middle node
|
||||
ch::retrievePackedPathFromSingleManyToManyHeap(query_heap, middle_node_id, packed_leg);
|
||||
std::reverse(packed_leg.begin(), packed_leg.end());
|
||||
|
||||
packed_leg.push_back(middle_node_id);
|
||||
|
||||
// Step 2: Find path from middle to target node
|
||||
retrievePackedPathFromSearchSpace(
|
||||
middle_node_id, column_index, search_space_with_buckets, packed_leg);
|
||||
|
||||
if (packed_leg.size() == 1 && (needsLoopForward(source_phantom, target_phantom) ||
|
||||
needsLoopBackwards(source_phantom, target_phantom)))
|
||||
{
|
||||
auto weight = ch::getLoopWeight<false>(facade, packed_leg.front());
|
||||
if (weight != INVALID_EDGE_WEIGHT)
|
||||
packed_leg.push_back(packed_leg.front());
|
||||
}
|
||||
if (!packed_leg.empty())
|
||||
{
|
||||
EdgeDistance annotation =
|
||||
ch::calculateEBGNodeAnnotations(facade, packed_leg.begin(), packed_leg.end());
|
||||
annotation = adjustPathDistanceToPhantomNodes(
|
||||
packed_leg, source_phantom, target_phantom, annotation);
|
||||
|
||||
distances_table[row_index * number_of_targets + column_index] = annotation;
|
||||
}
|
||||
packed_leg.clear();
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
|
||||
manyToManySearch(SearchEngineData<ch::Algorithm> &engine_working_data,
|
||||
const DataFacade<ch::Algorithm> &facade,
|
||||
const std::vector<PhantomNode> &phantom_nodes,
|
||||
const std::vector<std::size_t> &source_indices,
|
||||
const std::vector<std::size_t> &target_indices,
|
||||
const bool calculate_distance,
|
||||
const bool calculate_duration)
|
||||
std::vector<EdgeDuration> manyToManySearch(SearchEngineData<ch::Algorithm> &engine_working_data,
|
||||
const DataFacade<ch::Algorithm> &facade,
|
||||
const std::vector<PhantomNode> &phantom_nodes,
|
||||
const std::vector<std::size_t> &source_indices,
|
||||
const std::vector<std::size_t> &target_indices)
|
||||
{
|
||||
(void)calculate_duration; // TODO: stub to use when computing durations become optional
|
||||
|
||||
const auto number_of_sources = source_indices.size();
|
||||
const auto number_of_targets = target_indices.size();
|
||||
const auto number_of_entries = number_of_sources * number_of_targets;
|
||||
|
||||
std::vector<EdgeWeight> weights_table(number_of_entries, INVALID_EDGE_WEIGHT);
|
||||
std::vector<EdgeDuration> durations_table(number_of_entries, MAXIMAL_EDGE_DURATION);
|
||||
std::vector<EdgeDistance> distances_table;
|
||||
std::vector<NodeID> middle_nodes_table(number_of_entries, SPECIAL_NODEID);
|
||||
|
||||
std::vector<NodeBucket> search_space_with_buckets;
|
||||
|
||||
// Populate buckets with paths from all accessible nodes to destinations via backward searches
|
||||
for (std::uint32_t column_index = 0; column_index < target_indices.size(); ++column_index)
|
||||
for (std::uint32_t column_idx = 0; column_idx < target_indices.size(); ++column_idx)
|
||||
{
|
||||
const auto index = target_indices[column_index];
|
||||
const auto index = target_indices[column_idx];
|
||||
const auto &phantom = phantom_nodes[index];
|
||||
|
||||
engine_working_data.InitializeOrClearManyToManyThreadLocalStorage(
|
||||
@@ -288,8 +184,7 @@ manyToManySearch(SearchEngineData<ch::Algorithm> &engine_working_data,
|
||||
// Explore search space
|
||||
while (!query_heap.Empty())
|
||||
{
|
||||
backwardRoutingStep(
|
||||
facade, column_index, query_heap, search_space_with_buckets, phantom);
|
||||
backwardRoutingStep(facade, column_idx, query_heap, search_space_with_buckets, phantom);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,49 +192,32 @@ manyToManySearch(SearchEngineData<ch::Algorithm> &engine_working_data,
|
||||
std::sort(search_space_with_buckets.begin(), search_space_with_buckets.end());
|
||||
|
||||
// Find shortest paths from sources to all accessible nodes
|
||||
for (std::uint32_t row_index = 0; row_index < source_indices.size(); ++row_index)
|
||||
for (std::uint32_t row_idx = 0; row_idx < source_indices.size(); ++row_idx)
|
||||
{
|
||||
const auto source_index = source_indices[row_index];
|
||||
const auto &source_phantom = phantom_nodes[source_index];
|
||||
const auto index = source_indices[row_idx];
|
||||
const auto &phantom = phantom_nodes[index];
|
||||
|
||||
// Clear heap and insert source nodes
|
||||
engine_working_data.InitializeOrClearManyToManyThreadLocalStorage(
|
||||
facade.GetNumberOfNodes());
|
||||
auto &query_heap = *(engine_working_data.many_to_many_heap);
|
||||
insertSourceInHeap(query_heap, source_phantom);
|
||||
insertSourceInHeap(query_heap, phantom);
|
||||
|
||||
// Explore search space
|
||||
while (!query_heap.Empty())
|
||||
{
|
||||
forwardRoutingStep(facade,
|
||||
row_index,
|
||||
row_idx,
|
||||
number_of_targets,
|
||||
query_heap,
|
||||
search_space_with_buckets,
|
||||
weights_table,
|
||||
durations_table,
|
||||
middle_nodes_table,
|
||||
source_phantom);
|
||||
}
|
||||
|
||||
if (calculate_distance)
|
||||
{
|
||||
distances_table.resize(number_of_entries, INVALID_EDGE_DISTANCE);
|
||||
calculateDistances(query_heap,
|
||||
facade,
|
||||
phantom_nodes,
|
||||
target_indices,
|
||||
row_index,
|
||||
source_index,
|
||||
source_phantom,
|
||||
number_of_targets,
|
||||
search_space_with_buckets,
|
||||
distances_table,
|
||||
middle_nodes_table);
|
||||
phantom);
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_pair(durations_table, distances_table);
|
||||
return durations_table;
|
||||
}
|
||||
|
||||
} // namespace routing_algorithms
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "engine/routing_algorithms/many_to_many.hpp"
|
||||
#include "engine/routing_algorithms/routing_base_mld.hpp"
|
||||
#include "engine/routing_algorithms/routing_base.hpp"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/range/iterator_range_core.hpp>
|
||||
@@ -19,8 +19,22 @@ namespace routing_algorithms
|
||||
namespace mld
|
||||
{
|
||||
|
||||
using PackedEdge = std::tuple</*from*/ NodeID, /*to*/ NodeID, /*from_clique_arc*/ bool>;
|
||||
using PackedPath = std::vector<PackedEdge>;
|
||||
template <typename MultiLevelPartition>
|
||||
inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition,
|
||||
const NodeID node,
|
||||
const PhantomNode &phantom_node)
|
||||
{
|
||||
auto highest_diffrent_level = [&partition, node](const SegmentID &phantom_node) {
|
||||
if (phantom_node.enabled)
|
||||
return partition.GetHighestDifferentLevel(phantom_node.id, node);
|
||||
return INVALID_LEVEL_ID;
|
||||
};
|
||||
|
||||
const auto node_level = std::min(highest_diffrent_level(phantom_node.forward_segment_id),
|
||||
highest_diffrent_level(phantom_node.reverse_segment_id));
|
||||
|
||||
return node_level;
|
||||
}
|
||||
|
||||
template <typename MultiLevelPartition>
|
||||
inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition,
|
||||
@@ -36,6 +50,38 @@ inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition,
|
||||
return node_level;
|
||||
}
|
||||
|
||||
template <typename MultiLevelPartition>
|
||||
inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition,
|
||||
NodeID node,
|
||||
const std::vector<PhantomNode> &phantom_nodes,
|
||||
const std::size_t phantom_index,
|
||||
const std::vector<std::size_t> &phantom_indices)
|
||||
{
|
||||
auto min_level = [&partition, node](const PhantomNode &phantom_node) {
|
||||
|
||||
const auto &forward_segment = phantom_node.forward_segment_id;
|
||||
const auto forward_level =
|
||||
forward_segment.enabled ? partition.GetHighestDifferentLevel(node, forward_segment.id)
|
||||
: INVALID_LEVEL_ID;
|
||||
|
||||
const auto &reverse_segment = phantom_node.reverse_segment_id;
|
||||
const auto reverse_level =
|
||||
reverse_segment.enabled ? partition.GetHighestDifferentLevel(node, reverse_segment.id)
|
||||
: INVALID_LEVEL_ID;
|
||||
|
||||
return std::min(forward_level, reverse_level);
|
||||
};
|
||||
|
||||
// Get minimum level over all phantoms of the highest different level with respect to node
|
||||
// This is equivalent to min_{∀ source, target} partition.GetQueryLevel(source, node, target)
|
||||
auto result = min_level(phantom_nodes[phantom_index]);
|
||||
for (const auto &index : phantom_indices)
|
||||
{
|
||||
result = std::min(result, min_level(phantom_nodes[index]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <bool DIRECTION, typename... Args>
|
||||
void relaxOutgoingEdges(const DataFacade<mld::Algorithm> &facade,
|
||||
const NodeID node,
|
||||
@@ -79,10 +125,8 @@ void relaxOutgoingEdges(const DataFacade<mld::Algorithm> &facade,
|
||||
{
|
||||
query_heap.Insert(to, to_weight, {node, true, to_duration});
|
||||
}
|
||||
else if (std::tie(to_weight, to_duration, node) <
|
||||
std::tie(query_heap.GetKey(to),
|
||||
query_heap.GetData(to).duration,
|
||||
query_heap.GetData(to).parent))
|
||||
else if (std::tie(to_weight, to_duration) <
|
||||
std::tie(query_heap.GetKey(to), query_heap.GetData(to).duration))
|
||||
{
|
||||
query_heap.GetData(to) = {node, true, to_duration};
|
||||
query_heap.DecreaseKey(to, to_weight);
|
||||
@@ -111,10 +155,8 @@ void relaxOutgoingEdges(const DataFacade<mld::Algorithm> &facade,
|
||||
{
|
||||
query_heap.Insert(to, to_weight, {node, true, to_duration});
|
||||
}
|
||||
else if (std::tie(to_weight, to_duration, node) <
|
||||
std::tie(query_heap.GetKey(to),
|
||||
query_heap.GetData(to).duration,
|
||||
query_heap.GetData(to).parent))
|
||||
else if (std::tie(to_weight, to_duration) <
|
||||
std::tie(query_heap.GetKey(to), query_heap.GetData(to).duration))
|
||||
{
|
||||
query_heap.GetData(to) = {node, true, to_duration};
|
||||
query_heap.DecreaseKey(to, to_weight);
|
||||
@@ -130,8 +172,7 @@ void relaxOutgoingEdges(const DataFacade<mld::Algorithm> &facade,
|
||||
for (const auto edge : facade.GetBorderEdgeRange(level, node))
|
||||
{
|
||||
const auto &data = facade.GetEdgeData(edge);
|
||||
if ((DIRECTION == FORWARD_DIRECTION) ? facade.IsForwardEdge(edge)
|
||||
: facade.IsBackwardEdge(edge))
|
||||
if (DIRECTION == FORWARD_DIRECTION ? data.forward : data.backward)
|
||||
{
|
||||
const NodeID to = facade.GetTarget(edge);
|
||||
if (facade.ExcludeNode(to))
|
||||
@@ -139,16 +180,12 @@ void relaxOutgoingEdges(const DataFacade<mld::Algorithm> &facade,
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto turn_id = data.turn_id;
|
||||
const auto node_id = DIRECTION == FORWARD_DIRECTION ? node : facade.GetTarget(edge);
|
||||
const auto node_weight = facade.GetNodeWeight(node_id);
|
||||
const auto node_duration = facade.GetNodeDuration(node_id);
|
||||
const auto turn_weight = node_weight + facade.GetWeightPenaltyForEdgeID(turn_id);
|
||||
const auto turn_duration = node_duration + facade.GetDurationPenaltyForEdgeID(turn_id);
|
||||
const auto edge_weight = data.weight;
|
||||
const auto edge_duration = data.duration;
|
||||
|
||||
BOOST_ASSERT_MSG(node_weight + turn_weight > 0, "edge weight is invalid");
|
||||
const auto to_weight = weight + turn_weight;
|
||||
const auto to_duration = duration + turn_duration;
|
||||
BOOST_ASSERT_MSG(edge_weight > 0, "edge_weight invalid");
|
||||
const auto to_weight = weight + edge_weight;
|
||||
const auto to_duration = duration + edge_duration;
|
||||
|
||||
// New Node discovered -> Add to Heap + Node Info Storage
|
||||
if (!query_heap.WasInserted(to))
|
||||
@@ -156,10 +193,8 @@ void relaxOutgoingEdges(const DataFacade<mld::Algorithm> &facade,
|
||||
query_heap.Insert(to, to_weight, {node, false, to_duration});
|
||||
}
|
||||
// Found a shorter Path -> Update weight and set new parent
|
||||
else if (std::tie(to_weight, to_duration, node) <
|
||||
std::tie(query_heap.GetKey(to),
|
||||
query_heap.GetData(to).duration,
|
||||
query_heap.GetData(to).parent))
|
||||
else if (std::tie(to_weight, to_duration) <
|
||||
std::tie(query_heap.GetKey(to), query_heap.GetData(to).duration))
|
||||
{
|
||||
query_heap.GetData(to) = {node, false, to_duration};
|
||||
query_heap.DecreaseKey(to, to_weight);
|
||||
@@ -172,18 +207,14 @@ void relaxOutgoingEdges(const DataFacade<mld::Algorithm> &facade,
|
||||
// Unidirectional multi-layer Dijkstra search for 1-to-N and N-to-1 matrices
|
||||
//
|
||||
template <bool DIRECTION>
|
||||
std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
|
||||
oneToManySearch(SearchEngineData<Algorithm> &engine_working_data,
|
||||
const DataFacade<Algorithm> &facade,
|
||||
const std::vector<PhantomNode> &phantom_nodes,
|
||||
std::size_t phantom_index,
|
||||
const std::vector<std::size_t> &phantom_indices,
|
||||
const bool calculate_distance)
|
||||
std::vector<EdgeDuration> oneToManySearch(SearchEngineData<Algorithm> &engine_working_data,
|
||||
const DataFacade<Algorithm> &facade,
|
||||
const std::vector<PhantomNode> &phantom_nodes,
|
||||
std::size_t phantom_index,
|
||||
const std::vector<std::size_t> &phantom_indices)
|
||||
{
|
||||
std::vector<EdgeWeight> weights(phantom_indices.size(), INVALID_EDGE_WEIGHT);
|
||||
std::vector<EdgeDuration> durations(phantom_indices.size(), MAXIMAL_EDGE_DURATION);
|
||||
std::vector<EdgeDistance> distances_table;
|
||||
std::vector<NodeID> middle_nodes_table(phantom_indices.size(), SPECIAL_NODEID);
|
||||
|
||||
// Collect destination (source) nodes into a map
|
||||
std::unordered_multimap<NodeID, std::tuple<std::size_t, EdgeWeight, EdgeDuration>>
|
||||
@@ -251,7 +282,6 @@ oneToManySearch(SearchEngineData<Algorithm> &engine_working_data,
|
||||
{
|
||||
weights[index] = path_weight;
|
||||
durations[index] = path_duration;
|
||||
middle_nodes_table[index] = node;
|
||||
}
|
||||
|
||||
// Remove node from destinations list
|
||||
@@ -264,36 +294,21 @@ oneToManySearch(SearchEngineData<Algorithm> &engine_working_data,
|
||||
}
|
||||
};
|
||||
|
||||
// Check a single path result and insert adjacent nodes into heap
|
||||
auto insert_node = [&](NodeID node, EdgeWeight initial_weight, EdgeDuration initial_duration) {
|
||||
|
||||
// Update single node paths
|
||||
update_values(node, initial_weight, initial_duration);
|
||||
|
||||
query_heap.Insert(node, initial_weight, {node, initial_duration});
|
||||
|
||||
// Place adjacent nodes into heap
|
||||
for (auto edge : facade.GetAdjacentEdgeRange(node))
|
||||
{
|
||||
const auto &data = facade.GetEdgeData(edge);
|
||||
const auto to = facade.GetTarget(edge);
|
||||
|
||||
if (facade.ExcludeNode(to))
|
||||
if (DIRECTION == FORWARD_DIRECTION ? data.forward : data.backward)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((DIRECTION == FORWARD_DIRECTION ? facade.IsForwardEdge(edge)
|
||||
: facade.IsBackwardEdge(edge)) &&
|
||||
!query_heap.WasInserted(to))
|
||||
{
|
||||
const auto turn_id = data.turn_id;
|
||||
const auto node_id = DIRECTION == FORWARD_DIRECTION ? node : to;
|
||||
const auto edge_weight = initial_weight + facade.GetNodeWeight(node_id) +
|
||||
facade.GetWeightPenaltyForEdgeID(turn_id);
|
||||
const auto edge_duration = initial_duration + facade.GetNodeDuration(node_id) +
|
||||
facade.GetDurationPenaltyForEdgeID(turn_id);
|
||||
|
||||
query_heap.Insert(to, edge_weight, {node, edge_duration});
|
||||
query_heap.Insert(facade.GetTarget(edge),
|
||||
data.weight + initial_weight,
|
||||
{node, data.duration + initial_duration});
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -303,35 +318,28 @@ oneToManySearch(SearchEngineData<Algorithm> &engine_working_data,
|
||||
|
||||
if (DIRECTION == FORWARD_DIRECTION)
|
||||
{
|
||||
|
||||
if (phantom_node.IsValidForwardSource())
|
||||
{
|
||||
insert_node(phantom_node.forward_segment_id.id,
|
||||
-phantom_node.GetForwardWeightPlusOffset(),
|
||||
-phantom_node.GetForwardDuration());
|
||||
}
|
||||
|
||||
if (phantom_node.IsValidReverseSource())
|
||||
{
|
||||
insert_node(phantom_node.reverse_segment_id.id,
|
||||
-phantom_node.GetReverseWeightPlusOffset(),
|
||||
-phantom_node.GetReverseDuration());
|
||||
}
|
||||
}
|
||||
else if (DIRECTION == REVERSE_DIRECTION)
|
||||
{
|
||||
if (phantom_node.IsValidForwardTarget())
|
||||
{
|
||||
insert_node(phantom_node.forward_segment_id.id,
|
||||
phantom_node.GetForwardWeightPlusOffset(),
|
||||
phantom_node.GetForwardDuration());
|
||||
}
|
||||
|
||||
if (phantom_node.IsValidReverseTarget())
|
||||
{
|
||||
insert_node(phantom_node.reverse_segment_id.id,
|
||||
phantom_node.GetReverseWeightPlusOffset(),
|
||||
phantom_node.GetReverseDuration());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,127 +364,7 @@ oneToManySearch(SearchEngineData<Algorithm> &engine_working_data,
|
||||
phantom_indices);
|
||||
}
|
||||
|
||||
if (calculate_distance)
|
||||
{
|
||||
// Initialize unpacking heaps
|
||||
engine_working_data.InitializeOrClearFirstThreadLocalStorage(
|
||||
facade.GetNumberOfNodes(), facade.GetMaxBorderNodeID() + 1);
|
||||
|
||||
distances_table.resize(phantom_indices.size(), INVALID_EDGE_DISTANCE);
|
||||
|
||||
for (unsigned location = 0; location < phantom_indices.size(); ++location)
|
||||
{
|
||||
// Get the "middle" node that is the last node of a path
|
||||
const NodeID middle_node_id = middle_nodes_table[location];
|
||||
if (middle_node_id == SPECIAL_NODEID) // takes care of one-ways
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Retrieve the packed path from the heap
|
||||
PackedPath packed_path = mld::retrievePackedPathFromSingleManyToManyHeap<DIRECTION>(
|
||||
query_heap, middle_node_id);
|
||||
|
||||
// ... and reverse it to have packed edges in the correct order,
|
||||
if (DIRECTION == FORWARD_DIRECTION)
|
||||
{
|
||||
std::reverse(packed_path.begin(), packed_path.end());
|
||||
}
|
||||
|
||||
// ... unpack path
|
||||
auto &forward_heap = *engine_working_data.forward_heap_1;
|
||||
auto &reverse_heap = *engine_working_data.reverse_heap_1;
|
||||
EdgeWeight weight = INVALID_EDGE_WEIGHT;
|
||||
std::vector<NodeID> unpacked_nodes;
|
||||
std::vector<EdgeID> unpacked_edges;
|
||||
|
||||
std::tie(weight, unpacked_nodes, unpacked_edges) =
|
||||
unpackPathAndCalculateDistance(engine_working_data,
|
||||
facade,
|
||||
forward_heap,
|
||||
reverse_heap,
|
||||
DO_NOT_FORCE_LOOPS,
|
||||
DO_NOT_FORCE_LOOPS,
|
||||
INVALID_EDGE_WEIGHT,
|
||||
packed_path,
|
||||
middle_node_id,
|
||||
phantom_nodes,
|
||||
phantom_index,
|
||||
phantom_indices);
|
||||
|
||||
// Accumulate the path length without the last node
|
||||
auto annotation = 0.0;
|
||||
|
||||
BOOST_ASSERT(!unpacked_nodes.empty());
|
||||
for (auto node = unpacked_nodes.begin(), last_node = std::prev(unpacked_nodes.end());
|
||||
node != last_node;
|
||||
++node)
|
||||
{
|
||||
annotation += computeEdgeDistance(facade, *node);
|
||||
}
|
||||
|
||||
// ... and add negative source and positive target offsets
|
||||
// ⚠ for REVERSE_DIRECTION original source and target phantom nodes are swapped
|
||||
// Get source and target phantom nodes
|
||||
// * 1-to-N: source is a single index, target is the corresponding from the indices list
|
||||
// * N-to-1: source is the corresponding from the indices list, target is a single index
|
||||
auto source_phantom_index = phantom_index;
|
||||
auto target_phantom_index = phantom_indices[location];
|
||||
if (DIRECTION == REVERSE_DIRECTION)
|
||||
{
|
||||
std::swap(source_phantom_index, target_phantom_index);
|
||||
}
|
||||
const auto &source_phantom = phantom_nodes[source_phantom_index];
|
||||
const auto &target_phantom = phantom_nodes[target_phantom_index];
|
||||
const NodeID source_node = unpacked_nodes.front();
|
||||
const NodeID target_node = unpacked_nodes.back();
|
||||
|
||||
EdgeDistance source_offset = 0., target_offset = 0.;
|
||||
if (source_phantom.IsValidForwardSource() &&
|
||||
source_phantom.forward_segment_id.id == source_node)
|
||||
{
|
||||
// ............ <-- calculateEGBAnnotation returns distance from 0
|
||||
// to 3
|
||||
// -->s <-- subtract offset to start at source
|
||||
// ......... <-- want this distance as result
|
||||
// entry 0---1---2---3--- <-- 3 is exit node
|
||||
source_offset = source_phantom.GetForwardDistance();
|
||||
}
|
||||
else if (source_phantom.IsValidReverseSource() &&
|
||||
source_phantom.reverse_segment_id.id == source_node)
|
||||
{
|
||||
// ............ <-- calculateEGBAnnotation returns distance from 0 to 3
|
||||
// s<------- <-- subtract offset to start at source
|
||||
// ... <-- want this distance
|
||||
// entry 0---1---2---3 <-- 3 is exit node
|
||||
source_offset = source_phantom.GetReverseDistance();
|
||||
}
|
||||
if (target_phantom.IsValidForwardTarget() &&
|
||||
target_phantom.forward_segment_id.id == target_node)
|
||||
{
|
||||
// ............ <-- calculateEGBAnnotation returns distance from 0
|
||||
// to 3
|
||||
// ++>t <-- add offset to get to target
|
||||
// ................ <-- want this distance as result
|
||||
// entry 0---1---2---3--- <-- 3 is exit node
|
||||
target_offset = target_phantom.GetForwardDistance();
|
||||
}
|
||||
else if (target_phantom.IsValidReverseTarget() &&
|
||||
target_phantom.reverse_segment_id.id == target_node)
|
||||
{
|
||||
// ............ <-- calculateEGBAnnotation returns distance from 0
|
||||
// to 3
|
||||
// <++t <-- add offset to get from target
|
||||
// ................ <-- want this distance as result
|
||||
// entry 0---1---2---3--- <-- 3 is exit node
|
||||
target_offset = target_phantom.GetReverseDistance();
|
||||
}
|
||||
|
||||
distances_table[location] = -source_offset + annotation + target_offset;
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_pair(durations, distances_table);
|
||||
return durations;
|
||||
}
|
||||
|
||||
//
|
||||
@@ -491,7 +379,6 @@ void forwardRoutingStep(const DataFacade<Algorithm> &facade,
|
||||
const std::vector<NodeBucket> &search_space_with_buckets,
|
||||
std::vector<EdgeWeight> &weights_table,
|
||||
std::vector<EdgeDuration> &durations_table,
|
||||
std::vector<NodeID> &middle_nodes_table,
|
||||
const PhantomNode &phantom_node)
|
||||
{
|
||||
const auto node = query_heap.DeleteMin();
|
||||
@@ -528,7 +415,6 @@ void forwardRoutingStep(const DataFacade<Algorithm> &facade,
|
||||
{
|
||||
current_weight = new_weight;
|
||||
current_duration = new_duration;
|
||||
middle_nodes_table[location] = node;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -546,12 +432,9 @@ void backwardRoutingStep(const DataFacade<Algorithm> &facade,
|
||||
const auto node = query_heap.DeleteMin();
|
||||
const auto target_weight = query_heap.GetKey(node);
|
||||
const auto target_duration = query_heap.GetData(node).duration;
|
||||
const auto parent = query_heap.GetData(node).parent;
|
||||
const auto from_clique_arc = query_heap.GetData(node).from_clique_arc;
|
||||
|
||||
// Store settled nodes in search space bucket
|
||||
search_space_with_buckets.emplace_back(
|
||||
node, parent, from_clique_arc, column_idx, target_weight, target_duration);
|
||||
search_space_with_buckets.emplace_back(node, column_idx, target_weight, target_duration);
|
||||
|
||||
const auto &partition = facade.GetMultiLevelPartition();
|
||||
const auto maximal_level = partition.GetNumberOfLevels() - 1;
|
||||
@@ -561,225 +444,11 @@ void backwardRoutingStep(const DataFacade<Algorithm> &facade,
|
||||
}
|
||||
|
||||
template <bool DIRECTION>
|
||||
void retrievePackedPathFromSearchSpace(NodeID middle_node_id,
|
||||
const unsigned column_idx,
|
||||
const std::vector<NodeBucket> &search_space_with_buckets,
|
||||
PackedPath &path)
|
||||
{
|
||||
auto bucket_list = std::equal_range(search_space_with_buckets.begin(),
|
||||
search_space_with_buckets.end(),
|
||||
middle_node_id,
|
||||
NodeBucket::ColumnCompare(column_idx));
|
||||
|
||||
BOOST_ASSERT_MSG(std::distance(bucket_list.first, bucket_list.second) == 1,
|
||||
"The pointers are not pointing to the same element.");
|
||||
|
||||
NodeID current_node_id = middle_node_id;
|
||||
|
||||
while (bucket_list.first->parent_node != current_node_id &&
|
||||
bucket_list.first != search_space_with_buckets.end())
|
||||
{
|
||||
const auto parent_node_id = bucket_list.first->parent_node;
|
||||
|
||||
const auto from = DIRECTION == FORWARD_DIRECTION ? current_node_id : parent_node_id;
|
||||
const auto to = DIRECTION == FORWARD_DIRECTION ? parent_node_id : current_node_id;
|
||||
path.emplace_back(std::make_tuple(from, to, bucket_list.first->from_clique_arc));
|
||||
|
||||
current_node_id = parent_node_id;
|
||||
bucket_list = std::equal_range(search_space_with_buckets.begin(),
|
||||
search_space_with_buckets.end(),
|
||||
current_node_id,
|
||||
NodeBucket::ColumnCompare(column_idx));
|
||||
|
||||
BOOST_ASSERT_MSG(std::distance(bucket_list.first, bucket_list.second) == 1,
|
||||
"The pointers are not pointing to the same element.");
|
||||
}
|
||||
}
|
||||
|
||||
template <bool DIRECTION>
|
||||
void calculateDistances(typename SearchEngineData<mld::Algorithm>::ManyToManyQueryHeap &query_heap,
|
||||
const DataFacade<mld::Algorithm> &facade,
|
||||
const std::vector<PhantomNode> &phantom_nodes,
|
||||
const std::vector<std::size_t> &target_indices,
|
||||
const unsigned row_idx,
|
||||
const std::size_t source_index,
|
||||
const unsigned number_of_sources,
|
||||
const unsigned number_of_targets,
|
||||
const std::vector<NodeBucket> &search_space_with_buckets,
|
||||
std::vector<EdgeDistance> &distances_table,
|
||||
const std::vector<NodeID> &middle_nodes_table,
|
||||
SearchEngineData<mld::Algorithm> &engine_working_data)
|
||||
{
|
||||
engine_working_data.InitializeOrClearFirstThreadLocalStorage(facade.GetNumberOfNodes(),
|
||||
facade.GetMaxBorderNodeID() + 1);
|
||||
|
||||
for (unsigned column_idx = 0; column_idx < number_of_targets; ++column_idx)
|
||||
{
|
||||
// Step 1: Get source and target phantom nodes that were used in the bucketed search
|
||||
auto source_phantom_index = source_index;
|
||||
auto target_phantom_index = target_indices[column_idx];
|
||||
const auto &source_phantom = phantom_nodes[source_phantom_index];
|
||||
const auto &target_phantom = phantom_nodes[target_phantom_index];
|
||||
|
||||
const auto location = DIRECTION == FORWARD_DIRECTION
|
||||
? row_idx * number_of_targets + column_idx
|
||||
: row_idx + column_idx * number_of_sources;
|
||||
|
||||
if (source_phantom_index == target_phantom_index)
|
||||
{
|
||||
distances_table[location] = 0.0;
|
||||
continue;
|
||||
}
|
||||
|
||||
NodeID middle_node_id = middle_nodes_table[location];
|
||||
|
||||
if (middle_node_id == SPECIAL_NODEID) // takes care of one-ways
|
||||
{
|
||||
distances_table[location] = INVALID_EDGE_DISTANCE;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Step 2: Find path from source to middle node
|
||||
PackedPath packed_path =
|
||||
mld::retrievePackedPathFromSingleManyToManyHeap<DIRECTION>(query_heap, middle_node_id);
|
||||
|
||||
if (DIRECTION == FORWARD_DIRECTION)
|
||||
{
|
||||
std::reverse(packed_path.begin(), packed_path.end());
|
||||
}
|
||||
|
||||
auto &forward_heap = *engine_working_data.forward_heap_1;
|
||||
auto &reverse_heap = *engine_working_data.reverse_heap_1;
|
||||
EdgeWeight weight = INVALID_EDGE_WEIGHT;
|
||||
std::vector<NodeID> unpacked_nodes_from_source;
|
||||
std::vector<EdgeID> unpacked_edges;
|
||||
std::tie(weight, unpacked_nodes_from_source, unpacked_edges) =
|
||||
unpackPathAndCalculateDistance(engine_working_data,
|
||||
facade,
|
||||
forward_heap,
|
||||
reverse_heap,
|
||||
DO_NOT_FORCE_LOOPS,
|
||||
DO_NOT_FORCE_LOOPS,
|
||||
INVALID_EDGE_WEIGHT,
|
||||
packed_path,
|
||||
middle_node_id,
|
||||
source_phantom);
|
||||
|
||||
// Step 3: Find path from middle to target node
|
||||
packed_path.clear();
|
||||
retrievePackedPathFromSearchSpace<DIRECTION>(
|
||||
middle_node_id, column_idx, search_space_with_buckets, packed_path);
|
||||
|
||||
if (DIRECTION == REVERSE_DIRECTION)
|
||||
{
|
||||
std::reverse(packed_path.begin(), packed_path.end());
|
||||
}
|
||||
|
||||
std::vector<NodeID> unpacked_nodes_to_target;
|
||||
std::tie(weight, unpacked_nodes_to_target, unpacked_edges) =
|
||||
unpackPathAndCalculateDistance(engine_working_data,
|
||||
facade,
|
||||
forward_heap,
|
||||
reverse_heap,
|
||||
DO_NOT_FORCE_LOOPS,
|
||||
DO_NOT_FORCE_LOOPS,
|
||||
INVALID_EDGE_WEIGHT,
|
||||
packed_path,
|
||||
middle_node_id,
|
||||
target_phantom);
|
||||
|
||||
if (DIRECTION == REVERSE_DIRECTION)
|
||||
{
|
||||
std::swap(unpacked_nodes_to_target, unpacked_nodes_from_source);
|
||||
}
|
||||
|
||||
// Step 4: Compute annotation value along the path nodes without the target node
|
||||
auto annotation = 0.0;
|
||||
|
||||
for (auto node = unpacked_nodes_from_source.begin(),
|
||||
last_node = std::prev(unpacked_nodes_from_source.end());
|
||||
node != last_node;
|
||||
++node)
|
||||
{
|
||||
annotation += computeEdgeDistance(facade, *node);
|
||||
}
|
||||
|
||||
for (auto node = unpacked_nodes_to_target.begin(),
|
||||
last_node = std::prev(unpacked_nodes_to_target.end());
|
||||
node != last_node;
|
||||
++node)
|
||||
{
|
||||
annotation += computeEdgeDistance(facade, *node);
|
||||
}
|
||||
|
||||
// Step 5: Get phantom node offsets and compute the annotation value
|
||||
EdgeDistance source_offset = 0., target_offset = 0.;
|
||||
{
|
||||
// ⚠ for REVERSE_DIRECTION original source and target phantom nodes are swapped
|
||||
if (DIRECTION == REVERSE_DIRECTION)
|
||||
{
|
||||
std::swap(source_phantom_index, target_phantom_index);
|
||||
}
|
||||
const auto &source_phantom = phantom_nodes[source_phantom_index];
|
||||
const auto &target_phantom = phantom_nodes[target_phantom_index];
|
||||
|
||||
NodeID source_node = unpacked_nodes_from_source.front();
|
||||
NodeID target_node = unpacked_nodes_to_target.back();
|
||||
|
||||
if (source_phantom.IsValidForwardSource() &&
|
||||
source_phantom.forward_segment_id.id == source_node)
|
||||
{
|
||||
// ............ <-- calculateEGBAnnotation returns distance from 0
|
||||
// to 3
|
||||
// -->s <-- subtract offset to start at source
|
||||
// ......... <-- want this distance as result
|
||||
// entry 0---1---2---3--- <-- 3 is exit node
|
||||
source_offset = source_phantom.GetForwardDistance();
|
||||
}
|
||||
else if (source_phantom.IsValidReverseSource() &&
|
||||
source_phantom.reverse_segment_id.id == source_node)
|
||||
{
|
||||
// ............ <-- calculateEGBAnnotation returns distance from 0 to 3
|
||||
// s<------- <-- subtract offset to start at source
|
||||
// ... <-- want this distance
|
||||
// entry 0---1---2---3 <-- 3 is exit node
|
||||
source_offset = source_phantom.GetReverseDistance();
|
||||
}
|
||||
|
||||
if (target_phantom.IsValidForwardTarget() &&
|
||||
target_phantom.forward_segment_id.id == target_node)
|
||||
{
|
||||
// ............ <-- calculateEGBAnnotation returns distance from 0
|
||||
// to 3
|
||||
// ++>t <-- add offset to get to target
|
||||
// ................ <-- want this distance as result
|
||||
// entry 0---1---2---3--- <-- 3 is exit node
|
||||
target_offset = target_phantom.GetForwardDistance();
|
||||
}
|
||||
else if (target_phantom.IsValidReverseTarget() &&
|
||||
target_phantom.reverse_segment_id.id == target_node)
|
||||
{
|
||||
// ............ <-- calculateEGBAnnotation returns distance from 0
|
||||
// to 3
|
||||
// <++t <-- add offset to get from target
|
||||
// ................ <-- want this distance as result
|
||||
// entry 0---1---2---3--- <-- 3 is exit node
|
||||
target_offset = target_phantom.GetReverseDistance();
|
||||
}
|
||||
}
|
||||
|
||||
distances_table[location] = -source_offset + annotation + target_offset;
|
||||
}
|
||||
}
|
||||
|
||||
template <bool DIRECTION>
|
||||
std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
|
||||
manyToManySearch(SearchEngineData<Algorithm> &engine_working_data,
|
||||
const DataFacade<Algorithm> &facade,
|
||||
const std::vector<PhantomNode> &phantom_nodes,
|
||||
const std::vector<std::size_t> &source_indices,
|
||||
const std::vector<std::size_t> &target_indices,
|
||||
const bool calculate_distance)
|
||||
std::vector<EdgeDuration> manyToManySearch(SearchEngineData<Algorithm> &engine_working_data,
|
||||
const DataFacade<Algorithm> &facade,
|
||||
const std::vector<PhantomNode> &phantom_nodes,
|
||||
const std::vector<std::size_t> &source_indices,
|
||||
const std::vector<std::size_t> &target_indices)
|
||||
{
|
||||
const auto number_of_sources = source_indices.size();
|
||||
const auto number_of_targets = target_indices.size();
|
||||
@@ -787,8 +456,6 @@ manyToManySearch(SearchEngineData<Algorithm> &engine_working_data,
|
||||
|
||||
std::vector<EdgeWeight> weights_table(number_of_entries, INVALID_EDGE_WEIGHT);
|
||||
std::vector<EdgeDuration> durations_table(number_of_entries, MAXIMAL_EDGE_DURATION);
|
||||
std::vector<EdgeDistance> distances_table;
|
||||
std::vector<NodeID> middle_nodes_table(number_of_entries, SPECIAL_NODEID);
|
||||
|
||||
std::vector<NodeBucket> search_space_with_buckets;
|
||||
|
||||
@@ -796,22 +463,22 @@ manyToManySearch(SearchEngineData<Algorithm> &engine_working_data,
|
||||
for (std::uint32_t column_idx = 0; column_idx < target_indices.size(); ++column_idx)
|
||||
{
|
||||
const auto index = target_indices[column_idx];
|
||||
const auto &target_phantom = phantom_nodes[index];
|
||||
const auto &phantom = phantom_nodes[index];
|
||||
|
||||
engine_working_data.InitializeOrClearManyToManyThreadLocalStorage(
|
||||
facade.GetNumberOfNodes(), facade.GetMaxBorderNodeID() + 1);
|
||||
auto &query_heap = *(engine_working_data.many_to_many_heap);
|
||||
|
||||
if (DIRECTION == FORWARD_DIRECTION)
|
||||
insertTargetInHeap(query_heap, target_phantom);
|
||||
insertTargetInHeap(query_heap, phantom);
|
||||
else
|
||||
insertSourceInHeap(query_heap, target_phantom);
|
||||
insertSourceInHeap(query_heap, phantom);
|
||||
|
||||
// explore search space
|
||||
while (!query_heap.Empty())
|
||||
{
|
||||
backwardRoutingStep<DIRECTION>(
|
||||
facade, column_idx, query_heap, search_space_with_buckets, target_phantom);
|
||||
facade, column_idx, query_heap, search_space_with_buckets, phantom);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -821,19 +488,18 @@ manyToManySearch(SearchEngineData<Algorithm> &engine_working_data,
|
||||
// Find shortest paths from sources to all accessible nodes
|
||||
for (std::uint32_t row_idx = 0; row_idx < source_indices.size(); ++row_idx)
|
||||
{
|
||||
const auto source_index = source_indices[row_idx];
|
||||
const auto &source_phantom = phantom_nodes[source_index];
|
||||
const auto index = source_indices[row_idx];
|
||||
const auto &phantom = phantom_nodes[index];
|
||||
|
||||
// Clear heap and insert source nodes
|
||||
engine_working_data.InitializeOrClearManyToManyThreadLocalStorage(
|
||||
facade.GetNumberOfNodes(), facade.GetMaxBorderNodeID() + 1);
|
||||
|
||||
auto &query_heap = *(engine_working_data.many_to_many_heap);
|
||||
|
||||
if (DIRECTION == FORWARD_DIRECTION)
|
||||
insertSourceInHeap(query_heap, source_phantom);
|
||||
insertSourceInHeap(query_heap, phantom);
|
||||
else
|
||||
insertTargetInHeap(query_heap, source_phantom);
|
||||
insertTargetInHeap(query_heap, phantom);
|
||||
|
||||
// Explore search space
|
||||
while (!query_heap.Empty())
|
||||
@@ -846,29 +512,11 @@ manyToManySearch(SearchEngineData<Algorithm> &engine_working_data,
|
||||
search_space_with_buckets,
|
||||
weights_table,
|
||||
durations_table,
|
||||
middle_nodes_table,
|
||||
source_phantom);
|
||||
}
|
||||
|
||||
if (calculate_distance)
|
||||
{
|
||||
distances_table.resize(number_of_entries, INVALID_EDGE_DISTANCE);
|
||||
calculateDistances<DIRECTION>(query_heap,
|
||||
facade,
|
||||
phantom_nodes,
|
||||
target_indices, // source_indices
|
||||
row_idx,
|
||||
source_index,
|
||||
number_of_sources,
|
||||
number_of_targets,
|
||||
search_space_with_buckets,
|
||||
distances_table,
|
||||
middle_nodes_table,
|
||||
engine_working_data);
|
||||
phantom);
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_pair(durations_table, distances_table);
|
||||
return durations_table;
|
||||
}
|
||||
|
||||
} // namespace mld
|
||||
@@ -886,54 +534,32 @@ manyToManySearch(SearchEngineData<Algorithm> &engine_working_data,
|
||||
// then search is performed on a reversed graph with phantom nodes with flipped roles and
|
||||
// returning a transposed matrix.
|
||||
template <>
|
||||
std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
|
||||
manyToManySearch(SearchEngineData<mld::Algorithm> &engine_working_data,
|
||||
const DataFacade<mld::Algorithm> &facade,
|
||||
const std::vector<PhantomNode> &phantom_nodes,
|
||||
const std::vector<std::size_t> &source_indices,
|
||||
const std::vector<std::size_t> &target_indices,
|
||||
const bool calculate_distance,
|
||||
const bool calculate_duration)
|
||||
std::vector<EdgeDuration> manyToManySearch(SearchEngineData<mld::Algorithm> &engine_working_data,
|
||||
const DataFacade<mld::Algorithm> &facade,
|
||||
const std::vector<PhantomNode> &phantom_nodes,
|
||||
const std::vector<std::size_t> &source_indices,
|
||||
const std::vector<std::size_t> &target_indices)
|
||||
{
|
||||
(void)calculate_duration; // flag stub to use for calculating distances in matrix in mld in the
|
||||
// future
|
||||
|
||||
if (source_indices.size() == 1)
|
||||
{ // TODO: check if target_indices.size() == 1 and do a bi-directional search
|
||||
return mld::oneToManySearch<FORWARD_DIRECTION>(engine_working_data,
|
||||
facade,
|
||||
phantom_nodes,
|
||||
source_indices.front(),
|
||||
target_indices,
|
||||
calculate_distance);
|
||||
return mld::oneToManySearch<FORWARD_DIRECTION>(
|
||||
engine_working_data, facade, phantom_nodes, source_indices.front(), target_indices);
|
||||
}
|
||||
|
||||
if (target_indices.size() == 1)
|
||||
{
|
||||
return mld::oneToManySearch<REVERSE_DIRECTION>(engine_working_data,
|
||||
facade,
|
||||
phantom_nodes,
|
||||
target_indices.front(),
|
||||
source_indices,
|
||||
calculate_distance);
|
||||
return mld::oneToManySearch<REVERSE_DIRECTION>(
|
||||
engine_working_data, facade, phantom_nodes, target_indices.front(), source_indices);
|
||||
}
|
||||
|
||||
if (target_indices.size() < source_indices.size())
|
||||
{
|
||||
return mld::manyToManySearch<REVERSE_DIRECTION>(engine_working_data,
|
||||
facade,
|
||||
phantom_nodes,
|
||||
target_indices,
|
||||
source_indices,
|
||||
calculate_distance);
|
||||
return mld::manyToManySearch<REVERSE_DIRECTION>(
|
||||
engine_working_data, facade, phantom_nodes, target_indices, source_indices);
|
||||
}
|
||||
|
||||
return mld::manyToManySearch<FORWARD_DIRECTION>(engine_working_data,
|
||||
facade,
|
||||
phantom_nodes,
|
||||
source_indices,
|
||||
target_indices,
|
||||
calculate_distance);
|
||||
return mld::manyToManySearch<FORWARD_DIRECTION>(
|
||||
engine_working_data, facade, phantom_nodes, source_indices, target_indices);
|
||||
}
|
||||
|
||||
} // namespace routing_algorithms
|
||||
|
||||
@@ -227,9 +227,6 @@ SubMatchingList mapMatching(SearchEngineData<Algorithm> &engine_working_data,
|
||||
{
|
||||
continue;
|
||||
}
|
||||
forward_heap.Clear();
|
||||
const auto &source_phantom = prev_unbroken_timestamps_list[s].phantom_node;
|
||||
insertSourceInHeap(forward_heap, source_phantom);
|
||||
|
||||
for (const auto s_prime : util::irange<std::size_t>(0UL, current_viterbi.size()))
|
||||
{
|
||||
@@ -240,19 +237,14 @@ SubMatchingList mapMatching(SearchEngineData<Algorithm> &engine_working_data,
|
||||
continue;
|
||||
}
|
||||
|
||||
reverse_heap.Clear();
|
||||
const auto &target_phantom = current_timestamps_list[s_prime].phantom_node;
|
||||
insertTargetInHeap(reverse_heap, target_phantom);
|
||||
|
||||
double network_distance = getNetworkDistance(engine_working_data,
|
||||
facade,
|
||||
forward_heap,
|
||||
reverse_heap,
|
||||
source_phantom,
|
||||
target_phantom,
|
||||
weight_upper_bound);
|
||||
|
||||
network_distance = std::round(network_distance * 10) / 10;
|
||||
double network_distance =
|
||||
getNetworkDistance(engine_working_data,
|
||||
facade,
|
||||
forward_heap,
|
||||
reverse_heap,
|
||||
prev_unbroken_timestamps_list[s].phantom_node,
|
||||
current_timestamps_list[s_prime].phantom_node,
|
||||
weight_upper_bound);
|
||||
|
||||
// get distance diff between loc1/2 and locs/s_prime
|
||||
const auto d_t = std::abs(network_distance - haversine_distance);
|
||||
|
||||
@@ -33,79 +33,6 @@ bool needsLoopBackwards(const PhantomNodes &phantoms)
|
||||
return needsLoopBackwards(phantoms.source_phantom, phantoms.target_phantom);
|
||||
}
|
||||
|
||||
EdgeDistance adjustPathDistanceToPhantomNodes(const std::vector<NodeID> &path,
|
||||
const PhantomNode &source_phantom,
|
||||
const PhantomNode &target_phantom,
|
||||
const EdgeDistance uncorrected_distance)
|
||||
{
|
||||
EdgeDistance distance = uncorrected_distance;
|
||||
if (!path.empty())
|
||||
{
|
||||
|
||||
// check the direction of travel to figure out how to calculate the offset to/from
|
||||
// the source/target
|
||||
if (source_phantom.forward_segment_id.id == path.front())
|
||||
{
|
||||
// ............ <-- calculateEGBAnnotation returns distance from 0 to 3
|
||||
// -->s <-- subtract offset to start at source
|
||||
// ......... <-- want this distance as result
|
||||
// entry 0---1---2---3--- <-- 3 is exit node
|
||||
distance -= source_phantom.GetForwardDistance();
|
||||
}
|
||||
else if (source_phantom.reverse_segment_id.id == path.front())
|
||||
{
|
||||
// ............ <-- calculateEGBAnnotation returns distance from 0 to 3
|
||||
// s<------- <-- subtract offset to start at source
|
||||
// ... <-- want this distance
|
||||
// entry 0---1---2---3 <-- 3 is exit node
|
||||
distance -= source_phantom.GetReverseDistance();
|
||||
}
|
||||
if (target_phantom.forward_segment_id.id == path.back())
|
||||
{
|
||||
// ............ <-- calculateEGBAnnotation returns distance from 0 to 3
|
||||
// ++>t <-- add offset to get to target
|
||||
// ................ <-- want this distance as result
|
||||
// entry 0---1---2---3--- <-- 3 is exit node
|
||||
distance += target_phantom.GetForwardDistance();
|
||||
}
|
||||
else if (target_phantom.reverse_segment_id.id == path.back())
|
||||
{
|
||||
// ............ <-- calculateEGBAnnotation returns distance from 0 to 3
|
||||
// <++t <-- add offset to get from target
|
||||
// ................ <-- want this distance as result
|
||||
// entry 0---1---2---3--- <-- 3 is exit node
|
||||
distance += target_phantom.GetReverseDistance();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// there is no shortcut to unpack. source and target are on the same EBG Node.
|
||||
// if the offset of the target is greater than the offset of the source, subtract it
|
||||
if (target_phantom.GetForwardDistance() > source_phantom.GetForwardDistance())
|
||||
{
|
||||
// --------->t <-- offsets
|
||||
// ->s <-- subtract source offset from target offset
|
||||
// ......... <-- want this distance as result
|
||||
// entry 0---1---2---3--- <-- 3 is exit node
|
||||
distance = target_phantom.GetForwardDistance() - source_phantom.GetForwardDistance();
|
||||
}
|
||||
else
|
||||
{
|
||||
// s<--- <-- offsets
|
||||
// t<--------- <-- subtract source offset from target offset
|
||||
// ...... <-- want this distance as result
|
||||
// entry 0---1---2---3--- <-- 3 is exit node
|
||||
distance = target_phantom.GetReverseDistance() - source_phantom.GetReverseDistance();
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_ASSERT_MSG(distance >= 0 || distance > -1.0f,
|
||||
"Distance correction generated negative number");
|
||||
// guard against underflow errors caused by rounding
|
||||
distance = std::max(EdgeDistance{0}, distance);
|
||||
return distance;
|
||||
}
|
||||
|
||||
} // namespace routing_algorithms
|
||||
} // namespace engine
|
||||
} // namespace osrm
|
||||
|
||||
@@ -59,24 +59,6 @@ void retrievePackedPathFromSingleHeap(const SearchEngineData<Algorithm>::QueryHe
|
||||
}
|
||||
}
|
||||
|
||||
void retrievePackedPathFromSingleManyToManyHeap(
|
||||
const SearchEngineData<Algorithm>::ManyToManyQueryHeap &search_heap,
|
||||
const NodeID middle_node_id,
|
||||
std::vector<NodeID> &packed_path)
|
||||
{
|
||||
NodeID current_node_id = middle_node_id;
|
||||
// all initial nodes will have itself as parent, or a node not in the heap
|
||||
// in case of a core search heap. We need a distinction between core entry nodes
|
||||
// and start nodes since otherwise start node specific code that assumes
|
||||
// node == node.parent (e.g. the loop code) might get actived.
|
||||
while (current_node_id != search_heap.GetData(current_node_id).parent &&
|
||||
search_heap.WasInserted(search_heap.GetData(current_node_id).parent))
|
||||
{
|
||||
current_node_id = search_heap.GetData(current_node_id).parent;
|
||||
packed_path.emplace_back(current_node_id);
|
||||
}
|
||||
}
|
||||
|
||||
// assumes that heaps are already setup correctly.
|
||||
// ATTENTION: This only works if no additional offset is supplied next to the Phantom Node
|
||||
// Offsets.
|
||||
@@ -100,7 +82,7 @@ void search(SearchEngineData<Algorithm> & /*engine_working_data*/,
|
||||
const PhantomNodes & /*phantom_nodes*/,
|
||||
const EdgeWeight weight_upper_bound)
|
||||
{
|
||||
if (forward_heap.Empty() && reverse_heap.Empty())
|
||||
if (forward_heap.Empty() || reverse_heap.Empty())
|
||||
{
|
||||
weight = INVALID_EDGE_WEIGHT;
|
||||
return;
|
||||
@@ -110,14 +92,10 @@ void search(SearchEngineData<Algorithm> & /*engine_working_data*/,
|
||||
weight = weight_upper_bound;
|
||||
|
||||
// get offset to account for offsets on phantom nodes on compressed edges
|
||||
EdgeWeight min_edge_offset = 0;
|
||||
if (forward_heap.Size() > 0)
|
||||
{
|
||||
min_edge_offset = std::min(min_edge_offset, forward_heap.MinKey());
|
||||
BOOST_ASSERT(min_edge_offset <= 0);
|
||||
}
|
||||
const auto min_edge_offset = std::min(0, forward_heap.MinKey());
|
||||
BOOST_ASSERT(min_edge_offset <= 0);
|
||||
// we only every insert negative offsets for nodes in the forward heap
|
||||
BOOST_ASSERT(reverse_heap.Empty() || reverse_heap.MinKey() >= 0);
|
||||
BOOST_ASSERT(reverse_heap.MinKey() >= 0);
|
||||
|
||||
// run two-Target Dijkstra routing step.
|
||||
while (0 < (forward_heap.Size() + reverse_heap.Size()))
|
||||
@@ -180,6 +158,11 @@ double getNetworkDistance(SearchEngineData<Algorithm> &engine_working_data,
|
||||
const PhantomNode &target_phantom,
|
||||
EdgeWeight weight_upper_bound)
|
||||
{
|
||||
forward_heap.Clear();
|
||||
reverse_heap.Clear();
|
||||
|
||||
insertNodesInHeaps(forward_heap, reverse_heap, {source_phantom, target_phantom});
|
||||
|
||||
EdgeWeight weight = INVALID_EDGE_WEIGHT;
|
||||
std::vector<NodeID> packed_path;
|
||||
search(engine_working_data,
|
||||
@@ -198,31 +181,14 @@ double getNetworkDistance(SearchEngineData<Algorithm> &engine_working_data,
|
||||
return std::numeric_limits<double>::max();
|
||||
}
|
||||
|
||||
EdgeDistance distance = 0;
|
||||
std::vector<PathData> unpacked_path;
|
||||
unpackPath(facade,
|
||||
packed_path.begin(),
|
||||
packed_path.end(),
|
||||
{source_phantom, target_phantom},
|
||||
unpacked_path);
|
||||
|
||||
std::vector<NodeID> unpacked_nodes;
|
||||
unpacked_nodes.reserve(packed_path.size());
|
||||
if (!packed_path.empty())
|
||||
{
|
||||
unpacked_nodes.push_back(packed_path.front());
|
||||
unpackPath(
|
||||
facade, packed_path.begin(), packed_path.end(), [&](const auto &edge, const auto &) {
|
||||
BOOST_ASSERT(edge.first == unpacked_nodes.back());
|
||||
unpacked_nodes.push_back(edge.second);
|
||||
});
|
||||
|
||||
distance = std::accumulate(unpacked_nodes.begin(),
|
||||
std::prev(unpacked_nodes.end()),
|
||||
EdgeDistance{0},
|
||||
[&](const EdgeDistance distance, const auto node_id) {
|
||||
return distance + computeEdgeDistance(facade, node_id);
|
||||
});
|
||||
}
|
||||
|
||||
distance =
|
||||
adjustPathDistanceToPhantomNodes(unpacked_nodes, source_phantom, target_phantom, distance);
|
||||
|
||||
return distance;
|
||||
return getPathDistance(facade, unpacked_path, source_phantom, target_phantom);
|
||||
}
|
||||
} // namespace ch
|
||||
|
||||
|
||||
@@ -101,6 +101,7 @@ std::vector<TurnData> generateTurns(const datafacade &facade,
|
||||
// w
|
||||
// uv is the "approach"
|
||||
// vw is the "exit"
|
||||
|
||||
// Look at every node in the directed graph we created
|
||||
for (const auto &startnode : sorted_startnodes)
|
||||
{
|
||||
@@ -145,8 +146,42 @@ std::vector<TurnData> generateTurns(const datafacade &facade,
|
||||
const auto &data = facade.GetEdgeData(edge_based_edge_id);
|
||||
|
||||
// Now, calculate the sum of the weight of all the segments.
|
||||
const auto turn_weight = facade.GetWeightPenaltyForEdgeID(data.turn_id);
|
||||
const auto turn_duration = facade.GetDurationPenaltyForEdgeID(data.turn_id);
|
||||
const auto &geometry =
|
||||
edge_based_node_info.find(approachedge.edge_based_node_id)->second;
|
||||
EdgeWeight sum_node_weight = 0;
|
||||
EdgeDuration sum_node_duration = 0;
|
||||
if (geometry.is_geometry_forward)
|
||||
{
|
||||
const auto approach_weight =
|
||||
facade.GetUncompressedForwardWeights(geometry.packed_geometry_id);
|
||||
const auto approach_duration =
|
||||
facade.GetUncompressedForwardDurations(geometry.packed_geometry_id);
|
||||
sum_node_weight = std::accumulate(
|
||||
approach_weight.begin(), approach_weight.end(), EdgeWeight{0});
|
||||
sum_node_duration = std::accumulate(
|
||||
approach_duration.begin(), approach_duration.end(), EdgeDuration{0});
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto approach_weight =
|
||||
facade.GetUncompressedReverseWeights(geometry.packed_geometry_id);
|
||||
const auto approach_duration =
|
||||
facade.GetUncompressedReverseDurations(geometry.packed_geometry_id);
|
||||
sum_node_weight = std::accumulate(
|
||||
approach_weight.begin(), approach_weight.end(), EdgeWeight{0});
|
||||
sum_node_duration = std::accumulate(
|
||||
approach_duration.begin(), approach_duration.end(), EdgeDuration{0});
|
||||
}
|
||||
|
||||
// The edge.weight is the whole edge weight, which includes the turn
|
||||
// cost.
|
||||
// The turn cost is the edge.weight minus the sum of the individual road
|
||||
// segment weights. This might not be 100% accurate, because some
|
||||
// intersections include stop signs, traffic signals and other
|
||||
// penalties, but at this stage, we can't divide those out, so we just
|
||||
// treat the whole lot as the "turn cost" that we'll stick on the map.
|
||||
const auto turn_weight = data.weight - sum_node_weight;
|
||||
const auto turn_duration = data.duration - sum_node_duration;
|
||||
const auto turn_instruction = facade.GetTurnInstructionForEdgeID(data.turn_id);
|
||||
|
||||
// Find the three nodes that make up the turn movement)
|
||||
|
||||
@@ -107,13 +107,6 @@ void EdgeBasedGraphFactory::GetEdgeBasedNodeWeights(std::vector<EdgeWeight> &out
|
||||
swap(m_edge_based_node_weights, output_node_weights);
|
||||
}
|
||||
|
||||
void EdgeBasedGraphFactory::GetEdgeBasedNodeDurations(
|
||||
std::vector<EdgeWeight> &output_node_durations)
|
||||
{
|
||||
using std::swap; // Koenig swap
|
||||
swap(m_edge_based_node_durations, output_node_durations);
|
||||
}
|
||||
|
||||
std::uint32_t EdgeBasedGraphFactory::GetConnectivityChecksum() const
|
||||
{
|
||||
return m_connectivity_checksum;
|
||||
@@ -145,16 +138,9 @@ NBGToEBG EdgeBasedGraphFactory::InsertEdgeBasedNode(const NodeID node_u, const N
|
||||
BOOST_ASSERT(nbe_to_ebn_mapping[edge_id_1] != SPECIAL_NODEID ||
|
||||
nbe_to_ebn_mapping[edge_id_2] != SPECIAL_NODEID);
|
||||
|
||||
// ⚠ Use the sign bit of node weights to distinguish oneway streets:
|
||||
// * MSB is set - a node corresponds to a one-way street
|
||||
// * MSB is clear - a node corresponds to a bidirectional street
|
||||
// Before using node weights data values must be adjusted:
|
||||
// * in contraction if MSB is set the node weight is INVALID_EDGE_WEIGHT.
|
||||
// This adjustment is needed to enforce loop creation for oneways.
|
||||
// * in other cases node weights must be masked with 0x7fffffff to clear MSB
|
||||
if (nbe_to_ebn_mapping[edge_id_1] != SPECIAL_NODEID &&
|
||||
nbe_to_ebn_mapping[edge_id_2] == SPECIAL_NODEID)
|
||||
m_edge_based_node_weights[nbe_to_ebn_mapping[edge_id_1]] |= 0x80000000;
|
||||
m_edge_based_node_weights[nbe_to_ebn_mapping[edge_id_1]] = INVALID_EDGE_WEIGHT;
|
||||
|
||||
BOOST_ASSERT(m_compressed_edge_container.HasEntryForID(edge_id_1) ==
|
||||
m_compressed_edge_container.HasEntryForID(edge_id_2));
|
||||
@@ -292,7 +278,6 @@ unsigned EdgeBasedGraphFactory::LabelEdgeBasedNodes()
|
||||
// heuristic: node-based graph node is a simple intersection with four edges
|
||||
// (edge-based nodes)
|
||||
m_edge_based_node_weights.reserve(4 * m_node_based_graph.GetNumberOfNodes());
|
||||
m_edge_based_node_durations.reserve(4 * m_node_based_graph.GetNumberOfNodes());
|
||||
nbe_to_ebn_mapping.resize(m_node_based_graph.GetEdgeCapacity(), SPECIAL_NODEID);
|
||||
|
||||
// renumber edge based node of outgoing edges
|
||||
@@ -309,7 +294,6 @@ unsigned EdgeBasedGraphFactory::LabelEdgeBasedNodes()
|
||||
}
|
||||
|
||||
m_edge_based_node_weights.push_back(edge_data.weight);
|
||||
m_edge_based_node_durations.push_back(edge_data.duration);
|
||||
|
||||
BOOST_ASSERT(numbered_edges_count < m_node_based_graph.GetNumberOfEdges());
|
||||
nbe_to_ebn_mapping[current_edge] = numbered_edges_count;
|
||||
@@ -403,10 +387,8 @@ EdgeBasedGraphFactory::GenerateEdgeExpandedNodes(const WayRestrictionMap &way_re
|
||||
segregated_edges.count(eid) > 0;
|
||||
|
||||
const auto ebn_weight = m_edge_based_node_weights[nbe_to_ebn_mapping[eid]];
|
||||
BOOST_ASSERT((ebn_weight & 0x7fffffff) == edge_data.weight);
|
||||
BOOST_ASSERT(ebn_weight == INVALID_EDGE_WEIGHT || ebn_weight == edge_data.weight);
|
||||
m_edge_based_node_weights.push_back(ebn_weight);
|
||||
m_edge_based_node_durations.push_back(
|
||||
m_edge_based_node_durations[nbe_to_ebn_mapping[eid]]);
|
||||
|
||||
edge_based_node_id++;
|
||||
progress.PrintStatus(progress_counter++);
|
||||
@@ -415,7 +397,6 @@ EdgeBasedGraphFactory::GenerateEdgeExpandedNodes(const WayRestrictionMap &way_re
|
||||
|
||||
BOOST_ASSERT(m_edge_based_node_segments.size() == m_edge_based_node_is_startpoint.size());
|
||||
BOOST_ASSERT(m_number_of_edge_based_nodes == m_edge_based_node_weights.size());
|
||||
BOOST_ASSERT(m_number_of_edge_based_nodes == m_edge_based_node_durations.size());
|
||||
|
||||
util::Log() << "Generated " << m_number_of_edge_based_nodes << " nodes ("
|
||||
<< way_restriction_map.NumberOfDuplicatedNodes()
|
||||
|
||||
@@ -430,6 +430,7 @@ void ExtractionContainers::PrepareEdges(ScriptingEnvironment &scripting_environm
|
||||
util::UnbufferedLog log;
|
||||
log << "Sorting edges by renumbered start ... ";
|
||||
TIMER_START(sort_edges_by_renumbered_start);
|
||||
std::mutex name_data_mutex;
|
||||
tbb::parallel_sort(all_edges_list.begin(),
|
||||
all_edges_list.end(),
|
||||
CmpEdgeByInternalSourceTargetAndName{
|
||||
|
||||
@@ -241,7 +241,6 @@ int Extractor::run(ScriptingEnvironment &scripting_environment)
|
||||
util::DeallocatingVector<EdgeBasedEdge> edge_based_edge_list;
|
||||
std::vector<bool> node_is_startpoint;
|
||||
std::vector<EdgeWeight> edge_based_node_weights;
|
||||
std::vector<EdgeDuration> edge_based_node_durations;
|
||||
std::uint32_t ebg_connectivity_checksum = 0;
|
||||
|
||||
// Create a node-based graph from the OSRM file
|
||||
@@ -321,7 +320,6 @@ int Extractor::run(ScriptingEnvironment &scripting_environment)
|
||||
edge_based_node_segments,
|
||||
node_is_startpoint,
|
||||
edge_based_node_weights,
|
||||
edge_based_node_durations,
|
||||
edge_based_edge_list,
|
||||
ebg_connectivity_checksum);
|
||||
|
||||
@@ -345,8 +343,8 @@ int Extractor::run(ScriptingEnvironment &scripting_environment)
|
||||
|
||||
util::Log() << "Saving edge-based node weights to file.";
|
||||
TIMER_START(timer_write_node_weights);
|
||||
extractor::files::writeEdgeBasedNodeWeightsDurations(
|
||||
config.GetPath(".osrm.enw"), edge_based_node_weights, edge_based_node_durations);
|
||||
extractor::files::writeEdgeBasedNodeWeights(config.GetPath(".osrm.enw"),
|
||||
edge_based_node_weights);
|
||||
TIMER_STOP(timer_write_node_weights);
|
||||
util::Log() << "Done writing. (" << TIMER_SEC(timer_write_node_weights) << ")";
|
||||
|
||||
@@ -735,7 +733,6 @@ EdgeID Extractor::BuildEdgeExpandedGraph(
|
||||
std::vector<EdgeBasedNodeSegment> &edge_based_node_segments,
|
||||
std::vector<bool> &node_is_startpoint,
|
||||
std::vector<EdgeWeight> &edge_based_node_weights,
|
||||
std::vector<EdgeDuration> &edge_based_node_durations,
|
||||
util::DeallocatingVector<EdgeBasedEdge> &edge_based_edge_list,
|
||||
std::uint32_t &connectivity_checksum)
|
||||
{
|
||||
@@ -785,7 +782,6 @@ EdgeID Extractor::BuildEdgeExpandedGraph(
|
||||
edge_based_graph_factory.GetEdgeBasedNodeSegments(edge_based_node_segments);
|
||||
edge_based_graph_factory.GetStartPointMarkers(node_is_startpoint);
|
||||
edge_based_graph_factory.GetEdgeBasedNodeWeights(edge_based_node_weights);
|
||||
edge_based_graph_factory.GetEdgeBasedNodeDurations(edge_based_node_durations);
|
||||
connectivity_checksum = edge_based_graph_factory.GetConnectivityChecksum();
|
||||
|
||||
return number_of_edge_based_nodes;
|
||||
|
||||
@@ -263,7 +263,7 @@ NAN_METHOD(Engine::nearest) //
|
||||
|
||||
// clang-format off
|
||||
/**
|
||||
* Computes duration and distance tables for the given locations. Allows for both symmetric and asymmetric
|
||||
* Computes duration tables for the given locations. Allows for both symmetric and asymmetric
|
||||
* tables.
|
||||
*
|
||||
* @name table
|
||||
@@ -274,20 +274,17 @@ NAN_METHOD(Engine::nearest) //
|
||||
* Can be `null` or an array of `[{value},{range}]` with `integer 0 .. 360,integer 0 .. 180`.
|
||||
* @param {Array} [options.radiuses] Limits the coordinate snapping to streets in the given radius in meters. Can be `null` (unlimited, default) or `double >= 0`.
|
||||
* @param {Array} [options.hints] Hints for the coordinate snapping. Array of base64 encoded strings.
|
||||
* @param {Array} [options.sources] An array of `index` elements (`0 <= integer < #coordinates`) to use
|
||||
* location with given index as source. Default is to use all.
|
||||
* @param {Array} [options.destinations] An array of `index` elements (`0 <= integer < #coordinates`) to use location with given index as destination. Default is to use all.
|
||||
* @param {Array} [options.sources] An array of `index` elements (`0 <= integer < #coordinates`) to
|
||||
* use
|
||||
* location with given index as source. Default is to use all.
|
||||
* @param {Array} [options.destinations] An array of `index` elements (`0 <= integer <
|
||||
* #coordinates`) to use location with given index as destination. Default is to use all.
|
||||
* @param {Array} [options.approaches] Keep waypoints on curb side. Can be `null` (unrestricted, default) or `curb`.
|
||||
* @param {Array} [options.annotations] An array of the table types to return. Values can be `duration` or `distance` or both. If no annotations parameter is added, the default is to return the `durations` table. If `annotations=distance` or `annotations=duration,distance` is requested when running a MLD router, a `NotImplemented` error will be returned.
|
||||
|
||||
* @param {Function} callback
|
||||
*
|
||||
* @returns {Object} containing `durations`, `sources`, and `destinations`.
|
||||
* **`durations`**: array of arrays that stores the matrix in row-major order. `durations[i][j]` gives the travel time from the i-th waypoint to the j-th waypoint.
|
||||
* Values are given in seconds.
|
||||
* **`distances`**: array of arrays that stores the matrix in row-major order. `distances[i][j]` gives the travel time from the i-th waypoint to the j-th waypoint.
|
||||
* Values are given in meters. Note that computing the `distances` table is currently only implemented for CH. If `annotations=distance` or
|
||||
* `annotations=duration,distance` is requested when running a MLD router, a `NotImplemented` error will be returned.
|
||||
* **`sources`**: array of [`Ẁaypoint`](#waypoint) objects describing all sources in order.
|
||||
* **`destinations`**: array of [`Ẁaypoint`](#waypoint) objects describing all destinations in order.
|
||||
*
|
||||
@@ -302,7 +299,6 @@ NAN_METHOD(Engine::nearest) //
|
||||
* };
|
||||
* osrm.table(options, function(err, response) {
|
||||
* console.log(response.durations); // array of arrays, matrix in row-major order
|
||||
* console.log(response.distances); // array of arrays, matrix in row-major order (currently only implemented for CH router)
|
||||
* console.log(response.sources); // array of Waypoint objects
|
||||
* console.log(response.destinations); // array of Waypoint objects
|
||||
* });
|
||||
|
||||
@@ -144,16 +144,6 @@ int Partitioner::Run(const PartitionerConfig &config)
|
||||
renumber(node_data, permutation);
|
||||
extractor::files::writeNodeData(config.GetPath(".osrm.ebg_nodes"), node_data);
|
||||
}
|
||||
{
|
||||
std::vector<EdgeWeight> node_weights;
|
||||
std::vector<EdgeDuration> node_durations;
|
||||
extractor::files::readEdgeBasedNodeWeightsDurations(
|
||||
config.GetPath(".osrm.enw"), node_weights, node_durations);
|
||||
util::inplacePermutation(node_weights.begin(), node_weights.end(), permutation);
|
||||
util::inplacePermutation(node_durations.begin(), node_durations.end(), permutation);
|
||||
extractor::files::writeEdgeBasedNodeWeightsDurations(
|
||||
config.GetPath(".osrm.enw"), node_weights, node_durations);
|
||||
}
|
||||
{
|
||||
const auto &filename = config.GetPath(".osrm.maneuver_overrides");
|
||||
std::vector<extractor::StorageManeuverOverride> maneuver_overrides;
|
||||
|
||||
@@ -66,7 +66,7 @@ struct RegionHandle
|
||||
{
|
||||
std::unique_ptr<SharedMemory> memory;
|
||||
char *data_ptr;
|
||||
std::uint16_t shm_key;
|
||||
std::uint8_t shm_key;
|
||||
};
|
||||
|
||||
auto setupRegion(SharedRegionRegister &shared_register, const DataLayout &layout)
|
||||
@@ -554,7 +554,7 @@ void Storage::PopulateUpdatableData(const SharedDataIndex &index)
|
||||
{
|
||||
auto graph_view = make_multi_level_graph_view(index, "/mld/multilevelgraph");
|
||||
std::uint32_t graph_connectivity_checksum = 0;
|
||||
customizer::files::readGraph(
|
||||
partitioner::files::readGraph(
|
||||
config.GetPath(".osrm.mldgr"), graph_view, graph_connectivity_checksum);
|
||||
|
||||
auto turns_connectivity_checksum =
|
||||
|
||||
+10
-44
@@ -1,8 +1,6 @@
|
||||
#include "storage/serialization.hpp"
|
||||
#include "storage/shared_memory.hpp"
|
||||
#include "storage/shared_monitor.hpp"
|
||||
#include "storage/storage.hpp"
|
||||
|
||||
#include "osrm/exception.hpp"
|
||||
#include "util/log.hpp"
|
||||
#include "util/meminfo.hpp"
|
||||
@@ -27,7 +25,7 @@ void deleteRegion(const storage::SharedRegionRegister::ShmKey key)
|
||||
}
|
||||
}
|
||||
|
||||
void listRegions(bool show_blocks)
|
||||
void listRegions()
|
||||
{
|
||||
osrm::util::Log() << "name\tshm key\ttimestamp\tsize";
|
||||
if (!storage::SharedMonitor<storage::SharedRegionRegister>::exists())
|
||||
@@ -45,23 +43,6 @@ void listRegions(bool show_blocks)
|
||||
auto shm = osrm::storage::makeSharedMemory(region.shm_key);
|
||||
osrm::util::Log() << name << "\t" << static_cast<int>(region.shm_key) << "\t"
|
||||
<< region.timestamp << "\t" << shm->Size();
|
||||
|
||||
if (show_blocks)
|
||||
{
|
||||
using namespace storage;
|
||||
auto memory = makeSharedMemory(region.shm_key);
|
||||
io::BufferReader reader(reinterpret_cast<char *>(memory->Ptr()), memory->Size());
|
||||
|
||||
DataLayout layout;
|
||||
serialization::read(reader, layout);
|
||||
|
||||
std::vector<std::string> block_names;
|
||||
layout.List("", std::back_inserter(block_names));
|
||||
for (auto &name : block_names)
|
||||
{
|
||||
osrm::util::Log() << " " << name << " " << layout.GetBlockSize(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +63,7 @@ void springClean()
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto key : util::irange<storage::SharedRegionRegister::RegionID>(0, storage::SharedRegionRegister::MAX_SHM_KEYS))
|
||||
for (auto key : util::irange<std::uint8_t>(0, storage::SharedRegionRegister::MAX_SHM_KEYS))
|
||||
{
|
||||
deleteRegion(key);
|
||||
}
|
||||
@@ -98,7 +79,6 @@ bool generateDataStoreOptions(const int argc,
|
||||
int &max_wait,
|
||||
std::string &dataset_name,
|
||||
bool &list_datasets,
|
||||
bool &list_blocks,
|
||||
bool &only_metric)
|
||||
{
|
||||
// declare a group of options that will be allowed only on command line
|
||||
@@ -129,18 +109,12 @@ bool generateDataStoreOptions(const int argc,
|
||||
->default_value(false)
|
||||
->implicit_value(true),
|
||||
"List all OSRM datasets currently in memory") //
|
||||
("list-blocks",
|
||||
boost::program_options::value<bool>(&list_blocks)
|
||||
("only-metric",
|
||||
boost::program_options::value<bool>(&only_metric)
|
||||
->default_value(false)
|
||||
->implicit_value(true),
|
||||
"List all OSRM datasets currently in memory")(
|
||||
"only-metric",
|
||||
boost::program_options::value<bool>(&only_metric)
|
||||
->default_value(false)
|
||||
->implicit_value(true),
|
||||
"Only reload the metric data without updating the full dataset. This is an "
|
||||
"optimization "
|
||||
"for traffic updates.");
|
||||
"Only reload the metric data without updating the full dataset. This is an optimization "
|
||||
"for traffic updates.");
|
||||
|
||||
// hidden options, will be allowed on command line but will not be shown to the user
|
||||
boost::program_options::options_description hidden_options("Hidden options");
|
||||
@@ -235,26 +209,18 @@ int main(const int argc, const char *argv[]) try
|
||||
int max_wait = -1;
|
||||
std::string dataset_name;
|
||||
bool list_datasets = false;
|
||||
bool list_blocks = false;
|
||||
bool only_metric = false;
|
||||
if (!generateDataStoreOptions(argc,
|
||||
argv,
|
||||
verbosity,
|
||||
base_path,
|
||||
max_wait,
|
||||
dataset_name,
|
||||
list_datasets,
|
||||
list_blocks,
|
||||
only_metric))
|
||||
if (!generateDataStoreOptions(
|
||||
argc, argv, verbosity, base_path, max_wait, dataset_name, list_datasets, only_metric))
|
||||
{
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
util::LogPolicy::GetInstance().SetLevel(verbosity);
|
||||
|
||||
if (list_datasets || list_blocks)
|
||||
if (list_datasets)
|
||||
{
|
||||
listRegions(list_blocks);
|
||||
listRegions();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
+15
-17
@@ -517,6 +517,7 @@ updateConditionalTurns(std::vector<TurnPenalty> &turn_weight_penalties,
|
||||
{
|
||||
if (IsRestrictionValid(time_zone_handler, penalty))
|
||||
{
|
||||
std::cout << "Disabling: " << penalty.turn_offset << std::endl;
|
||||
turn_weight_penalties[penalty.turn_offset] = INVALID_TURN_PENALTY;
|
||||
updated_turns.push_back(penalty.turn_offset);
|
||||
}
|
||||
@@ -525,20 +526,20 @@ updateConditionalTurns(std::vector<TurnPenalty> &turn_weight_penalties,
|
||||
}
|
||||
}
|
||||
|
||||
EdgeID
|
||||
Updater::LoadAndUpdateEdgeExpandedGraph(std::vector<extractor::EdgeBasedEdge> &edge_based_edge_list,
|
||||
std::vector<EdgeWeight> &node_weights,
|
||||
std::uint32_t &connectivity_checksum) const
|
||||
Updater::NumNodesAndEdges Updater::LoadAndUpdateEdgeExpandedGraph() const
|
||||
{
|
||||
std::vector<EdgeDuration> node_durations(node_weights.size());
|
||||
return LoadAndUpdateEdgeExpandedGraph(
|
||||
edge_based_edge_list, node_weights, node_durations, connectivity_checksum);
|
||||
std::vector<EdgeWeight> node_weights;
|
||||
std::vector<extractor::EdgeBasedEdge> edge_based_edge_list;
|
||||
std::uint32_t connectivity_checksum;
|
||||
auto number_of_edge_based_nodes = Updater::LoadAndUpdateEdgeExpandedGraph(
|
||||
edge_based_edge_list, node_weights, connectivity_checksum);
|
||||
return std::make_tuple(
|
||||
number_of_edge_based_nodes, std::move(edge_based_edge_list), connectivity_checksum);
|
||||
}
|
||||
|
||||
EdgeID
|
||||
Updater::LoadAndUpdateEdgeExpandedGraph(std::vector<extractor::EdgeBasedEdge> &edge_based_edge_list,
|
||||
std::vector<EdgeWeight> &node_weights,
|
||||
std::vector<EdgeDuration> &node_durations,
|
||||
std::uint32_t &connectivity_checksum) const
|
||||
{
|
||||
TIMER_START(load_edges);
|
||||
@@ -547,9 +548,6 @@ Updater::LoadAndUpdateEdgeExpandedGraph(std::vector<extractor::EdgeBasedEdge> &e
|
||||
std::vector<util::Coordinate> coordinates;
|
||||
extractor::PackedOSMIDs osm_node_ids;
|
||||
|
||||
extractor::files::readEdgeBasedNodeWeightsDurations(
|
||||
config.GetPath(".osrm.enw"), node_weights, node_durations);
|
||||
|
||||
extractor::files::readEdgeBasedGraph(config.GetPath(".osrm.ebg"),
|
||||
number_of_edge_based_nodes,
|
||||
edge_based_edge_list,
|
||||
@@ -745,12 +743,12 @@ Updater::LoadAndUpdateEdgeExpandedGraph(std::vector<extractor::EdgeBasedEdge> &e
|
||||
accumulated_segment_data[updated_iter - updated_segments.begin()];
|
||||
|
||||
// Update the node-weight cache. This is the weight of the edge-based-node
|
||||
// only, it doesn't include the turn. We may visit the same node multiple times,
|
||||
// but we should always assign the same value here.
|
||||
BOOST_ASSERT(edge.source < node_weights.size());
|
||||
node_weights[edge.source] =
|
||||
node_weights[edge.source] & 0x80000000 ? new_weight | 0x80000000 : new_weight;
|
||||
node_durations[edge.source] = new_duration;
|
||||
// only,
|
||||
// it doesn't include the turn. We may visit the same node multiple times,
|
||||
// but
|
||||
// we should always assign the same value here.
|
||||
if (node_weights.size() > 0)
|
||||
node_weights[edge.source] = new_weight;
|
||||
|
||||
// We found a zero-speed edge, so we'll skip this whole edge-based-edge
|
||||
// which
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
#include <mapbox/cheap_ruler.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
@@ -20,49 +18,6 @@ namespace util
|
||||
namespace coordinate_calculation
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// earth radius varies between 6,356.750-6,378.135 km (3,949.901-3,963.189mi)
|
||||
// The IUGG value for the equatorial radius is 6378.137 km (3963.19 miles)
|
||||
const constexpr double EARTH_RADIUS = 6372797.560856;
|
||||
|
||||
class CheapRulerContainer
|
||||
{
|
||||
public:
|
||||
CheapRulerContainer(const int number_of_rulers)
|
||||
: cheap_ruler_cache(number_of_rulers, mapbox::cheap_ruler::CheapRuler(0)),
|
||||
step(90.0 * COORDINATE_PRECISION / number_of_rulers)
|
||||
{
|
||||
for (int n = 0; n < number_of_rulers; n++)
|
||||
{
|
||||
cheap_ruler_cache[n] = mapbox::cheap_ruler::CheapRuler(
|
||||
step * (n + 0.5) / COORDINATE_PRECISION, mapbox::cheap_ruler::CheapRuler::Meters);
|
||||
}
|
||||
};
|
||||
|
||||
mapbox::cheap_ruler::CheapRuler &getRuler(const FixedLatitude lat_1, const FixedLatitude lat_2)
|
||||
{
|
||||
auto lat = (lat_1 + lat_2) / util::FixedLatitude{2};
|
||||
return getRuler(lat);
|
||||
}
|
||||
|
||||
mapbox::cheap_ruler::CheapRuler &getRuler(const FixedLatitude lat)
|
||||
{
|
||||
BOOST_ASSERT(step > 2);
|
||||
// the |lat| > 0 -> |lat|-1 > -1 -> (|lat|-1)/step > -1/step > -1/2 >= -1 -> bin >= 0
|
||||
std::size_t bin = (std::abs(static_cast<int>(lat)) - 1) / step;
|
||||
BOOST_ASSERT(bin < cheap_ruler_cache.size());
|
||||
return cheap_ruler_cache[bin];
|
||||
};
|
||||
|
||||
private:
|
||||
std::vector<mapbox::cheap_ruler::CheapRuler> cheap_ruler_cache;
|
||||
const int step;
|
||||
};
|
||||
static CheapRulerContainer cheap_ruler_container(1800);
|
||||
} // namespace
|
||||
|
||||
// Does not project the coordinates!
|
||||
std::uint64_t squaredEuclideanDistance(const Coordinate lhs, const Coordinate rhs)
|
||||
{
|
||||
@@ -77,20 +32,6 @@ std::uint64_t squaredEuclideanDistance(const Coordinate lhs, const Coordinate rh
|
||||
return result;
|
||||
}
|
||||
|
||||
// Uses method described here:
|
||||
// https://www.gpo.gov/fdsys/pkg/CFR-2005-title47-vol4/pdf/CFR-2005-title47-vol4-sec73-208.pdf
|
||||
// should be within 0.1% or so of Vincenty method (assuming 19 buckets are enough)
|
||||
// Should be more faster and more precise than Haversine
|
||||
double fccApproximateDistance(const Coordinate coordinate_1, const Coordinate coordinate_2)
|
||||
{
|
||||
const auto lon1 = static_cast<double>(util::toFloating(coordinate_1.lon));
|
||||
const auto lat1 = static_cast<double>(util::toFloating(coordinate_1.lat));
|
||||
const auto lon2 = static_cast<double>(util::toFloating(coordinate_2.lon));
|
||||
const auto lat2 = static_cast<double>(util::toFloating(coordinate_2.lat));
|
||||
return cheap_ruler_container.getRuler(coordinate_1.lat, coordinate_2.lat)
|
||||
.distance({lon1, lat1}, {lon2, lat2});
|
||||
}
|
||||
|
||||
double haversineDistance(const Coordinate coordinate_1, const Coordinate coordinate_2)
|
||||
{
|
||||
auto lon1 = static_cast<int>(coordinate_1.lon);
|
||||
@@ -117,7 +58,7 @@ double haversineDistance(const Coordinate coordinate_1, const Coordinate coordin
|
||||
const double aharv = std::pow(std::sin(dlat / 2.0), 2.0) +
|
||||
std::cos(dlat1) * std::cos(dlat2) * std::pow(std::sin(dlong / 2.), 2);
|
||||
const double charv = 2. * std::atan2(std::sqrt(aharv), std::sqrt(1.0 - aharv));
|
||||
return EARTH_RADIUS * charv;
|
||||
return detail::EARTH_RADIUS * charv;
|
||||
}
|
||||
|
||||
double greatCircleDistance(const Coordinate coordinate_1, const Coordinate coordinate_2)
|
||||
@@ -138,7 +79,7 @@ double greatCircleDistance(const Coordinate coordinate_1, const Coordinate coord
|
||||
|
||||
const double x_value = (float_lon2 - float_lon1) * std::cos((float_lat1 + float_lat2) / 2.0);
|
||||
const double y_value = float_lat2 - float_lat1;
|
||||
return std::hypot(x_value, y_value) * EARTH_RADIUS;
|
||||
return std::hypot(x_value, y_value) * detail::EARTH_RADIUS;
|
||||
}
|
||||
|
||||
double perpendicularDistance(const Coordinate segment_source,
|
||||
@@ -482,6 +423,6 @@ double computeArea(const std::vector<Coordinate> &polygon)
|
||||
return area / 2.;
|
||||
}
|
||||
|
||||
} // namespace coordinate_calculation
|
||||
} // namespace util
|
||||
} // namespace osrm
|
||||
} // ns coordinate_calculation
|
||||
} // ns util
|
||||
} // ns osrm
|
||||
|
||||
@@ -159,7 +159,6 @@
|
||||
{"key": "maxspeed", "value": "DE:rural"},
|
||||
{"key": "maxspeed", "value": "DE:motorway"},
|
||||
{"key": "maxspeed", "value": "DK:rural"},
|
||||
{"key": "maxspeed", "value": "FR:rural"},
|
||||
{"key": "maxspeed", "value": "GB:nsl_single"},
|
||||
{"key": "maxspeed", "value": "GB:nsl_dual"},
|
||||
{"key": "maxspeed", "value": "GB:motorway"},
|
||||
|
||||
@@ -10,7 +10,7 @@ exports.three_test_coordinates = [[7.41337, 43.72956],
|
||||
|
||||
exports.two_test_coordinates = exports.three_test_coordinates.slice(0, 2)
|
||||
|
||||
exports.test_tile = {'at': [17059, 11948, 15], 'size': 148750};
|
||||
exports.test_tile = {'at': [17059, 11948, 15], 'size': 168571};
|
||||
|
||||
// Test files generated by the routing engine; check test/data
|
||||
if (process.env.OSRM_DATA_PATH !== undefined) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user