Compare commits
118 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 49e09294bf | |||
| 4a4d7ac62f | |||
| 91a24bf537 | |||
| 01f050cebb | |||
| 7ff8428ad1 | |||
| ddb25cdf22 | |||
| 524f8cc312 | |||
| d9ecb8693f | |||
| 0bf0535a8a | |||
| 2cc32dcc88 | |||
| 8ba516c17e | |||
| 4e0c018dff | |||
| 6bd74c287b | |||
| 5597415f28 | |||
| 5476f6ab27 | |||
| 0971f06193 | |||
| 85515f063a | |||
| 69d7825542 | |||
| 9b779c704f | |||
| 5bd7d04fe3 | |||
| 0f78f7b2cc | |||
| 8473be69d2 | |||
| c0124f7d77 | |||
| b630b4e32a | |||
| 89fabc1b9c | |||
| a649a8a5cf | |||
| f928956584 | |||
| 7ff68792d7 | |||
| 3088dd0342 | |||
| d2590989f5 | |||
| 3a7b377586 | |||
| 82b5648c97 | |||
| 1628a3c4d5 | |||
| e5d8319c43 | |||
| 06699132eb | |||
| 918e794d6a | |||
| 8dd8ee1fc2 | |||
| c3d0efda93 | |||
| 3b4e2e83ef | |||
| c459530cb6 | |||
| 2a15e6dec8 | |||
| 1a1293608d | |||
| 318df9deaa | |||
| cacb162520 | |||
| 105709cb43 | |||
| 9695fa7941 | |||
| fd9bebbfa7 | |||
| db18e8669f | |||
| 9b4a4fdd82 | |||
| cdc7e5f021 | |||
| adc87149e2 | |||
| 8adbfe06ed | |||
| ae805f9ec8 | |||
| be24689b0f | |||
| 41c31a2388 | |||
| 8b866502d1 | |||
| 7837cd61df | |||
| b573485c31 | |||
| 4e68f3a7e1 | |||
| 5ba26d3d6d | |||
| a6cf2eee7e | |||
| 6843eb1479 | |||
| b331885d3c | |||
| 73716bd651 | |||
| 9edd161da3 | |||
| 1bc3ff6491 | |||
| b0f2ef287e | |||
| 16f53ff81a | |||
| 7a260dc2ba | |||
| 1c0d951f5e | |||
| 77a740c0fb | |||
| 61101db8cf | |||
| b51632a2fb | |||
| 9d10503a9c | |||
| 7d50e5afe0 | |||
| 549216c792 | |||
| 1990de7dcc | |||
| 89f6e2d55b | |||
| c628ecbf24 | |||
| b6f19cd544 | |||
| 14860b62e9 | |||
| 9970b7d580 | |||
| 7feb79ef91 | |||
| 6f84812903 | |||
| 8085e86170 | |||
| 35973576d9 | |||
| 7740d5d7c0 | |||
| c4eff6cd65 | |||
| 5c4353b46e | |||
| 05a5918909 | |||
| 80ad38bbc6 | |||
| 6bee8866de | |||
| ba92674c6e | |||
| 68019a1fb2 | |||
| 3a9acde2c3 | |||
| a3a7a822e1 | |||
| c7d22c2b92 | |||
| b2aeb47630 | |||
| 0a1d1901cc | |||
| 29db0c80d9 | |||
| 4654751872 | |||
| b5b18d8afc | |||
| 15139c934f | |||
| 7b8619e37c | |||
| f52b5c31b6 | |||
| e98859e4c0 | |||
| c5cc4c5a74 | |||
| e3b831364f | |||
| 730d2b5ef2 | |||
| 16abee1022 | |||
| 69fa52a010 | |||
| e1efa4c6ab | |||
| 17cd1575f6 | |||
| 3cd8e0fef8 | |||
| 397bb694fd | |||
| 473ebfcbf6 | |||
| a06171438e | |||
| ea0881553e |
+1
-2
@@ -15,7 +15,6 @@ branches:
|
|||||||
- master
|
- master
|
||||||
# enable building tags
|
# enable building tags
|
||||||
- /^v\d+\.\d+(\.\d+)?(-\S*)?$/
|
- /^v\d+\.\d+(\.\d+)?(-\S*)?$/
|
||||||
- "5.17"
|
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
yarn: true
|
yarn: true
|
||||||
@@ -366,7 +365,7 @@ install:
|
|||||||
script:
|
script:
|
||||||
- if [[ $TARGET_ARCH == armhf ]] ; then echo "Skip tests for $TARGET_ARCH" && exit 0 ; fi
|
- if [[ $TARGET_ARCH == armhf ]] ; then echo "Skip tests for $TARGET_ARCH" && exit 0 ; fi
|
||||||
- make -C test/data benchmark
|
- make -C test/data benchmark
|
||||||
- ./example/build/osrm-example test/data/mld/monaco.osrm
|
# - ./example/build/osrm-example test/data/mld/monaco.osrm
|
||||||
# All tests assume to be run from the build directory
|
# All tests assume to be run from the build directory
|
||||||
- pushd ${OSRM_BUILD_DIR}
|
- pushd ${OSRM_BUILD_DIR}
|
||||||
- ./unit_tests/library-tests
|
- ./unit_tests/library-tests
|
||||||
|
|||||||
+22
-4
@@ -1,9 +1,27 @@
|
|||||||
# 5.17.3
|
# UNRELEASED
|
||||||
- Changes from 5.17.2:
|
- 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)
|
||||||
|
- CHANGED: Use Grisu2 for serializing floating point numbers. [#5188](https://github.com/Project-OSRM/osrm-backend/pull/5188)
|
||||||
|
- ADDED: Node bindings can return pre-rendered JSON buffer. [#5189](https://github.com/Project-OSRM/osrm-backend/pull/5189)
|
||||||
- Bugfixes:
|
- Bugfixes:
|
||||||
- FIXED: Increased probability of returning alternatives and quality of the ones returned for MLD. [#5048](https://github.com/Project-OSRM/osrm-backend/issues/5048)
|
- FIXED: collapsing of ExitRoundabout instructions [#5114](https://github.com/Project-OSRM/osrm-backend/issues/5114)
|
||||||
|
- Misc:
|
||||||
|
- CHANGED: Support up to 512 named shared memory regions [#5185](https://github.com/Project-OSRM/osrm-backend/pull/5185)
|
||||||
|
|
||||||
# 5.17.2
|
# 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
|
||||||
- Changes from 5.17.0:
|
- Changes from 5.17.0:
|
||||||
- Bugfixes:
|
- Bugfixes:
|
||||||
- FIXED: Do not combine a segregated edge with a roundabout [#5039](https://github.com/Project-OSRM/osrm-backend/issues/5039)
|
- FIXED: Do not combine a segregated edge with a roundabout [#5039](https://github.com/Project-OSRM/osrm-backend/issues/5039)
|
||||||
|
|||||||
+24
-13
@@ -167,7 +167,7 @@ add_executable(osrm-customize src/tools/customize.cpp)
|
|||||||
add_executable(osrm-contract src/tools/contract.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-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_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_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_extract src/osrm/extractor.cpp $<TARGET_OBJECTS:EXTRACTOR> $<TARGET_OBJECTS:MICROTAR> $<TARGET_OBJECTS:UTIL>)
|
||||||
add_library(osrm_guidance $<TARGET_OBJECTS:GUIDANCE> $<TARGET_OBJECTS:UTIL>)
|
add_library(osrm_guidance $<TARGET_OBJECTS:GUIDANCE> $<TARGET_OBJECTS:UTIL>)
|
||||||
@@ -416,11 +416,30 @@ if(UNIX AND NOT APPLE)
|
|||||||
set(MAYBE_RT_LIBRARY -lrt)
|
set(MAYBE_RT_LIBRARY -lrt)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Disallow deprecated protozero APIs
|
|
||||||
add_definitions(-DPROTOZERO_STRICT_API)
|
|
||||||
|
|
||||||
find_package(Threads REQUIRED)
|
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
|
# if mason is enabled no find_package calls are made
|
||||||
# to ensure that we are only compiling and linking against
|
# to ensure that we are only compiling and linking against
|
||||||
# fully portable mason packages
|
# fully portable mason packages
|
||||||
@@ -554,14 +573,6 @@ else()
|
|||||||
include_directories(SYSTEM ${OSMIUM_INCLUDE_DIR})
|
include_directories(SYSTEM ${OSMIUM_INCLUDE_DIR})
|
||||||
endif()
|
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
|
# 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"))
|
if(ENABLE_CCACHE AND (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU"))
|
||||||
find_program(CCACHE_FOUND ccache)
|
find_program(CCACHE_FOUND ccache)
|
||||||
@@ -862,4 +873,4 @@ if (ENABLE_NODE_BINDINGS)
|
|||||||
endforeach()
|
endforeach()
|
||||||
add_library(check-headers STATIC EXCLUDE_FROM_ALL ${sources})
|
add_library(check-headers STATIC EXCLUDE_FROM_ALL ${sources})
|
||||||
set_target_properties(check-headers PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${check_headers_dir})
|
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:
|
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
|
- Nearest - Snaps coordinates to the street network and returns the nearest matches
|
||||||
- Route - Finds the fastest route between coordinates
|
- Route - Finds the fastest route between coordinates
|
||||||
- Table - Computes the duration of the fastest route between all pairs of supplied coordinates
|
- Table - Computes the duration or distances of the fastest route between all pairs of supplied coordinates
|
||||||
- Match - Snaps noisy GPS traces to the road network in the most plausible way
|
- Match - Snaps noisy GPS traces to the road network in the most plausible way
|
||||||
- Trip - Solves the Traveling Salesman Problem using a greedy heuristic
|
- Trip - Solves the Traveling Salesman Problem using a greedy heuristic
|
||||||
- Tile - Generates Mapbox Vector Tiles with internal routing metadata
|
- 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',
|
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',
|
todo: '--strict --tags @todo --require features/support --require features/step_definitions',
|
||||||
all: '--strict --require features/support --require features/step_definitions',
|
all: '--strict --require features/support --require features/step_definitions',
|
||||||
mld: '--strict --tags ~@stress --tags ~@todo --require features/support --require features/step_definitions -f progress'
|
mld: '--strict --tags ~@stress --tags ~@todo --tags ~@ch --require features/support --require features/step_definitions -f progress'
|
||||||
};
|
};
|
||||||
|
|||||||
+117
-8
@@ -222,13 +222,13 @@ curl 'http://router.project-osrm.org/route/v1/driving/13.388860,52.517037;13.397
|
|||||||
|
|
||||||
### Table service
|
### Table service
|
||||||
|
|
||||||
Computes the duration of the fastest route between all pairs of supplied coordinates.
|
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.
|
||||||
|
|
||||||
```endpoint
|
```endpoint
|
||||||
GET /table/v1/{profile}/{coordinates}?{sources}=[{elem}...];&destinations=[{elem}...]
|
GET /table/v1/{profile}/{coordinates}?{sources}=[{elem}...];&{destinations}=[{elem}...]&annotations={duration|distance|duration,distance}
|
||||||
```
|
```
|
||||||
|
|
||||||
**Coordinates**
|
**Options**
|
||||||
|
|
||||||
In addition to the [general options](#general-options) the following options are supported for this service:
|
In addition to the [general options](#general-options) the following options are supported for this service:
|
||||||
|
|
||||||
@@ -236,6 +236,8 @@ 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. |
|
|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.|
|
|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**
|
Unlike other array encoded options, the length of `sources` and `destinations` can be **smaller or equal**
|
||||||
to number of input locations;
|
to number of input locations;
|
||||||
@@ -253,14 +255,23 @@ sources=0;5;7&destinations=5;1;4;2;3;6
|
|||||||
#### Example Request
|
#### Example Request
|
||||||
|
|
||||||
```curl
|
```curl
|
||||||
# Returns a 3x3 matrix:
|
# 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'
|
curl 'http://router.project-osrm.org/table/v1/driving/13.388860,52.517037;13.397634,52.529407;13.428555,52.523219'
|
||||||
|
|
||||||
# Returns a 1x3 matrix
|
# Returns a 1x3 duration matrix
|
||||||
curl 'http://router.project-osrm.org/table/v1/driving/13.388860,52.517037;13.397634,52.529407;13.428555,52.523219?sources=0'
|
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 matrix with from the polyline encoded locations `qikdcB}~dpXkkHz`:
|
# Returns a asymmetric 3x2 duration 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'
|
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**
|
**Response**
|
||||||
@@ -268,17 +279,115 @@ curl 'http://router.project-osrm.org/table/v1/driving/polyline(egs_Iq_aqAppHzbHu
|
|||||||
- `code` if the request was successful `Ok` otherwise see the service dependent and general status codes.
|
- `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
|
- `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.
|
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
|
- `sources` array of `Waypoint` objects describing all sources in order
|
||||||
- `destinations` array of `Waypoint` objects describing all destinations 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:
|
In case of error the following `code`s are supported in addition to the general ones:
|
||||||
|
|
||||||
| Type | Description |
|
| Type | Description |
|
||||||
|-------------------|-----------------|
|
|------------------|-----------------|
|
||||||
| `NoTable` | No route found. |
|
| `NoTable` | No route found. |
|
||||||
|
| `NotImplemented` | This request is not supported |
|
||||||
|
|
||||||
All other properties might be undefined.
|
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
|
### Match service
|
||||||
|
|
||||||
Map matching matches/snaps given GPS points to the road network in the most plausible way.
|
Map matching matches/snaps given GPS points to the road network in the most plausible way.
|
||||||
|
|||||||
+25
-2
@@ -110,8 +110,8 @@ Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refer
|
|||||||
|
|
||||||
### table
|
### table
|
||||||
|
|
||||||
Computes duration tables for the given locations. Allows for both symmetric and asymmetric
|
Computes duration table for the given locations. Allows for both symmetric and asymmetric
|
||||||
tables.
|
tables. Optionally returns distance table.
|
||||||
|
|
||||||
**Parameters**
|
**Parameters**
|
||||||
|
|
||||||
@@ -297,6 +297,29 @@ Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refer
|
|||||||
2) `waypoint_index`: index of the point in the trip.
|
2) `waypoint_index`: index of the point in the trip.
|
||||||
**`trips`**: an array of [`Route`](#route) objects that assemble the trace.
|
**`trips`**: an array of [`Route`](#route) objects that assemble the trace.
|
||||||
|
|
||||||
|
## Plugin behaviour
|
||||||
|
|
||||||
|
All plugins support a second additional object that is available to configure some NodeJS specific behaviours.
|
||||||
|
|
||||||
|
- `plugin_config` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Object literal containing parameters for the trip query.
|
||||||
|
- `plugin_config.format` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)?** The format of the result object to various API calls. Valid options are `object` (default), which returns a standard Javascript object, as described above, and `json_buffer`, which will return a NodeJS **[Buffer](https://nodejs.org/api/buffer.html)** object, containing a JSON string. The latter has the advantage that it can be immediately serialized to disk/sent over the network, and the generation of the string is performed outside the main NodeJS event loop. This option is ignored by the `tile` plugin.
|
||||||
|
|
||||||
|
**Examples**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var osrm = new OSRM('network.osrm');
|
||||||
|
var options = {
|
||||||
|
coordinates: [
|
||||||
|
[13.36761474609375, 52.51663871100423],
|
||||||
|
[13.374481201171875, 52.506191342034576]
|
||||||
|
]
|
||||||
|
};
|
||||||
|
osrm.route(options, { format: "json_buffer" }, function(err, response) {
|
||||||
|
if (err) throw err;
|
||||||
|
console.log(response.toString("utf-8"));
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
## Responses
|
## Responses
|
||||||
|
|
||||||
Responses
|
Responses
|
||||||
|
|||||||
+5
-5
@@ -68,7 +68,7 @@ If you want to prioritize certain streets, increase the rate on these.
|
|||||||
|
|
||||||
## Elements
|
## Elements
|
||||||
### api_version
|
### 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 2.
|
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.
|
||||||
|
|
||||||
### Library files
|
### Library files
|
||||||
The folder [profiles/lib/](../profiles/lib/) contains LUA library files for handling many common processing tasks.
|
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
|
Argument | Description
|
||||||
---------|-------------------------------------------------------
|
---------|-------------------------------------------------------
|
||||||
profile | The configuration table you returned in `setup`.
|
profile | The configuration table you returned in `setup`.
|
||||||
node | The input way to process (read-only).
|
way | The input way to process (read-only).
|
||||||
result | The output that you will modify.
|
result | The output that you will modify.
|
||||||
relations| Storage of relations to access relations, where `way` is a member.
|
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 | ""
|
source.lat | Read | Float | ""
|
||||||
target.lon | Read | Float | Co-ordinates of segment end
|
target.lon | Read | Float | Co-ordinates of segment end
|
||||||
target.lat | Read | Float | ""
|
target.lat | Read | Float | ""
|
||||||
target.distance | Read | Float | Length of segment
|
distance | Read | Float | Length of segment
|
||||||
weight | Read/write | Float | Routing weight for this segment
|
weight | Read/write | Float | Routing weight for this segment
|
||||||
duration | Read/write | Float | Duration 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_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_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`
|
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 it from a restricted access road? (See definition in `process_way`)
|
target_restricted | Read | Boolean | Is the target 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_mode | Read | Enum | Travel mode after the turn. Defined in `include/extractor/travel_mode.hpp`
|
||||||
target_is_motorway | Read | Boolean | Is the target road a motorway?
|
target_is_motorway | Read | Boolean | Is the target road a motorway?
|
||||||
target_is_link | Read | Boolean | Is the target road a link?
|
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)
|
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`
|
1. Check out the appropriate release branch `x.y`
|
||||||
2. Make sure `CHANGELOG.md` is up to date.
|
2. Make sure `CHANGELOG.md` is up to date.
|
||||||
3. Make sure the `package.json` 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 :thumbs_up:)
|
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.
|
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.
|
||||||
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
|
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`
|
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`,
|
8. On https://github.com/Project-OSRM/osrm-backend/releases press `Draft a new release`,
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ Feature: Barriers
|
|||||||
| entrance | x |
|
| entrance | x |
|
||||||
| wall | |
|
| wall | |
|
||||||
| fence | |
|
| fence | |
|
||||||
| some_tag | |
|
| some_tag | x |
|
||||||
| block | x |
|
| block | x |
|
||||||
|
|
||||||
Scenario: Bike - Access tag trumphs barriers
|
Scenario: Bike - Access tag trumphs barriers
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
@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)],[()],[()] |
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
@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,3 +48,22 @@ Feature: Car - Handle physical limitation
|
|||||||
| primary | | none | x |
|
| primary | | none | x |
|
||||||
| primary | | no-sign | x |
|
| primary | | no-sign | x |
|
||||||
| primary | | unsigned | 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 |
|
| entrance | x |
|
||||||
| wall | |
|
| wall | |
|
||||||
| fence | |
|
| fence | |
|
||||||
| some_tag | |
|
| some_tag | x |
|
||||||
| block | x |
|
| block | x |
|
||||||
|
|
||||||
Scenario: Foot - Access tag trumphs barriers
|
Scenario: Foot - Access tag trumphs barriers
|
||||||
|
|||||||
@@ -1209,3 +1209,33 @@ Feature: Simple Turns
|
|||||||
| a | c | knob,knob | depart,arrive |
|
| a | c | knob,knob | depart,arrive |
|
||||||
| d | e | soph,soph | depart,arrive |
|
| d | e | soph,soph | depart,arrive |
|
||||||
| d | a | soph,knob,knob | depart,turn left,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,74 +1,88 @@
|
|||||||
var util = require('util');
|
var util = require('util');
|
||||||
|
|
||||||
module.exports = function () {
|
module.exports = function () {
|
||||||
this.When(/^I request a travel time matrix I should get$/, (table, callback) => {
|
const durationsRegex = new RegExp(/^I request a travel time matrix I should get$/);
|
||||||
var NO_ROUTE = 2147483647; // MAX_INT
|
const distancesRegex = new RegExp(/^I request a travel distance matrix I should get$/);
|
||||||
|
|
||||||
var tableRows = table.raw();
|
const DURATIONS_NO_ROUTE = 2147483647; // MAX_INT
|
||||||
|
const DISTANCES_NO_ROUTE = 3.40282e+38; // MAX_FLOAT
|
||||||
|
|
||||||
if (tableRows[0][0] !== '') throw new Error('*** Top-left cell of matrix table must be empty');
|
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));
|
||||||
|
};
|
||||||
|
|
||||||
var waypoints = [],
|
const durationsParse = function(v) { return isNaN(parseInt(v)); };
|
||||||
columnHeaders = tableRows[0].slice(1),
|
const distancesParse = function(v) { return isNaN(parseFloat(v)); };
|
||||||
rowHeaders = tableRows.map((h) => h[0]).slice(1),
|
|
||||||
symmetric = columnHeaders.length == rowHeaders.length && columnHeaders.every((ele, i) => ele === rowHeaders[i]);
|
|
||||||
|
|
||||||
if (symmetric) {
|
function tableParse(table, noRoute, annotation, callback) {
|
||||||
columnHeaders.forEach((nodeName) => {
|
|
||||||
var node = this.findNodeByName(nodeName);
|
const parse = annotation == 'distances' ? distancesParse : durationsParse;
|
||||||
if (!node) throw new Error(util.format('*** unknown node "%s"', nodeName));
|
const params = this.queryParams;
|
||||||
waypoints.push({ coord: node, type: 'loc' });
|
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;
|
||||||
});
|
});
|
||||||
} 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 = [];
|
var testRow = (row, ri, cb) => {
|
||||||
actual.push(table.headers);
|
for (var k in result[ri]) {
|
||||||
|
if (this.FuzzyMatch.match(result[ri][k], row[k])) {
|
||||||
this.reprocessAndLoadData((e) => {
|
result[ri][k] = row[k];
|
||||||
if (e) return callback(e);
|
} else if (row[k] === '' && result[ri][k] === noRoute) {
|
||||||
// compute matrix
|
result[ri][k] = '';
|
||||||
var params = this.queryParams;
|
} else {
|
||||||
|
result[ri][k] = result[ri][k].toString();
|
||||||
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[''];
|
result[ri][''] = row[''];
|
||||||
cb(null, result[ri]);
|
cb(null, result[ri]);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.processRowsAndDiff(table, testRow, callback);
|
this.processRowsAndDiff(table, testRow, callback);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ module.exports = {
|
|||||||
matchRe = want.match(/^\/(.*)\/$/),
|
matchRe = want.match(/^\/(.*)\/$/),
|
||||||
// we use this for matching before/after bearing
|
// we use this for matching before/after bearing
|
||||||
matchBearingListAbs = want.match(/^((\d+)->(\d+))(,(\d+)->(\d+))*\s+\+\-(.+)$/),
|
matchBearingListAbs = want.match(/^((\d+)->(\d+))(,(\d+)->(\d+))*\s+\+\-(.+)$/),
|
||||||
matchIntersectionListAbs = want.match(/^(((((true|false):\d+)\s{0,1})+,{0,1})+;{0,1})+\s+\+\-(.+)$/);
|
matchIntersectionListAbs = want.match(/^(((((true|false):\d+)\s{0,1})+,{0,1})+;{0,1})+\s+\+\-(.+)$/),
|
||||||
|
matchRangeNumbers = want.match(/\d+\+\-\d+/);
|
||||||
|
|
||||||
function inRange(margin, got, want) {
|
function inRange(margin, got, want) {
|
||||||
var fromR = parseFloat(want) - margin,
|
var fromR = parseFloat(want) - margin,
|
||||||
@@ -105,6 +106,11 @@ module.exports = {
|
|||||||
return inRange(margin, got, matchAbs[1]);
|
return inRange(margin, got, matchAbs[1]);
|
||||||
} else if (matchRe) { // regex: /a,b,.*/
|
} else if (matchRe) { // regex: /a,b,.*/
|
||||||
return got.match(matchRe[1]);
|
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 {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ module.exports = function () {
|
|||||||
.defer(rimraf, this.scenarioLogFile)
|
.defer(rimraf, this.scenarioLogFile)
|
||||||
.awaitAll(callback);
|
.awaitAll(callback);
|
||||||
// uncomment to get path to logfile
|
// 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) => {
|
this.After((scenario, callback) => {
|
||||||
|
|||||||
@@ -226,3 +226,22 @@ Feature: Distance calculation
|
|||||||
| x | v | xv,xv | 424m +-1 |
|
| x | v | xv,xv | 424m +-1 |
|
||||||
| x | w | xw,xw | 360m +-1 |
|
| x | w | xw,xw | 360m +-1 |
|
||||||
| x | y | xy,xy | 316m +-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,14 +1,12 @@
|
|||||||
@matrix @testbot
|
@matrix @testbot
|
||||||
Feature: Basic Distance Matrix
|
Feature: Basic Distance Matrix
|
||||||
# note that results are travel time, specified in 1/10th of seconds
|
# note that results of travel distance are in metres
|
||||||
# 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:
|
Background:
|
||||||
Given the profile "testbot"
|
Given the profile "testbot"
|
||||||
And the partition extra arguments "--small-component-size 1 --max-cell-sizes 2,4,8,16"
|
And the partition extra arguments "--small-component-size 1 --max-cell-sizes 2,4,8,16"
|
||||||
|
|
||||||
Scenario: Testbot - Travel time matrix of minimal network
|
Scenario: Testbot - Travel distance matrix of minimal network
|
||||||
Given the node map
|
Given the node map
|
||||||
"""
|
"""
|
||||||
a b
|
a b
|
||||||
@@ -18,12 +16,99 @@ Feature: Basic Distance Matrix
|
|||||||
| nodes |
|
| nodes |
|
||||||
| ab |
|
| ab |
|
||||||
|
|
||||||
When I request a travel time matrix I should get
|
When I request a travel distance matrix I should get
|
||||||
| | a | b |
|
| | a | b |
|
||||||
| a | 0 | 10 |
|
| a | 0 | 100+-1 |
|
||||||
| b | 10 | 0 |
|
| b | 100+-1 | 0 |
|
||||||
|
|
||||||
Scenario: Testbot - Travel time matrix with different way speeds
|
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
|
||||||
Given the node map
|
Given the node map
|
||||||
"""
|
"""
|
||||||
a b c d
|
a b c d
|
||||||
@@ -35,40 +120,25 @@ Feature: Basic Distance Matrix
|
|||||||
| bc | secondary |
|
| bc | secondary |
|
||||||
| cd | tertiary |
|
| cd | tertiary |
|
||||||
|
|
||||||
When I request a travel time matrix I should get
|
When I request a travel distance matrix I should get
|
||||||
| | a | b | c | d |
|
| | a | b | c | d |
|
||||||
| a | 0 | 10 | 30 | 60 |
|
| a | 0 | 100+-1 | 200+-1 | 300+-1 |
|
||||||
| b | 10 | 0 | 20 | 50 |
|
| b | 100+-1 | 0 | 100+-1 | 200+-1 |
|
||||||
| c | 30 | 20 | 0 | 30 |
|
| c | 200+-1 | 100+-1 | 0 | 100+-1 |
|
||||||
| d | 60 | 50 | 30 | 0 |
|
| d | 300+-1 | 200+-1 | 100+-1 | 0 |
|
||||||
|
|
||||||
When I request a travel time matrix I should get
|
When I request a travel distance matrix I should get
|
||||||
| | a | b | c | d |
|
| | a | b | c | d |
|
||||||
| a | 0 | 10 | 30 | 60 |
|
| a | 0 | 100+-1 | 200+-1 | 300+-1 |
|
||||||
|
|
||||||
When I request a travel time matrix I should get
|
When I request a travel distance matrix I should get
|
||||||
| | a |
|
| | a |
|
||||||
| a | 0 |
|
| a | 0 |
|
||||||
| b | 10 |
|
| b | 100+-1 |
|
||||||
| c | 30 |
|
| c | 200+-1 |
|
||||||
| d | 60 |
|
| d | 300+-1 |
|
||||||
|
|
||||||
Scenario: Testbot - Travel time matrix with fuzzy match
|
Scenario: Testbot - Travel distance matrix of small grid
|
||||||
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
|
Given the node map
|
||||||
"""
|
"""
|
||||||
a b c
|
a b c
|
||||||
@@ -83,14 +153,14 @@ Feature: Basic Distance Matrix
|
|||||||
| be |
|
| be |
|
||||||
| cf |
|
| cf |
|
||||||
|
|
||||||
When I request a travel time matrix I should get
|
When I request a travel distance matrix I should get
|
||||||
| | a | b | e | f |
|
| | a | b | e | f |
|
||||||
| a | 0 | 10 | 20 | 30 |
|
| a | 0 | 100+-1 | 200+-1 | 300+-1 |
|
||||||
| b | 10 | 0 | 10 | 20 |
|
| b | 100+-1 | 0 | 100+-1 | 200+-1 |
|
||||||
| e | 20 | 10 | 0 | 10 |
|
| e | 200+-1 | 100+-1 | 0 | 100+-1 |
|
||||||
| f | 30 | 20 | 10 | 0 |
|
| f | 300+-1 | 200+-1 | 100+-1 | 0 |
|
||||||
|
|
||||||
Scenario: Testbot - Travel time matrix of network with unroutable parts
|
Scenario: Testbot - Travel distance matrix of network with unroutable parts
|
||||||
Given the node map
|
Given the node map
|
||||||
"""
|
"""
|
||||||
a b
|
a b
|
||||||
@@ -100,12 +170,12 @@ Feature: Basic Distance Matrix
|
|||||||
| nodes | oneway |
|
| nodes | oneway |
|
||||||
| ab | yes |
|
| ab | yes |
|
||||||
|
|
||||||
When I request a travel time matrix I should get
|
When I request a travel distance matrix I should get
|
||||||
| | a | b |
|
| | a | b |
|
||||||
| a | 0 | 10 |
|
| a | 0 | 100+-1 |
|
||||||
| b | | 0 |
|
| b | | 0 |
|
||||||
|
|
||||||
Scenario: Testbot - Travel time matrix of network with oneways
|
Scenario: Testbot - Travel distance matrix of network with oneways
|
||||||
Given the node map
|
Given the node map
|
||||||
"""
|
"""
|
||||||
x a b y
|
x a b y
|
||||||
@@ -118,14 +188,14 @@ Feature: Basic Distance Matrix
|
|||||||
| xa | |
|
| xa | |
|
||||||
| by | |
|
| by | |
|
||||||
|
|
||||||
When I request a travel time matrix I should get
|
When I request a travel distance matrix I should get
|
||||||
| | x | y | d | e |
|
| | x | y | d | e |
|
||||||
| x | 0 | 30 | 40 | 30 |
|
| x | 0 | 300+-2 | 400+-2 | 300+-2 |
|
||||||
| y | 50 | 0 | 30 | 20 |
|
| y | 500+-2 | 0 | 300+-2 | 200+-2 |
|
||||||
| d | 20 | 30 | 0 | 30 |
|
| d | 200+-2 | 300+-2 | 0 | 300+-2 |
|
||||||
| e | 30 | 40 | 10 | 0 |
|
| e | 300+-2 | 400+-2 | 100+-2 | 0 |
|
||||||
|
|
||||||
Scenario: Testbot - Rectangular travel time matrix
|
Scenario: Testbot - Rectangular travel distance matrix
|
||||||
Given the node map
|
Given the node map
|
||||||
"""
|
"""
|
||||||
a b c
|
a b c
|
||||||
@@ -140,51 +210,57 @@ Feature: Basic Distance Matrix
|
|||||||
| be |
|
| be |
|
||||||
| cf |
|
| cf |
|
||||||
|
|
||||||
When I request a travel time matrix I should get
|
When I route I should get
|
||||||
| | a | b | e | f |
|
| from | to | distance |
|
||||||
| a | 0 | 10 | 20 | 30 |
|
| 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
|
When I request a travel distance matrix I should get
|
||||||
| | a |
|
| | a | b | e | f |
|
||||||
| a | 0 |
|
| a | 0 | 100+-1 | 200+-1 | 300+-1 |
|
||||||
| b | 10 |
|
|
||||||
| e | 20 |
|
|
||||||
| f | 30 |
|
|
||||||
|
|
||||||
When I request a travel time matrix I should get
|
When I request a travel distance matrix I should get
|
||||||
| | a | b | e | f |
|
| | a |
|
||||||
| a | 0 | 10 | 20 | 30 |
|
| a | 0 |
|
||||||
| b | 10 | 0 | 10 | 20 |
|
| b | 100+-1 |
|
||||||
|
| e | 200+-1 |
|
||||||
|
| f | 300+-1 |
|
||||||
|
|
||||||
When I request a travel time matrix I should get
|
When I request a travel distance matrix I should get
|
||||||
| | a | b |
|
| | a | b | e | f |
|
||||||
| a | 0 | 10 |
|
| a | 0 | 100+-1 | 200+-1 | 300+-1 |
|
||||||
| b | 10 | 0 |
|
| b | 100+-1 | 0 | 100+-1 | 200+-1 |
|
||||||
| e | 20 | 10 |
|
|
||||||
| f | 30 | 20 |
|
|
||||||
|
|
||||||
When I request a travel time matrix I should get
|
When I request a travel distance matrix I should get
|
||||||
| | a | b | e | f |
|
| | a | b |
|
||||||
| a | 0 | 10 | 20 | 30 |
|
| a | 0 | 100+-1 |
|
||||||
| b | 10 | 0 | 10 | 20 |
|
| b | 100+-1 | 0 |
|
||||||
| e | 20 | 10 | 0 | 10 |
|
| e | 200+-1 | 100+-1 |
|
||||||
|
| f | 300+-1 | 200+-1 |
|
||||||
|
|
||||||
When I request a travel time matrix I should get
|
When I request a travel distance matrix I should get
|
||||||
| | a | b | e |
|
| | a | b | e | f |
|
||||||
| a | 0 | 10 | 20 |
|
| a | 0 | 100+-1 | 200+-1 | 300+-1 |
|
||||||
| b | 10 | 0 | 10 |
|
| b | 100+-1 | 0 | 100+-1 | 200+-1 |
|
||||||
| e | 20 | 10 | 0 |
|
| e | 200+-1 | 100+-1 | 0 | 100+-1 |
|
||||||
| f | 30 | 20 | 10 |
|
|
||||||
|
|
||||||
When I request a travel time matrix I should get
|
When I request a travel distance matrix I should get
|
||||||
| | a | b | e | f |
|
| | a | b | e |
|
||||||
| a | 0 | 10 | 20 | 30 |
|
| a | 0 | 100+-1 | 200+-1 |
|
||||||
| b | 10 | 0 | 10 | 20 |
|
| b | 100+-1 | 0 | 100+-1 |
|
||||||
| e | 20 | 10 | 0 | 10 |
|
| e | 200+-1 | 100+-1 | 0 |
|
||||||
| f | 30 | 20 | 10 | 0 |
|
| f | 300+-1 | 200+-1 | 100+-1 |
|
||||||
|
|
||||||
|
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 time 3x2 matrix
|
Scenario: Testbot - Travel distance 3x2 matrix
|
||||||
Given the node map
|
Given the node map
|
||||||
"""
|
"""
|
||||||
a b c
|
a b c
|
||||||
@@ -199,10 +275,11 @@ Feature: Basic Distance Matrix
|
|||||||
| be |
|
| be |
|
||||||
| cf |
|
| cf |
|
||||||
|
|
||||||
When I request a travel time matrix I should get
|
|
||||||
| | b | e | f |
|
When I request a travel distance matrix I should get
|
||||||
| a | 10 | 20 | 30 |
|
| | b | e | f |
|
||||||
| b | 0 | 10 | 20 |
|
| a | 100+-1 | 200+-1 | 300+-1 |
|
||||||
|
| b | 0 | 100+-1 | 200+-1 |
|
||||||
|
|
||||||
Scenario: Testbot - All coordinates are from same small component
|
Scenario: Testbot - All coordinates are from same small component
|
||||||
Given a grid size of 300 meters
|
Given a grid size of 300 meters
|
||||||
@@ -221,10 +298,10 @@ Feature: Basic Distance Matrix
|
|||||||
| da |
|
| da |
|
||||||
| fg |
|
| fg |
|
||||||
|
|
||||||
When I request a travel time matrix I should get
|
When I request a travel distance matrix I should get
|
||||||
| | f | g |
|
| | f | g |
|
||||||
| f | 0 | 30 |
|
| f | 0 | 300+-2 |
|
||||||
| g | 30 | 0 |
|
| g | 300+-2 | 0 |
|
||||||
|
|
||||||
Scenario: Testbot - Coordinates are from different small component and snap to big CC
|
Scenario: Testbot - Coordinates are from different small component and snap to big CC
|
||||||
Given a grid size of 300 meters
|
Given a grid size of 300 meters
|
||||||
@@ -244,14 +321,25 @@ Feature: Basic Distance Matrix
|
|||||||
| fg |
|
| fg |
|
||||||
| hi |
|
| hi |
|
||||||
|
|
||||||
When I request a travel time matrix I should get
|
When I route I should get
|
||||||
| | f | g | h | i |
|
| from | to | distance |
|
||||||
| f | 0 | 30 | 0 | 30 |
|
| f | g | 300m |
|
||||||
| g | 30 | 0 | 30 | 0 |
|
| f | i | 300m |
|
||||||
| h | 0 | 30 | 0 | 30 |
|
| g | f | 300m |
|
||||||
| i | 30 | 0 | 30 | 0 |
|
| g | h | 300m |
|
||||||
|
| h | g | 300m |
|
||||||
|
| h | i | 300m |
|
||||||
|
| i | f | 300m |
|
||||||
|
| i | h | 300m |
|
||||||
|
|
||||||
Scenario: Testbot - Travel time matrix with loops
|
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
|
||||||
Given the node map
|
Given the node map
|
||||||
"""
|
"""
|
||||||
a 1 2 b
|
a 1 2 b
|
||||||
@@ -265,14 +353,15 @@ Feature: Basic Distance Matrix
|
|||||||
| cd | yes |
|
| cd | yes |
|
||||||
| da | yes |
|
| da | yes |
|
||||||
|
|
||||||
When I request a travel time matrix I should get
|
When I request a travel distance matrix I should get
|
||||||
| | 1 | 2 | 3 | 4 |
|
| | 1 | 2 | 3 | 4 |
|
||||||
| 1 | 0 | 10 +-1 | 40 +-1 | 50 +-1 |
|
| 1 | 0 | 100+-1 | 400+-1 | 500+-1 |
|
||||||
| 2 | 70 +-1 | 0 | 30 +-1 | 40 +-1 |
|
| 2 | 700+-1 | 0 | 300+-1 | 400+-1 |
|
||||||
| 3 | 40 +-1 | 50 +-1 | 0 | 10 +-1 |
|
| 3 | 400+-1 | 500+-1 | 0 | 100+-1 |
|
||||||
| 4 | 30 +-1 | 40 +-1 | 70 +-1 | 0 |
|
| 4 | 300+-1 | 400+-1 | 700+-1 | 0 |
|
||||||
|
|
||||||
Scenario: Testbot - Travel time matrix based on segment durations
|
|
||||||
|
Scenario: Testbot - Travel distance matrix based on segment durations
|
||||||
Given the profile file
|
Given the profile file
|
||||||
"""
|
"""
|
||||||
local functions = require('testbot')
|
local functions = require('testbot')
|
||||||
@@ -301,20 +390,19 @@ Feature: Basic Distance Matrix
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
And the ways
|
And the ways
|
||||||
| nodes |
|
| nodes |
|
||||||
| abcd |
|
| abcd |
|
||||||
| ce |
|
| ce |
|
||||||
|
|
||||||
When I request a travel time matrix I should get
|
When I request a travel distance matrix I should get
|
||||||
| | a | b | c | d | e |
|
| | a | b | c | d | e |
|
||||||
| a | 0 | 11 | 22 | 33 | 33 |
|
| a | 0 | 100+-2 | 200+-2 | 300+-2 | 400+-2 |
|
||||||
| b | 11 | 0 | 11 | 22 | 22 |
|
| b | 100+-2 | 0 | 100+-2 | 200+-2 | 300+-2 |
|
||||||
| c | 22 | 11 | 0 | 11 | 11 |
|
| c | 200+-2 | 100+-2 | 0 | 100+-2 | 200+-2 |
|
||||||
| d | 33 | 22 | 11 | 0 | 22 |
|
| d | 300+-2 | 200+-2 | 100+-2 | 0 | 300+-2 |
|
||||||
| e | 33 | 22 | 11 | 22 | 0 |
|
| e | 400+-2 | 300+-2 | 200+-2 | 300+-2 | 0 |
|
||||||
|
|
||||||
|
Scenario: Testbot - Travel distance matrix for alternative loop paths
|
||||||
Scenario: Testbot - Travel time matrix for alternative loop paths
|
|
||||||
Given the profile file
|
Given the profile file
|
||||||
"""
|
"""
|
||||||
local functions = require('testbot')
|
local functions = require('testbot')
|
||||||
@@ -350,62 +438,132 @@ Feature: Basic Distance Matrix
|
|||||||
| dc | yes |
|
| dc | yes |
|
||||||
| ca | yes |
|
| ca | yes |
|
||||||
|
|
||||||
When I request a travel time matrix I should get
|
When I request a travel distance matrix I should get
|
||||||
| | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
|
| | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
|
||||||
| 1 | 0 | 11 | 3 | 2 | 6 | 5 | 8.9 | 7.9 |
|
| 1 | 0 | 1100+-5 | 300+-5 | 200+-5 | 600+-5 | 500+-5 | 900+-5 | 800+-5 |
|
||||||
| 2 | 1 | 0 | 4 | 3 | 7 | 6 | 9.9 | 8.9 |
|
| 2 | 100+-5 | 0 | 400+-5 | 300+-5 | 700+-5 | 600+-5 | 1000+-5 | 900+-5 |
|
||||||
| 3 | 9 | 8 | 0 | 11 | 3 | 2 | 5.9 | 4.9 |
|
| 3 | 900+-5 | 800+-5 | 0 | 1100+-5 | 300+-5 | 200+-5 | 600+-5 | 500+-5 |
|
||||||
| 4 | 10 | 9 | 1 | 0 | 4 | 3 | 6.9 | 5.9 |
|
| 4 | 1000+-5 | 900+-5 | 100+-5 | 0 | 400+-5 | 300+-5 | 700+-5 | 600+-5 |
|
||||||
| 5 | 6 | 5 | 9 | 8 | 0 | 11 | 2.9 | 1.9 |
|
| 5 | 600+-5 | 500+-5 | 900+-5 | 800+-5 | 0 | 1100+-5 | 300+-5 | 200+-5 |
|
||||||
| 6 | 7 | 6 | 10 | 9 | 1 | 0 | 3.9 | 2.9 |
|
| 6 | 700+-5 | 600+-5 | 1000+-5 | 900+-5 | 100+-5 | 0 | 400+-5 | 300+-5 |
|
||||||
| 7 | 3.1 | 2.1 | 6.1 | 5.1 | 9.1 | 8.1 | 0 | 11 |
|
| 7 | 300+-5 | 200+-5 | 600+-5 | 500+-5 | 900+-5 | 800+-5 | 0 | 1100+-5 |
|
||||||
| 8 | 4.1 | 3.1 | 7.1 | 6.1 | 10.1 | 9.1 | 1 | 0 |
|
| 8 | 400+-5 | 300+-5 | 700+-5 | 600+-5 | 1000+-5 | 900+-5 | 100+-5 | 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 time matrix with ties
|
Scenario: Testbot - Travel distance matrix with ties
|
||||||
Given the profile file
|
Given the node map
|
||||||
"""
|
|
||||||
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
|
c d
|
||||||
"""
|
"""
|
||||||
|
|
||||||
And the ways
|
And the ways
|
||||||
| nodes |
|
| nodes |
|
||||||
| ab |
|
| ab |
|
||||||
| ac |
|
| ac |
|
||||||
| bd |
|
| bd |
|
||||||
| dc |
|
| dc |
|
||||||
|
|
||||||
|
|
||||||
When I route I should get
|
When I route I should get
|
||||||
| from | to | route | distance | time | weight |
|
| from | to | route | distance | time | weight |
|
||||||
| a | c | ac,ac | 200m | 5s | 5 |
|
| a | c | ac,ac | 200m | 20s | 20 |
|
||||||
|
|
||||||
When I request a travel time matrix I should get
|
When I route I should get
|
||||||
| | a | b | c | d |
|
| from | to | route | distance |
|
||||||
| a | 0 | 1 | 5 | 10 |
|
| 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
|
When I request a travel distance matrix I should get
|
||||||
| | a |
|
| | a | b | c | d |
|
||||||
| a | 0 |
|
| a | 0 | 450+-2 | 200+-2 | 500+-2 |
|
||||||
| b | 1 |
|
|
||||||
| c | 15 |
|
When I request a travel distance matrix I should get
|
||||||
| d | 10 |
|
| | 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 |
|
||||||
|
|||||||
@@ -0,0 +1,512 @@
|
|||||||
|
@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 |
|
||||||
@@ -106,6 +106,40 @@ Feature: Multi level routing
|
|||||||
| l | 144.7 | 60 |
|
| l | 144.7 | 60 |
|
||||||
| o | 124.7 | 0 |
|
| 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
|
Scenario: Testbot - Multi level routing: horizontal road
|
||||||
Given the node map
|
Given the node map
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ Feature: Traffic - speeds
|
|||||||
| a | d | ad,ad | 27 km/h | 1275.7,0 | 1 |
|
| a | d | ad,ad | 27 km/h | 1275.7,0 | 1 |
|
||||||
| d | c | dc,dc | 36 km/h | 956.8,0 | 0 |
|
| d | c | dc,dc | 36 km/h | 956.8,0 | 0 |
|
||||||
| g | b | fb,fb | 36 km/h | 164.7,0 | 0 |
|
| g | b | fb,fb | 36 km/h | 164.7,0 | 0 |
|
||||||
| a | g | ad,df,fb,fb | 30 km/h | 1275.7,487.5,304.7,0 | 1:0:0 |
|
| a | g | ad,df,fb,fb | 30 km/h | 1295.7,487.5,304.7,0 | 1:0:0 |
|
||||||
|
|
||||||
|
|
||||||
Scenario: Weighting based on speed file weights, ETA based on file durations
|
Scenario: Weighting based on speed file weights, ETA based on file durations
|
||||||
|
|||||||
@@ -72,8 +72,14 @@ struct ContractorConfig final : storage::IOConfig
|
|||||||
// The remaining vertices form the core of the hierarchy
|
// The remaining vertices form the core of the hierarchy
|
||||||
//(e.g. 0.8 contracts 80 percent of the hierarchy, leaving a core of 20%)
|
//(e.g. 0.8 contracts 80 percent of the hierarchy, leaving a core of 20%)
|
||||||
double core_factor;
|
double core_factor;
|
||||||
|
|
||||||
|
// Whether to store distances for CH edges in addition to duration/weight
|
||||||
|
// Defaults to false. Setting to true will require more storage/memory,
|
||||||
|
// but avoids the need for path unpacking to learn the distance of a CH
|
||||||
|
// route (useful for faster distance results in table queries)
|
||||||
|
bool cache_distances;
|
||||||
};
|
};
|
||||||
}
|
} // namespace contractor
|
||||||
}
|
} // namespace osrm
|
||||||
|
|
||||||
#endif // EXTRACTOR_OPTIONS_HPP
|
#endif // EXTRACTOR_OPTIONS_HPP
|
||||||
|
|||||||
@@ -12,23 +12,26 @@ namespace contractor
|
|||||||
struct ContractorEdgeData
|
struct ContractorEdgeData
|
||||||
{
|
{
|
||||||
ContractorEdgeData()
|
ContractorEdgeData()
|
||||||
: weight(0), duration(0), id(0), originalEdges(0), shortcut(0), forward(0), backward(0)
|
: weight(0), duration(0), distance(0), id(0), originalEdges(0), shortcut(0), forward(0),
|
||||||
|
backward(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
ContractorEdgeData(EdgeWeight weight,
|
ContractorEdgeData(EdgeWeight weight,
|
||||||
EdgeWeight duration,
|
EdgeWeight duration,
|
||||||
|
EdgeDistance distance,
|
||||||
unsigned original_edges,
|
unsigned original_edges,
|
||||||
unsigned id,
|
unsigned id,
|
||||||
bool shortcut,
|
bool shortcut,
|
||||||
bool forward,
|
bool forward,
|
||||||
bool backward)
|
bool backward)
|
||||||
: weight(weight), duration(duration), id(id),
|
: weight(weight), duration(duration), distance(distance), id(id),
|
||||||
originalEdges(std::min((1u << 29) - 1u, original_edges)), shortcut(shortcut),
|
originalEdges(std::min((1u << 29) - 1u, original_edges)), shortcut(shortcut),
|
||||||
forward(forward), backward(backward)
|
forward(forward), backward(backward)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
EdgeWeight weight;
|
EdgeWeight weight;
|
||||||
EdgeWeight duration;
|
EdgeWeight duration;
|
||||||
|
EdgeDistance distance;
|
||||||
unsigned id;
|
unsigned id;
|
||||||
unsigned originalEdges : 29;
|
unsigned originalEdges : 29;
|
||||||
bool shortcut : 1;
|
bool shortcut : 1;
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ ContractorGraph toContractorGraph(NodeID number_of_nodes, InputEdgeContainer inp
|
|||||||
input_edge.target,
|
input_edge.target,
|
||||||
std::max(input_edge.data.weight, 1),
|
std::max(input_edge.data.weight, 1),
|
||||||
input_edge.data.duration,
|
input_edge.data.duration,
|
||||||
|
input_edge.data.distance,
|
||||||
1,
|
1,
|
||||||
input_edge.data.turn_id,
|
input_edge.data.turn_id,
|
||||||
false,
|
false,
|
||||||
@@ -51,6 +52,7 @@ ContractorGraph toContractorGraph(NodeID number_of_nodes, InputEdgeContainer inp
|
|||||||
input_edge.source,
|
input_edge.source,
|
||||||
std::max(input_edge.data.weight, 1),
|
std::max(input_edge.data.weight, 1),
|
||||||
input_edge.data.duration,
|
input_edge.data.duration,
|
||||||
|
input_edge.data.distance,
|
||||||
1,
|
1,
|
||||||
input_edge.data.turn_id,
|
input_edge.data.turn_id,
|
||||||
false,
|
false,
|
||||||
@@ -82,6 +84,7 @@ ContractorGraph toContractorGraph(NodeID number_of_nodes, InputEdgeContainer inp
|
|||||||
forward_edge.data.originalEdges = reverse_edge.data.originalEdges = 1;
|
forward_edge.data.originalEdges = reverse_edge.data.originalEdges = 1;
|
||||||
forward_edge.data.weight = reverse_edge.data.weight = INVALID_EDGE_WEIGHT;
|
forward_edge.data.weight = reverse_edge.data.weight = INVALID_EDGE_WEIGHT;
|
||||||
forward_edge.data.duration = reverse_edge.data.duration = MAXIMAL_EDGE_DURATION;
|
forward_edge.data.duration = reverse_edge.data.duration = MAXIMAL_EDGE_DURATION;
|
||||||
|
forward_edge.data.distance = reverse_edge.data.distance = MAXIMAL_EDGE_DISTANCE;
|
||||||
// remove parallel edges
|
// remove parallel edges
|
||||||
while (i < edges.size() && edges[i].source == source && edges[i].target == target)
|
while (i < edges.size() && edges[i].source == source && edges[i].target == target)
|
||||||
{
|
{
|
||||||
@@ -90,12 +93,16 @@ ContractorGraph toContractorGraph(NodeID number_of_nodes, InputEdgeContainer inp
|
|||||||
forward_edge.data.weight = std::min(edges[i].data.weight, forward_edge.data.weight);
|
forward_edge.data.weight = std::min(edges[i].data.weight, forward_edge.data.weight);
|
||||||
forward_edge.data.duration =
|
forward_edge.data.duration =
|
||||||
std::min(edges[i].data.duration, forward_edge.data.duration);
|
std::min(edges[i].data.duration, forward_edge.data.duration);
|
||||||
|
forward_edge.data.distance =
|
||||||
|
std::min(edges[i].data.distance, forward_edge.data.distance);
|
||||||
}
|
}
|
||||||
if (edges[i].data.backward)
|
if (edges[i].data.backward)
|
||||||
{
|
{
|
||||||
reverse_edge.data.weight = std::min(edges[i].data.weight, reverse_edge.data.weight);
|
reverse_edge.data.weight = std::min(edges[i].data.weight, reverse_edge.data.weight);
|
||||||
reverse_edge.data.duration =
|
reverse_edge.data.duration =
|
||||||
std::min(edges[i].data.duration, reverse_edge.data.duration);
|
std::min(edges[i].data.duration, reverse_edge.data.duration);
|
||||||
|
reverse_edge.data.distance =
|
||||||
|
std::min(edges[i].data.distance, reverse_edge.data.distance);
|
||||||
}
|
}
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
@@ -151,6 +158,7 @@ template <class Edge, typename GraphT> inline std::vector<Edge> toEdges(GraphT g
|
|||||||
BOOST_ASSERT_MSG(SPECIAL_NODEID != new_edge.target, "Target id invalid");
|
BOOST_ASSERT_MSG(SPECIAL_NODEID != new_edge.target, "Target id invalid");
|
||||||
new_edge.data.weight = data.weight;
|
new_edge.data.weight = data.weight;
|
||||||
new_edge.data.duration = data.duration;
|
new_edge.data.duration = data.duration;
|
||||||
|
new_edge.data.distance = data.distance;
|
||||||
new_edge.data.shortcut = data.shortcut;
|
new_edge.data.shortcut = data.shortcut;
|
||||||
new_edge.data.turn_id = data.id;
|
new_edge.data.turn_id = data.id;
|
||||||
BOOST_ASSERT_MSG(new_edge.data.turn_id != INT_MAX, // 2^31
|
BOOST_ASSERT_MSG(new_edge.data.turn_id != INT_MAX, // 2^31
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ struct QueryEdge
|
|||||||
struct EdgeData
|
struct EdgeData
|
||||||
{
|
{
|
||||||
explicit EdgeData()
|
explicit EdgeData()
|
||||||
: turn_id(0), shortcut(false), weight(0), duration(0), forward(false), backward(false)
|
: turn_id(0), shortcut(false), weight(0), duration(0), forward(false), backward(false),
|
||||||
|
distance(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,10 +26,11 @@ struct QueryEdge
|
|||||||
const bool shortcut,
|
const bool shortcut,
|
||||||
const EdgeWeight weight,
|
const EdgeWeight weight,
|
||||||
const EdgeWeight duration,
|
const EdgeWeight duration,
|
||||||
|
const EdgeDistance distance,
|
||||||
const bool forward,
|
const bool forward,
|
||||||
const bool backward)
|
const bool backward)
|
||||||
: turn_id(turn_id), shortcut(shortcut), weight(weight), duration(duration),
|
: turn_id(turn_id), shortcut(shortcut), weight(weight), duration(duration),
|
||||||
forward(forward), backward(backward)
|
forward(forward), backward(backward), distance(distance)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,6 +42,7 @@ struct QueryEdge
|
|||||||
turn_id = other.id;
|
turn_id = other.id;
|
||||||
forward = other.forward;
|
forward = other.forward;
|
||||||
backward = other.backward;
|
backward = other.backward;
|
||||||
|
distance = other.distance;
|
||||||
}
|
}
|
||||||
// this ID is either the middle node of the shortcut, or the ID of the edge based node (node
|
// this ID is either the middle node of the shortcut, or the ID of the edge based node (node
|
||||||
// based edge) storing the appropriate data. If `shortcut` is set to true, we get the middle
|
// based edge) storing the appropriate data. If `shortcut` is set to true, we get the middle
|
||||||
@@ -50,6 +53,7 @@ struct QueryEdge
|
|||||||
EdgeWeight duration : 30;
|
EdgeWeight duration : 30;
|
||||||
std::uint32_t forward : 1;
|
std::uint32_t forward : 1;
|
||||||
std::uint32_t backward : 1;
|
std::uint32_t backward : 1;
|
||||||
|
EdgeDistance distance;
|
||||||
} data;
|
} data;
|
||||||
|
|
||||||
QueryEdge() : source(SPECIAL_NODEID), target(SPECIAL_NODEID) {}
|
QueryEdge() : source(SPECIAL_NODEID), target(SPECIAL_NODEID) {}
|
||||||
@@ -69,10 +73,11 @@ struct QueryEdge
|
|||||||
return (source == right.source && target == right.target &&
|
return (source == right.source && target == right.target &&
|
||||||
data.weight == right.data.weight && data.duration == right.data.duration &&
|
data.weight == right.data.weight && data.duration == right.data.duration &&
|
||||||
data.shortcut == right.data.shortcut && data.forward == right.data.forward &&
|
data.shortcut == right.data.shortcut && data.forward == right.data.forward &&
|
||||||
data.backward == right.data.backward && data.turn_id == right.data.turn_id);
|
data.backward == right.data.backward && data.turn_id == right.data.turn_id &&
|
||||||
|
data.distance == right.data.distance);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
} // namespace contractor
|
||||||
}
|
} // namespace osrm
|
||||||
|
|
||||||
#endif // QUERYEDGE_HPP
|
#endif // QUERYEDGE_HPP
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ struct CustomizationConfig final : storage::IOConfig
|
|||||||
".osrm.partition",
|
".osrm.partition",
|
||||||
".osrm.cells",
|
".osrm.cells",
|
||||||
".osrm.ebg_nodes",
|
".osrm.ebg_nodes",
|
||||||
".osrm.properties"},
|
".osrm.properties",
|
||||||
|
".osrm.enw"},
|
||||||
{},
|
{},
|
||||||
{".osrm.cell_metrics", ".osrm.mldgr"}),
|
{".osrm.cell_metrics", ".osrm.mldgr"}),
|
||||||
requested_num_threads(0)
|
requested_num_threads(0)
|
||||||
|
|||||||
@@ -16,28 +16,109 @@ namespace osrm
|
|||||||
namespace customizer
|
namespace customizer
|
||||||
{
|
{
|
||||||
|
|
||||||
using EdgeBasedGraphEdgeData = partitioner::EdgeBasedGraphEdgeData;
|
struct EdgeBasedGraphEdgeData
|
||||||
|
|
||||||
struct MultiLevelEdgeBasedGraph
|
|
||||||
: public partitioner::MultiLevelGraph<EdgeBasedGraphEdgeData, storage::Ownership::Container>
|
|
||||||
{
|
{
|
||||||
using Base =
|
NodeID turn_id; // ID of the edge based node (node based edge)
|
||||||
partitioner::MultiLevelGraph<EdgeBasedGraphEdgeData, storage::Ownership::Container>;
|
|
||||||
using Base::Base;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MultiLevelEdgeBasedGraphView
|
template <typename EdgeDataT, storage::Ownership Ownership> class MultiLevelGraph;
|
||||||
: public partitioner::MultiLevelGraph<EdgeBasedGraphEdgeData, storage::Ownership::View>
|
|
||||||
|
namespace serialization
|
||||||
{
|
{
|
||||||
using Base = partitioner::MultiLevelGraph<EdgeBasedGraphEdgeData, storage::Ownership::View>;
|
template <typename EdgeDataT, storage::Ownership Ownership>
|
||||||
using Base::Base;
|
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct StaticEdgeBasedGraphEdge : MultiLevelEdgeBasedGraph::InputEdge
|
using MultiLevelEdgeBasedGraph =
|
||||||
{
|
MultiLevelGraph<EdgeBasedGraphEdgeData, storage::Ownership::Container>;
|
||||||
using Base = MultiLevelEdgeBasedGraph::InputEdge;
|
using MultiLevelEdgeBasedGraphView =
|
||||||
using Base::Base;
|
MultiLevelGraph<EdgeBasedGraphEdgeData, storage::Ownership::View>;
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -73,6 +73,39 @@ 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,6 +1,8 @@
|
|||||||
#ifndef OSRM_CUSTOMIZER_SERIALIZATION_HPP
|
#ifndef OSRM_CUSTOMIZER_SERIALIZATION_HPP
|
||||||
#define OSRM_CUSTOMIZER_SERIALIZATION_HPP
|
#define OSRM_CUSTOMIZER_SERIALIZATION_HPP
|
||||||
|
|
||||||
|
#include "customizer/edge_based_graph.hpp"
|
||||||
|
|
||||||
#include "partitioner/cell_storage.hpp"
|
#include "partitioner/cell_storage.hpp"
|
||||||
|
|
||||||
#include "storage/serialization.hpp"
|
#include "storage/serialization.hpp"
|
||||||
@@ -31,6 +33,34 @@ inline void write(storage::tar::FileWriter &writer,
|
|||||||
storage::serialization::write(writer, name + "/weights", metric.weights);
|
storage::serialization::write(writer, name + "/weights", metric.weights);
|
||||||
storage::serialization::write(writer, name + "/durations", metric.durations);
|
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,6 +50,9 @@ template <typename AlgorithmT> struct HasMapMatching final : std::false_type
|
|||||||
template <typename AlgorithmT> struct HasManyToManySearch 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
|
template <typename AlgorithmT> struct HasGetTileTurns final : std::false_type
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
@@ -73,6 +76,9 @@ template <> struct HasMapMatching<ch::Algorithm> final : std::true_type
|
|||||||
template <> struct HasManyToManySearch<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
|
template <> struct HasGetTileTurns<ch::Algorithm> final : std::true_type
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
@@ -96,6 +102,9 @@ template <> struct HasMapMatching<mld::Algorithm> final : std::true_type
|
|||||||
template <> struct HasManyToManySearch<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
|
template <> struct HasGetTileTurns<mld::Algorithm> final : std::true_type
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -36,9 +36,10 @@ class TableAPI final : public BaseAPI
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void MakeResponse(const std::vector<EdgeWeight> &durations,
|
virtual void
|
||||||
const std::vector<PhantomNode> &phantoms,
|
MakeResponse(const std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>> &tables,
|
||||||
util::json::Object &response) const
|
const std::vector<PhantomNode> &phantoms,
|
||||||
|
util::json::Object &response) const
|
||||||
{
|
{
|
||||||
auto number_of_sources = parameters.sources.size();
|
auto number_of_sources = parameters.sources.size();
|
||||||
auto number_of_destinations = parameters.destinations.size();
|
auto number_of_destinations = parameters.destinations.size();
|
||||||
@@ -64,8 +65,18 @@ class TableAPI final : public BaseAPI
|
|||||||
response.values["destinations"] = MakeWaypoints(phantoms, parameters.destinations);
|
response.values["destinations"] = MakeWaypoints(phantoms, parameters.destinations);
|
||||||
}
|
}
|
||||||
|
|
||||||
response.values["durations"] =
|
if (parameters.annotations & TableParameters::AnnotationsType::Duration)
|
||||||
MakeTable(durations, number_of_sources, number_of_destinations);
|
{
|
||||||
|
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["code"] = "Ok";
|
response.values["code"] = "Ok";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,9 +108,9 @@ class TableAPI final : public BaseAPI
|
|||||||
return json_waypoints;
|
return json_waypoints;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual util::json::Array MakeTable(const std::vector<EdgeWeight> &values,
|
virtual util::json::Array MakeDurationTable(const std::vector<EdgeWeight> &values,
|
||||||
std::size_t number_of_rows,
|
std::size_t number_of_rows,
|
||||||
std::size_t number_of_columns) const
|
std::size_t number_of_columns) const
|
||||||
{
|
{
|
||||||
util::json::Array json_table;
|
util::json::Array json_table;
|
||||||
for (const auto row : util::irange<std::size_t>(0UL, number_of_rows))
|
for (const auto row : util::irange<std::size_t>(0UL, number_of_rows))
|
||||||
@@ -116,6 +127,7 @@ class TableAPI final : public BaseAPI
|
|||||||
{
|
{
|
||||||
return util::json::Value(util::json::Null());
|
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.));
|
return util::json::Value(util::json::Number(duration / 10.));
|
||||||
});
|
});
|
||||||
json_table.values.push_back(std::move(json_row));
|
json_table.values.push_back(std::move(json_row));
|
||||||
@@ -123,6 +135,34 @@ class TableAPI final : public BaseAPI
|
|||||||
return json_table;
|
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;
|
const TableParameters ¶meters;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -60,6 +60,16 @@ struct TableParameters : public BaseParameters
|
|||||||
std::vector<std::size_t> sources;
|
std::vector<std::size_t> sources;
|
||||||
std::vector<std::size_t> destinations;
|
std::vector<std::size_t> destinations;
|
||||||
|
|
||||||
|
enum class AnnotationsType
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Duration = 0x01,
|
||||||
|
Distance = 0x02,
|
||||||
|
All = Duration | Distance
|
||||||
|
};
|
||||||
|
|
||||||
|
AnnotationsType annotations = AnnotationsType::Duration;
|
||||||
|
|
||||||
TableParameters() = default;
|
TableParameters() = default;
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
TableParameters(std::vector<std::size_t> sources_,
|
TableParameters(std::vector<std::size_t> sources_,
|
||||||
@@ -70,6 +80,16 @@ 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
|
bool IsValid() const
|
||||||
{
|
{
|
||||||
if (!BaseParameters::IsValid())
|
if (!BaseParameters::IsValid())
|
||||||
@@ -79,7 +99,7 @@ struct TableParameters : public BaseParameters
|
|||||||
if (coordinates.size() < 2)
|
if (coordinates.size() < 2)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// 1/ The user is able to specify duplicates in srcs and dsts, in that case it's her fault
|
// 1/ The user is able to specify duplicates in srcs and dsts, in that case it's their fault
|
||||||
|
|
||||||
// 2/ len(srcs) and len(dsts) smaller or equal to len(locations)
|
// 2/ len(srcs) and len(dsts) smaller or equal to len(locations)
|
||||||
if (sources.size() > coordinates.size())
|
if (sources.size() > coordinates.size())
|
||||||
@@ -100,6 +120,26 @@ struct TableParameters : public BaseParameters
|
|||||||
return true;
|
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,6 +2,7 @@
|
|||||||
#define OSRM_ENGINE_DATAFACADE_ALGORITHM_DATAFACADE_HPP
|
#define OSRM_ENGINE_DATAFACADE_ALGORITHM_DATAFACADE_HPP
|
||||||
|
|
||||||
#include "contractor/query_edge.hpp"
|
#include "contractor/query_edge.hpp"
|
||||||
|
#include "customizer/edge_based_graph.hpp"
|
||||||
#include "extractor/edge_based_edge.hpp"
|
#include "extractor/edge_based_edge.hpp"
|
||||||
#include "engine/algorithm.hpp"
|
#include "engine/algorithm.hpp"
|
||||||
|
|
||||||
@@ -59,7 +60,7 @@ template <> class AlgorithmDataFacade<CH>
|
|||||||
template <> class AlgorithmDataFacade<MLD>
|
template <> class AlgorithmDataFacade<MLD>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using EdgeData = extractor::EdgeBasedEdge::EdgeData;
|
using EdgeData = customizer::EdgeBasedGraphEdgeData;
|
||||||
using EdgeRange = util::range<EdgeID>;
|
using EdgeRange = util::range<EdgeID>;
|
||||||
|
|
||||||
// search graph access
|
// search graph access
|
||||||
@@ -71,12 +72,20 @@ template <> class AlgorithmDataFacade<MLD>
|
|||||||
|
|
||||||
virtual unsigned GetOutDegree(const NodeID n) const = 0;
|
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 NodeID GetTarget(const EdgeID e) const = 0;
|
||||||
|
|
||||||
virtual const EdgeData &GetEdgeData(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::MultiLevelPartitionView &GetMultiLevelPartition() const = 0;
|
||||||
|
|
||||||
virtual const partitioner::CellStorageView &GetCellStorage() const = 0;
|
virtual const partitioner::CellStorageView &GetCellStorage() const = 0;
|
||||||
|
|||||||
@@ -133,7 +133,6 @@ class ContiguousInternalMemoryDataFacadeBase : public BaseDataFacade
|
|||||||
using RTreeNode = SharedRTree::TreeNode;
|
using RTreeNode = SharedRTree::TreeNode;
|
||||||
|
|
||||||
extractor::ClassData exclude_mask;
|
extractor::ClassData exclude_mask;
|
||||||
std::string m_timestamp;
|
|
||||||
extractor::ProfileProperties *m_profile_properties;
|
extractor::ProfileProperties *m_profile_properties;
|
||||||
extractor::Datasources *m_datasources;
|
extractor::Datasources *m_datasources;
|
||||||
|
|
||||||
@@ -283,13 +282,13 @@ class ContiguousInternalMemoryDataFacadeBase : public BaseDataFacade
|
|||||||
return segment_data.GetReverseDatasources(id);
|
return segment_data.GetReverseDatasources(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
TurnPenalty GetWeightPenaltyForEdgeID(const unsigned id) const override final
|
TurnPenalty GetWeightPenaltyForEdgeID(const EdgeID id) const override final
|
||||||
{
|
{
|
||||||
BOOST_ASSERT(m_turn_weight_penalties.size() > id);
|
BOOST_ASSERT(m_turn_weight_penalties.size() > id);
|
||||||
return m_turn_weight_penalties[id];
|
return m_turn_weight_penalties[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
TurnPenalty GetDurationPenaltyForEdgeID(const unsigned id) const override final
|
TurnPenalty GetDurationPenaltyForEdgeID(const EdgeID id) const override final
|
||||||
{
|
{
|
||||||
BOOST_ASSERT(m_turn_duration_penalties.size() > id);
|
BOOST_ASSERT(m_turn_duration_penalties.size() > id);
|
||||||
return m_turn_duration_penalties[id];
|
return m_turn_duration_penalties[id];
|
||||||
@@ -622,7 +621,6 @@ class ContiguousInternalMemoryDataFacade<CH>
|
|||||||
const std::size_t exclude_index)
|
const std::size_t exclude_index)
|
||||||
: ContiguousInternalMemoryDataFacadeBase(allocator, metric_name, exclude_index),
|
: ContiguousInternalMemoryDataFacadeBase(allocator, metric_name, exclude_index),
|
||||||
ContiguousInternalMemoryAlgorithmDataFacade<CH>(allocator, metric_name, exclude_index)
|
ContiguousInternalMemoryAlgorithmDataFacade<CH>(allocator, metric_name, exclude_index)
|
||||||
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -684,6 +682,31 @@ template <> class ContiguousInternalMemoryAlgorithmDataFacade<MLD> : public Algo
|
|||||||
return query_graph.GetOutDegree(n);
|
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); }
|
NodeID GetTarget(const EdgeID e) const override final { return query_graph.GetTarget(e); }
|
||||||
|
|
||||||
const EdgeData &GetEdgeData(const EdgeID e) const override final
|
const EdgeData &GetEdgeData(const EdgeID e) const override final
|
||||||
@@ -691,11 +714,6 @@ template <> class ContiguousInternalMemoryAlgorithmDataFacade<MLD> : public Algo
|
|||||||
return query_graph.GetEdgeData(e);
|
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
|
EdgeRange GetBorderEdgeRange(const LevelID level, const NodeID node) const override final
|
||||||
{
|
{
|
||||||
return query_graph.GetBorderEdgeRange(level, node);
|
return query_graph.GetBorderEdgeRange(level, node);
|
||||||
@@ -720,7 +738,6 @@ class ContiguousInternalMemoryDataFacade<MLD> final
|
|||||||
const std::size_t exclude_index)
|
const std::size_t exclude_index)
|
||||||
: ContiguousInternalMemoryDataFacadeBase(allocator, metric_name, exclude_index),
|
: ContiguousInternalMemoryDataFacadeBase(allocator, metric_name, exclude_index),
|
||||||
ContiguousInternalMemoryAlgorithmDataFacade<MLD>(allocator, metric_name, exclude_index)
|
ContiguousInternalMemoryAlgorithmDataFacade<MLD>(allocator, metric_name, exclude_index)
|
||||||
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -33,7 +33,6 @@
|
|||||||
|
|
||||||
#include <boost/range/adaptor/reversed.hpp>
|
#include <boost/range/adaptor/reversed.hpp>
|
||||||
#include <boost/range/any_range.hpp>
|
#include <boost/range/any_range.hpp>
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -87,9 +86,9 @@ class BaseDataFacade
|
|||||||
virtual NodeForwardRange GetUncompressedForwardGeometry(const EdgeID id) const = 0;
|
virtual NodeForwardRange GetUncompressedForwardGeometry(const EdgeID id) const = 0;
|
||||||
virtual NodeReverseRange GetUncompressedReverseGeometry(const EdgeID id) const = 0;
|
virtual NodeReverseRange GetUncompressedReverseGeometry(const EdgeID id) const = 0;
|
||||||
|
|
||||||
virtual TurnPenalty GetWeightPenaltyForEdgeID(const unsigned id) const = 0;
|
virtual TurnPenalty GetWeightPenaltyForEdgeID(const EdgeID id) const = 0;
|
||||||
|
|
||||||
virtual TurnPenalty GetDurationPenaltyForEdgeID(const unsigned id) const = 0;
|
virtual TurnPenalty GetDurationPenaltyForEdgeID(const EdgeID id) const = 0;
|
||||||
|
|
||||||
// Gets the weight values for each segment in an uncompressed geometry.
|
// Gets the weight values for each segment in an uncompressed geometry.
|
||||||
// Should always be 1 shorter than GetUncompressedGeometry
|
// Should always be 1 shorter than GetUncompressedGeometry
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <iterator>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -447,6 +448,8 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
|
|||||||
const auto forward_durations = datafacade.GetUncompressedForwardDurations(geometry_id);
|
const auto forward_durations = datafacade.GetUncompressedForwardDurations(geometry_id);
|
||||||
const auto reverse_durations = datafacade.GetUncompressedReverseDurations(geometry_id);
|
const auto reverse_durations = datafacade.GetUncompressedReverseDurations(geometry_id);
|
||||||
|
|
||||||
|
const auto forward_geometry = datafacade.GetUncompressedForwardGeometry(geometry_id);
|
||||||
|
|
||||||
const auto forward_weight_offset =
|
const auto forward_weight_offset =
|
||||||
std::accumulate(forward_weights.begin(),
|
std::accumulate(forward_weights.begin(),
|
||||||
forward_weights.begin() + data.fwd_segment_position,
|
forward_weights.begin() + data.fwd_segment_position,
|
||||||
@@ -457,12 +460,25 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
|
|||||||
forward_durations.begin() + data.fwd_segment_position,
|
forward_durations.begin() + data.fwd_segment_position,
|
||||||
EdgeDuration{0});
|
EdgeDuration{0});
|
||||||
|
|
||||||
EdgeWeight forward_weight = forward_weights[data.fwd_segment_position];
|
EdgeDistance forward_distance_offset = 0;
|
||||||
EdgeDuration forward_duration = forward_durations[data.fwd_segment_position];
|
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)));
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_ASSERT(data.fwd_segment_position <
|
BOOST_ASSERT(data.fwd_segment_position <
|
||||||
std::distance(forward_durations.begin(), forward_durations.end()));
|
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 reverse_weight_offset =
|
const auto reverse_weight_offset =
|
||||||
std::accumulate(reverse_weights.begin(),
|
std::accumulate(reverse_weights.begin(),
|
||||||
reverse_weights.end() - data.fwd_segment_position - 1,
|
reverse_weights.end() - data.fwd_segment_position - 1,
|
||||||
@@ -473,10 +489,23 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
|
|||||||
reverse_durations.end() - data.fwd_segment_position - 1,
|
reverse_durations.end() - data.fwd_segment_position - 1,
|
||||||
EdgeDuration{0});
|
EdgeDuration{0});
|
||||||
|
|
||||||
|
EdgeDistance reverse_distance_offset = 0;
|
||||||
|
for (auto current = forward_geometry.begin();
|
||||||
|
current < forward_geometry.end() - data.fwd_segment_position - 2;
|
||||||
|
++current)
|
||||||
|
{
|
||||||
|
reverse_distance_offset += util::coordinate_calculation::fccApproximateDistance(
|
||||||
|
datafacade.GetCoordinateOfNode(*current),
|
||||||
|
datafacade.GetCoordinateOfNode(*std::next(current)));
|
||||||
|
}
|
||||||
|
|
||||||
EdgeWeight reverse_weight =
|
EdgeWeight reverse_weight =
|
||||||
reverse_weights[reverse_weights.size() - data.fwd_segment_position - 1];
|
reverse_weights[reverse_weights.size() - data.fwd_segment_position - 1];
|
||||||
EdgeDuration reverse_duration =
|
EdgeDuration reverse_duration =
|
||||||
reverse_durations[reverse_durations.size() - data.fwd_segment_position - 1];
|
reverse_durations[reverse_durations.size() - data.fwd_segment_position - 1];
|
||||||
|
EdgeDistance reverse_distance = util::coordinate_calculation::fccApproximateDistance(
|
||||||
|
point_on_segment,
|
||||||
|
datafacade.GetCoordinateOfNode(forward_geometry(data.fwd_segment_position + 1)));
|
||||||
|
|
||||||
ratio = std::min(1.0, std::max(0.0, ratio));
|
ratio = std::min(1.0, std::max(0.0, ratio));
|
||||||
if (data.forward_segment_id.id != SPECIAL_SEGMENTID)
|
if (data.forward_segment_id.id != SPECIAL_SEGMENTID)
|
||||||
@@ -510,6 +539,10 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
|
|||||||
reverse_weight,
|
reverse_weight,
|
||||||
forward_weight_offset,
|
forward_weight_offset,
|
||||||
reverse_weight_offset,
|
reverse_weight_offset,
|
||||||
|
forward_distance,
|
||||||
|
reverse_distance,
|
||||||
|
forward_distance_offset,
|
||||||
|
reverse_distance_offset,
|
||||||
forward_duration,
|
forward_duration,
|
||||||
reverse_duration,
|
reverse_duration,
|
||||||
forward_duration_offset,
|
forward_duration_offset,
|
||||||
|
|||||||
@@ -63,8 +63,8 @@ struct Hint
|
|||||||
friend std::ostream &operator<<(std::ostream &, const Hint &);
|
friend std::ostream &operator<<(std::ostream &, const Hint &);
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(sizeof(Hint) == 64 + 4, "Hint is bigger than expected");
|
static_assert(sizeof(Hint) == 80 + 4, "Hint is bigger than expected");
|
||||||
constexpr std::size_t ENCODED_HINT_SIZE = 92;
|
constexpr std::size_t ENCODED_HINT_SIZE = 112;
|
||||||
static_assert(ENCODED_HINT_SIZE / 4 * 3 >= sizeof(Hint),
|
static_assert(ENCODED_HINT_SIZE / 4 * 3 >= sizeof(Hint),
|
||||||
"ENCODED_HINT_SIZE does not match size of Hint");
|
"ENCODED_HINT_SIZE does not match size of Hint");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||||||
#include "util/coordinate.hpp"
|
#include "util/coordinate.hpp"
|
||||||
#include "util/typedefs.hpp"
|
#include "util/typedefs.hpp"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
#include <boost/assert.hpp>
|
#include <boost/assert.hpp>
|
||||||
|
|
||||||
namespace osrm
|
namespace osrm
|
||||||
@@ -47,10 +49,13 @@ struct PhantomNode
|
|||||||
: forward_segment_id{SPECIAL_SEGMENTID, false},
|
: forward_segment_id{SPECIAL_SEGMENTID, false},
|
||||||
reverse_segment_id{SPECIAL_SEGMENTID, false}, forward_weight(INVALID_EDGE_WEIGHT),
|
reverse_segment_id{SPECIAL_SEGMENTID, false}, forward_weight(INVALID_EDGE_WEIGHT),
|
||||||
reverse_weight(INVALID_EDGE_WEIGHT), forward_weight_offset(0), reverse_weight_offset(0),
|
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(MAXIMAL_EDGE_DURATION), reverse_duration(MAXIMAL_EDGE_DURATION),
|
||||||
forward_duration_offset(0), reverse_duration_offset(0), fwd_segment_position(0),
|
forward_duration_offset(0), reverse_duration_offset(0), fwd_segment_position(0),
|
||||||
is_valid_forward_source{false}, is_valid_forward_target{false},
|
is_valid_forward_source{false}, is_valid_forward_target{false},
|
||||||
is_valid_reverse_source{false}, is_valid_reverse_target{false}, bearing(0)
|
is_valid_reverse_source{false}, is_valid_reverse_target{false}, bearing(0)
|
||||||
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,6 +83,30 @@ struct PhantomNode
|
|||||||
return reverse_duration + reverse_duration_offset;
|
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 IsBidirected() const { return forward_segment_id.enabled && reverse_segment_id.enabled; }
|
||||||
|
|
||||||
bool IsValid(const unsigned number_of_nodes) const
|
bool IsValid(const unsigned number_of_nodes) const
|
||||||
@@ -88,6 +117,8 @@ struct PhantomNode
|
|||||||
(reverse_weight != INVALID_EDGE_WEIGHT)) &&
|
(reverse_weight != INVALID_EDGE_WEIGHT)) &&
|
||||||
((forward_duration != MAXIMAL_EDGE_DURATION) ||
|
((forward_duration != MAXIMAL_EDGE_DURATION) ||
|
||||||
(reverse_duration != MAXIMAL_EDGE_DURATION)) &&
|
(reverse_duration != MAXIMAL_EDGE_DURATION)) &&
|
||||||
|
((forward_distance != INVALID_EDGE_DISTANCE) ||
|
||||||
|
(reverse_distance != INVALID_EDGE_DISTANCE)) &&
|
||||||
(component.id != INVALID_COMPONENTID);
|
(component.id != INVALID_COMPONENTID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,6 +161,10 @@ struct PhantomNode
|
|||||||
EdgeWeight reverse_weight,
|
EdgeWeight reverse_weight,
|
||||||
EdgeWeight forward_weight_offset,
|
EdgeWeight forward_weight_offset,
|
||||||
EdgeWeight reverse_weight_offset,
|
EdgeWeight reverse_weight_offset,
|
||||||
|
EdgeDistance forward_distance,
|
||||||
|
EdgeDistance reverse_distance,
|
||||||
|
EdgeDistance forward_distance_offset,
|
||||||
|
EdgeDistance reverse_distance_offset,
|
||||||
EdgeWeight forward_duration,
|
EdgeWeight forward_duration,
|
||||||
EdgeWeight reverse_duration,
|
EdgeWeight reverse_duration,
|
||||||
EdgeWeight forward_duration_offset,
|
EdgeWeight forward_duration_offset,
|
||||||
@@ -144,7 +179,9 @@ struct PhantomNode
|
|||||||
: forward_segment_id{other.forward_segment_id},
|
: forward_segment_id{other.forward_segment_id},
|
||||||
reverse_segment_id{other.reverse_segment_id}, forward_weight{forward_weight},
|
reverse_segment_id{other.reverse_segment_id}, forward_weight{forward_weight},
|
||||||
reverse_weight{reverse_weight}, forward_weight_offset{forward_weight_offset},
|
reverse_weight{reverse_weight}, forward_weight_offset{forward_weight_offset},
|
||||||
reverse_weight_offset{reverse_weight_offset}, forward_duration{forward_duration},
|
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_duration{reverse_duration}, forward_duration_offset{forward_duration_offset},
|
reverse_duration{reverse_duration}, forward_duration_offset{forward_duration_offset},
|
||||||
reverse_duration_offset{reverse_duration_offset},
|
reverse_duration_offset{reverse_duration_offset},
|
||||||
component{component.id, component.is_tiny}, location{location},
|
component{component.id, component.is_tiny}, location{location},
|
||||||
@@ -162,13 +199,17 @@ struct PhantomNode
|
|||||||
EdgeWeight reverse_weight;
|
EdgeWeight reverse_weight;
|
||||||
EdgeWeight forward_weight_offset; // TODO: try to remove -> requires path unpacking changes
|
EdgeWeight forward_weight_offset; // TODO: try to remove -> requires path unpacking changes
|
||||||
EdgeWeight reverse_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 forward_duration;
|
||||||
EdgeWeight reverse_duration;
|
EdgeWeight reverse_duration;
|
||||||
EdgeWeight forward_duration_offset; // TODO: try to remove -> requires path unpacking changes
|
EdgeWeight forward_duration_offset; // TODO: try to remove -> requires path unpacking changes
|
||||||
EdgeWeight reverse_duration_offset; // TODO: try to remove -> requires path unpacking changes
|
EdgeWeight reverse_duration_offset; // TODO: try to remove -> requires path unpacking changes
|
||||||
ComponentID component;
|
ComponentID component;
|
||||||
|
|
||||||
util::Coordinate location;
|
util::Coordinate location; // this is the coordinate of x
|
||||||
util::Coordinate input_location;
|
util::Coordinate input_location;
|
||||||
unsigned short fwd_segment_position;
|
unsigned short fwd_segment_position;
|
||||||
// is phantom node valid to be used as source or target
|
// is phantom node valid to be used as source or target
|
||||||
@@ -180,7 +221,7 @@ struct PhantomNode
|
|||||||
unsigned short bearing : 12;
|
unsigned short bearing : 12;
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(sizeof(PhantomNode) == 64, "PhantomNode has more padding then expected");
|
static_assert(sizeof(PhantomNode) == 80, "PhantomNode has more padding then expected");
|
||||||
|
|
||||||
using PhantomNodePair = std::pair<PhantomNode, PhantomNode>;
|
using PhantomNodePair = std::pair<PhantomNode, PhantomNode>;
|
||||||
|
|
||||||
@@ -195,7 +236,7 @@ struct PhantomNodes
|
|||||||
PhantomNode source_phantom;
|
PhantomNode source_phantom;
|
||||||
PhantomNode target_phantom;
|
PhantomNode target_phantom;
|
||||||
};
|
};
|
||||||
}
|
} // namespace engine
|
||||||
}
|
} // namespace osrm
|
||||||
|
|
||||||
#endif // PHANTOM_NODES_H
|
#endif // PHANTOM_NODES_H
|
||||||
|
|||||||
@@ -30,10 +30,12 @@ class RoutingAlgorithmsInterface
|
|||||||
virtual InternalRouteResult
|
virtual InternalRouteResult
|
||||||
DirectShortestPathSearch(const PhantomNodes &phantom_node_pair) const = 0;
|
DirectShortestPathSearch(const PhantomNodes &phantom_node_pair) const = 0;
|
||||||
|
|
||||||
virtual std::vector<EdgeDuration>
|
virtual std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
|
||||||
ManyToManySearch(const std::vector<PhantomNode> &phantom_nodes,
|
ManyToManySearch(const std::vector<PhantomNode> &phantom_nodes,
|
||||||
const std::vector<std::size_t> &source_indices,
|
const std::vector<std::size_t> &source_indices,
|
||||||
const std::vector<std::size_t> &target_indices) const = 0;
|
const std::vector<std::size_t> &target_indices,
|
||||||
|
const bool calculate_distance,
|
||||||
|
const bool calculate_duration) const = 0;
|
||||||
|
|
||||||
virtual routing_algorithms::SubMatchingList
|
virtual routing_algorithms::SubMatchingList
|
||||||
MapMatching(const routing_algorithms::CandidateLists &candidates_list,
|
MapMatching(const routing_algorithms::CandidateLists &candidates_list,
|
||||||
@@ -53,6 +55,7 @@ class RoutingAlgorithmsInterface
|
|||||||
virtual bool HasDirectShortestPathSearch() const = 0;
|
virtual bool HasDirectShortestPathSearch() const = 0;
|
||||||
virtual bool HasMapMatching() const = 0;
|
virtual bool HasMapMatching() const = 0;
|
||||||
virtual bool HasManyToManySearch() const = 0;
|
virtual bool HasManyToManySearch() const = 0;
|
||||||
|
virtual bool SupportsDistanceAnnotationType() const = 0;
|
||||||
virtual bool HasGetTileTurns() const = 0;
|
virtual bool HasGetTileTurns() const = 0;
|
||||||
virtual bool HasExcludeFlags() const = 0;
|
virtual bool HasExcludeFlags() const = 0;
|
||||||
virtual bool IsValid() const = 0;
|
virtual bool IsValid() const = 0;
|
||||||
@@ -81,10 +84,12 @@ template <typename Algorithm> class RoutingAlgorithms final : public RoutingAlgo
|
|||||||
InternalRouteResult
|
InternalRouteResult
|
||||||
DirectShortestPathSearch(const PhantomNodes &phantom_nodes) const final override;
|
DirectShortestPathSearch(const PhantomNodes &phantom_nodes) const final override;
|
||||||
|
|
||||||
std::vector<EdgeDuration>
|
virtual std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
|
||||||
ManyToManySearch(const std::vector<PhantomNode> &phantom_nodes,
|
ManyToManySearch(const std::vector<PhantomNode> &phantom_nodes,
|
||||||
const std::vector<std::size_t> &source_indices,
|
const std::vector<std::size_t> &source_indices,
|
||||||
const std::vector<std::size_t> &target_indices) const final override;
|
const std::vector<std::size_t> &target_indices,
|
||||||
|
const bool calculate_distance,
|
||||||
|
const bool calculate_duration) const final override;
|
||||||
|
|
||||||
routing_algorithms::SubMatchingList
|
routing_algorithms::SubMatchingList
|
||||||
MapMatching(const routing_algorithms::CandidateLists &candidates_list,
|
MapMatching(const routing_algorithms::CandidateLists &candidates_list,
|
||||||
@@ -124,6 +129,11 @@ template <typename Algorithm> class RoutingAlgorithms final : public RoutingAlgo
|
|||||||
return routing_algorithms::HasManyToManySearch<Algorithm>::value;
|
return routing_algorithms::HasManyToManySearch<Algorithm>::value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SupportsDistanceAnnotationType() const final override
|
||||||
|
{
|
||||||
|
return routing_algorithms::SupportsDistanceAnnotationType<Algorithm>::value;
|
||||||
|
}
|
||||||
|
|
||||||
bool HasGetTileTurns() const final override
|
bool HasGetTileTurns() const final override
|
||||||
{
|
{
|
||||||
return routing_algorithms::HasGetTileTurns<Algorithm>::value;
|
return routing_algorithms::HasGetTileTurns<Algorithm>::value;
|
||||||
@@ -184,10 +194,12 @@ inline routing_algorithms::SubMatchingList RoutingAlgorithms<Algorithm>::MapMatc
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename Algorithm>
|
template <typename Algorithm>
|
||||||
std::vector<EdgeDuration> RoutingAlgorithms<Algorithm>::ManyToManySearch(
|
std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
|
||||||
const std::vector<PhantomNode> &phantom_nodes,
|
RoutingAlgorithms<Algorithm>::ManyToManySearch(const std::vector<PhantomNode> &phantom_nodes,
|
||||||
const std::vector<std::size_t> &_source_indices,
|
const std::vector<std::size_t> &_source_indices,
|
||||||
const std::vector<std::size_t> &_target_indices) const
|
const std::vector<std::size_t> &_target_indices,
|
||||||
|
const bool calculate_distance,
|
||||||
|
const bool calculate_duration) const
|
||||||
{
|
{
|
||||||
BOOST_ASSERT(!phantom_nodes.empty());
|
BOOST_ASSERT(!phantom_nodes.empty());
|
||||||
|
|
||||||
@@ -205,8 +217,13 @@ std::vector<EdgeDuration> RoutingAlgorithms<Algorithm>::ManyToManySearch(
|
|||||||
std::iota(target_indices.begin(), target_indices.end(), 0);
|
std::iota(target_indices.begin(), target_indices.end(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return routing_algorithms::manyToManySearch(
|
return routing_algorithms::manyToManySearch(heaps,
|
||||||
heaps, *facade, phantom_nodes, std::move(source_indices), std::move(target_indices));
|
*facade,
|
||||||
|
phantom_nodes,
|
||||||
|
std::move(source_indices),
|
||||||
|
std::move(target_indices),
|
||||||
|
calculate_distance,
|
||||||
|
calculate_duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Algorithm>
|
template <typename Algorithm>
|
||||||
|
|||||||
@@ -15,23 +15,46 @@ namespace engine
|
|||||||
{
|
{
|
||||||
namespace routing_algorithms
|
namespace routing_algorithms
|
||||||
{
|
{
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
struct NodeBucket
|
struct NodeBucket
|
||||||
{
|
{
|
||||||
NodeID middle_node;
|
NodeID middle_node;
|
||||||
unsigned column_index; // a column in the weight/duration matrix
|
NodeID parent_node;
|
||||||
|
unsigned column_index : 31; // a column in the weight/duration matrix
|
||||||
|
unsigned from_clique_arc : 1;
|
||||||
EdgeWeight weight;
|
EdgeWeight weight;
|
||||||
EdgeDuration duration;
|
EdgeDuration duration;
|
||||||
|
EdgeDistance distance;
|
||||||
|
|
||||||
NodeBucket(NodeID middle_node, unsigned column_index, EdgeWeight weight, EdgeDuration duration)
|
NodeBucket(NodeID middle_node,
|
||||||
: middle_node(middle_node), column_index(column_index), weight(weight), duration(duration)
|
NodeID parent_node,
|
||||||
|
bool from_clique_arc,
|
||||||
|
unsigned column_index,
|
||||||
|
EdgeWeight weight,
|
||||||
|
EdgeDuration duration,
|
||||||
|
EdgeDistance distance)
|
||||||
|
: middle_node(middle_node), parent_node(parent_node), column_index(column_index),
|
||||||
|
from_clique_arc(from_clique_arc), weight(weight), duration(duration), distance(distance)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeBucket(NodeID middle_node,
|
||||||
|
NodeID parent_node,
|
||||||
|
unsigned column_index,
|
||||||
|
EdgeWeight weight,
|
||||||
|
EdgeDuration duration,
|
||||||
|
EdgeDistance distance)
|
||||||
|
: middle_node(middle_node), parent_node(parent_node), column_index(column_index),
|
||||||
|
from_clique_arc(false), weight(weight), duration(duration), distance(distance)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
// partial order comparison
|
// partial order comparison
|
||||||
bool operator<(const NodeBucket &rhs) const { return middle_node < rhs.middle_node; }
|
bool operator<(const NodeBucket &rhs) const
|
||||||
|
{
|
||||||
|
return std::tie(middle_node, column_index) < std::tie(rhs.middle_node, rhs.column_index);
|
||||||
|
}
|
||||||
|
|
||||||
// functor for equal_range
|
// functor for equal_range
|
||||||
struct Compare
|
struct Compare
|
||||||
@@ -46,15 +69,36 @@ struct NodeBucket
|
|||||||
return lhs < rhs.middle_node;
|
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>
|
template <typename Algorithm>
|
||||||
std::vector<EdgeDuration> manyToManySearch(SearchEngineData<Algorithm> &engine_working_data,
|
std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
|
||||||
const DataFacade<Algorithm> &facade,
|
manyToManySearch(SearchEngineData<Algorithm> &engine_working_data,
|
||||||
const std::vector<PhantomNode> &phantom_nodes,
|
const DataFacade<Algorithm> &facade,
|
||||||
const std::vector<std::size_t> &source_indices,
|
const std::vector<PhantomNode> &phantom_nodes,
|
||||||
const std::vector<std::size_t> &target_indices);
|
const std::vector<std::size_t> &source_indices,
|
||||||
|
const std::vector<std::size_t> &target_indices,
|
||||||
|
const bool calculate_distance,
|
||||||
|
const bool calculate_duration);
|
||||||
|
|
||||||
} // namespace routing_algorithms
|
} // namespace routing_algorithms
|
||||||
} // namespace engine
|
} // namespace engine
|
||||||
|
|||||||
@@ -85,13 +85,17 @@ void insertSourceInHeap(ManyToManyQueryHeap &heap, const PhantomNode &phantom_no
|
|||||||
{
|
{
|
||||||
heap.Insert(phantom_node.forward_segment_id.id,
|
heap.Insert(phantom_node.forward_segment_id.id,
|
||||||
-phantom_node.GetForwardWeightPlusOffset(),
|
-phantom_node.GetForwardWeightPlusOffset(),
|
||||||
{phantom_node.forward_segment_id.id, -phantom_node.GetForwardDuration()});
|
{phantom_node.forward_segment_id.id,
|
||||||
|
-phantom_node.GetForwardDuration(),
|
||||||
|
-phantom_node.GetForwardDistance()});
|
||||||
}
|
}
|
||||||
if (phantom_node.IsValidReverseSource())
|
if (phantom_node.IsValidReverseSource())
|
||||||
{
|
{
|
||||||
heap.Insert(phantom_node.reverse_segment_id.id,
|
heap.Insert(phantom_node.reverse_segment_id.id,
|
||||||
-phantom_node.GetReverseWeightPlusOffset(),
|
-phantom_node.GetReverseWeightPlusOffset(),
|
||||||
{phantom_node.reverse_segment_id.id, -phantom_node.GetReverseDuration()});
|
{phantom_node.reverse_segment_id.id,
|
||||||
|
-phantom_node.GetReverseDuration(),
|
||||||
|
-phantom_node.GetReverseDistance()});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,13 +106,17 @@ void insertTargetInHeap(ManyToManyQueryHeap &heap, const PhantomNode &phantom_no
|
|||||||
{
|
{
|
||||||
heap.Insert(phantom_node.forward_segment_id.id,
|
heap.Insert(phantom_node.forward_segment_id.id,
|
||||||
phantom_node.GetForwardWeightPlusOffset(),
|
phantom_node.GetForwardWeightPlusOffset(),
|
||||||
{phantom_node.forward_segment_id.id, phantom_node.GetForwardDuration()});
|
{phantom_node.forward_segment_id.id,
|
||||||
|
phantom_node.GetForwardDuration(),
|
||||||
|
phantom_node.GetForwardDistance()});
|
||||||
}
|
}
|
||||||
if (phantom_node.IsValidReverseTarget())
|
if (phantom_node.IsValidReverseTarget())
|
||||||
{
|
{
|
||||||
heap.Insert(phantom_node.reverse_segment_id.id,
|
heap.Insert(phantom_node.reverse_segment_id.id,
|
||||||
phantom_node.GetReverseWeightPlusOffset(),
|
phantom_node.GetReverseWeightPlusOffset(),
|
||||||
{phantom_node.reverse_segment_id.id, phantom_node.GetReverseDuration()});
|
{phantom_node.reverse_segment_id.id,
|
||||||
|
phantom_node.GetReverseDuration(),
|
||||||
|
phantom_node.GetReverseDistance()});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,6 +189,7 @@ void annotatePath(const FacadeT &facade,
|
|||||||
BOOST_ASSERT(datasource_vector.size() > 0);
|
BOOST_ASSERT(datasource_vector.size() > 0);
|
||||||
BOOST_ASSERT(weight_vector.size() + 1 == id_vector.size());
|
BOOST_ASSERT(weight_vector.size() + 1 == id_vector.size());
|
||||||
BOOST_ASSERT(duration_vector.size() + 1 == id_vector.size());
|
BOOST_ASSERT(duration_vector.size() + 1 == id_vector.size());
|
||||||
|
|
||||||
const bool is_first_segment = unpacked_path.empty();
|
const bool is_first_segment = unpacked_path.empty();
|
||||||
|
|
||||||
const std::size_t start_index =
|
const std::size_t start_index =
|
||||||
@@ -405,6 +414,22 @@ InternalRouteResult extractRoute(const DataFacade<AlgorithmT> &facade,
|
|||||||
return raw_route_data;
|
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 routing_algorithms
|
||||||
} // namespace engine
|
} // namespace engine
|
||||||
} // namespace osrm
|
} // namespace osrm
|
||||||
|
|||||||
@@ -186,9 +186,10 @@ void routingStep(const DataFacade<Algorithm> &facade,
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <bool UseDuration>
|
template <bool UseDuration>
|
||||||
EdgeWeight getLoopWeight(const DataFacade<Algorithm> &facade, NodeID node)
|
std::tuple<EdgeWeight, EdgeDistance> getLoopWeight(const DataFacade<Algorithm> &facade, NodeID node)
|
||||||
{
|
{
|
||||||
EdgeWeight loop_weight = UseDuration ? MAXIMAL_EDGE_DURATION : INVALID_EDGE_WEIGHT;
|
EdgeWeight loop_weight = UseDuration ? MAXIMAL_EDGE_DURATION : INVALID_EDGE_WEIGHT;
|
||||||
|
EdgeDistance loop_distance = MAXIMAL_EDGE_DISTANCE;
|
||||||
for (auto edge : facade.GetAdjacentEdgeRange(node))
|
for (auto edge : facade.GetAdjacentEdgeRange(node))
|
||||||
{
|
{
|
||||||
const auto &data = facade.GetEdgeData(edge);
|
const auto &data = facade.GetEdgeData(edge);
|
||||||
@@ -198,11 +199,15 @@ EdgeWeight getLoopWeight(const DataFacade<Algorithm> &facade, NodeID node)
|
|||||||
if (to == node)
|
if (to == node)
|
||||||
{
|
{
|
||||||
const auto value = UseDuration ? data.duration : data.weight;
|
const auto value = UseDuration ? data.duration : data.weight;
|
||||||
loop_weight = std::min(loop_weight, value);
|
if (value < loop_weight)
|
||||||
|
{
|
||||||
|
loop_weight = value;
|
||||||
|
loop_distance = data.distance;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return loop_weight;
|
return std::make_tuple(loop_weight, loop_distance);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -288,6 +293,106 @@ 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>
|
template <typename RandomIter, typename FacadeT>
|
||||||
void unpackPath(const FacadeT &facade,
|
void unpackPath(const FacadeT &facade,
|
||||||
RandomIter packed_path_begin,
|
RandomIter packed_path_begin,
|
||||||
@@ -340,6 +445,11 @@ void retrievePackedPathFromSingleHeap(const SearchEngineData<Algorithm>::QueryHe
|
|||||||
const NodeID middle_node_id,
|
const NodeID middle_node_id,
|
||||||
std::vector<NodeID> &packed_path);
|
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.
|
// assumes that heaps are already setup correctly.
|
||||||
// ATTENTION: This only works if no additional offset is supplied next to the Phantom Node
|
// ATTENTION: This only works if no additional offset is supplied next to the Phantom Node
|
||||||
// Offsets.
|
// Offsets.
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ inline bool checkParentCellRestriction(CellID, const PhantomNodes &) { return tr
|
|||||||
|
|
||||||
// Restricted search (Args is LevelID, CellID):
|
// Restricted search (Args is LevelID, CellID):
|
||||||
// * use the fixed level for queries
|
// * use the fixed level for queries
|
||||||
// * check if the node cell is the same as the specified parent onr
|
// * check if the node cell is the same as the specified parent
|
||||||
template <typename MultiLevelPartition>
|
template <typename MultiLevelPartition>
|
||||||
inline LevelID getNodeQueryLevel(const MultiLevelPartition &, NodeID, LevelID level, CellID)
|
inline LevelID getNodeQueryLevel(const MultiLevelPartition &, NodeID, LevelID level, CellID)
|
||||||
{
|
{
|
||||||
@@ -65,6 +65,61 @@ inline bool checkParentCellRestriction(CellID cell, LevelID, CellID parent)
|
|||||||
{
|
{
|
||||||
return cell == 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Heaps only record for each node its predecessor ("parent") on the shortest path.
|
// Heaps only record for each node its predecessor ("parent") on the shortest path.
|
||||||
@@ -74,6 +129,46 @@ inline bool checkParentCellRestriction(CellID cell, LevelID, CellID parent)
|
|||||||
using PackedEdge = std::tuple</*from*/ NodeID, /*to*/ NodeID, /*from_clique_arc*/ bool>;
|
using PackedEdge = std::tuple</*from*/ NodeID, /*to*/ NodeID, /*from_clique_arc*/ bool>;
|
||||||
using PackedPath = std::vector<PackedEdge>;
|
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>
|
template <bool DIRECTION, typename OutIter>
|
||||||
inline void retrievePackedPathFromSingleHeap(const SearchEngineData<Algorithm>::QueryHeap &heap,
|
inline void retrievePackedPathFromSingleHeap(const SearchEngineData<Algorithm>::QueryHeap &heap,
|
||||||
const NodeID middle,
|
const NodeID middle,
|
||||||
@@ -206,15 +301,22 @@ void relaxOutgoingEdges(const DataFacade<Algorithm> &facade,
|
|||||||
for (const auto edge : facade.GetBorderEdgeRange(level, node))
|
for (const auto edge : facade.GetBorderEdgeRange(level, node))
|
||||||
{
|
{
|
||||||
const auto &edge_data = facade.GetEdgeData(edge);
|
const auto &edge_data = facade.GetEdgeData(edge);
|
||||||
if (DIRECTION == FORWARD_DIRECTION ? edge_data.forward : edge_data.backward)
|
|
||||||
|
if ((DIRECTION == FORWARD_DIRECTION) ? facade.IsForwardEdge(edge)
|
||||||
|
: facade.IsBackwardEdge(edge))
|
||||||
{
|
{
|
||||||
const NodeID to = facade.GetTarget(edge);
|
const NodeID to = facade.GetTarget(edge);
|
||||||
|
|
||||||
if (!facade.ExcludeNode(to) &&
|
if (!facade.ExcludeNode(to) &&
|
||||||
checkParentCellRestriction(partition.GetCell(level + 1, to), args...))
|
checkParentCellRestriction(partition.GetCell(level + 1, to), args...))
|
||||||
{
|
{
|
||||||
BOOST_ASSERT_MSG(edge_data.weight > 0, "edge_weight invalid");
|
const auto node_weight =
|
||||||
const EdgeWeight to_weight = weight + edge_data.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;
|
||||||
|
|
||||||
if (!forward_heap.WasInserted(to))
|
if (!forward_heap.WasInserted(to))
|
||||||
{
|
{
|
||||||
@@ -407,6 +509,90 @@ UnpackedPath search(SearchEngineData<Algorithm> &engine_working_data,
|
|||||||
return std::make_tuple(weight, std::move(unpacked_nodes), std::move(unpacked_edges));
|
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
|
// Alias to be compatible with the CH-based search
|
||||||
template <typename Algorithm>
|
template <typename Algorithm>
|
||||||
inline void search(SearchEngineData<Algorithm> &engine_working_data,
|
inline void search(SearchEngineData<Algorithm> &engine_working_data,
|
||||||
|
|||||||
@@ -30,7 +30,11 @@ struct HeapData
|
|||||||
struct ManyToManyHeapData : HeapData
|
struct ManyToManyHeapData : HeapData
|
||||||
{
|
{
|
||||||
EdgeWeight duration;
|
EdgeWeight duration;
|
||||||
ManyToManyHeapData(NodeID p, EdgeWeight duration) : HeapData(p), duration(duration) {}
|
EdgeDistance distance;
|
||||||
|
ManyToManyHeapData(NodeID p, EdgeWeight duration, EdgeDistance distance)
|
||||||
|
: HeapData(p), duration(duration), distance(distance)
|
||||||
|
{
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <> struct SearchEngineData<routing_algorithms::ch::Algorithm>
|
template <> struct SearchEngineData<routing_algorithms::ch::Algorithm>
|
||||||
@@ -75,12 +79,16 @@ struct MultiLayerDijkstraHeapData
|
|||||||
struct ManyToManyMultiLayerDijkstraHeapData : MultiLayerDijkstraHeapData
|
struct ManyToManyMultiLayerDijkstraHeapData : MultiLayerDijkstraHeapData
|
||||||
{
|
{
|
||||||
EdgeWeight duration;
|
EdgeWeight duration;
|
||||||
ManyToManyMultiLayerDijkstraHeapData(NodeID p, EdgeWeight duration)
|
EdgeDistance distance;
|
||||||
: MultiLayerDijkstraHeapData(p), duration(duration)
|
ManyToManyMultiLayerDijkstraHeapData(NodeID p, EdgeWeight duration, EdgeDistance distance)
|
||||||
|
: MultiLayerDijkstraHeapData(p), duration(duration), distance(distance)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
ManyToManyMultiLayerDijkstraHeapData(NodeID p, bool from, EdgeWeight duration)
|
ManyToManyMultiLayerDijkstraHeapData(NodeID p,
|
||||||
: MultiLayerDijkstraHeapData(p, from), duration(duration)
|
bool from,
|
||||||
|
EdgeWeight duration,
|
||||||
|
EdgeDistance distance)
|
||||||
|
: MultiLayerDijkstraHeapData(p, from), duration(duration), distance(distance)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -112,7 +120,7 @@ template <> struct SearchEngineData<routing_algorithms::mld::Algorithm>
|
|||||||
void InitializeOrClearManyToManyThreadLocalStorage(unsigned number_of_nodes,
|
void InitializeOrClearManyToManyThreadLocalStorage(unsigned number_of_nodes,
|
||||||
unsigned number_of_boundary_nodes);
|
unsigned number_of_boundary_nodes);
|
||||||
};
|
};
|
||||||
}
|
} // namespace engine
|
||||||
}
|
} // namespace osrm
|
||||||
|
|
||||||
#endif // SEARCH_ENGINE_DATA_HPP
|
#endif // SEARCH_ENGINE_DATA_HPP
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ class CompressedEdgeContainer
|
|||||||
std::unordered_map<EdgeID, unsigned> m_reverse_edge_id_to_zipped_index_map;
|
std::unordered_map<EdgeID, unsigned> m_reverse_edge_id_to_zipped_index_map;
|
||||||
std::unique_ptr<SegmentDataContainer> segment_data;
|
std::unique_ptr<SegmentDataContainer> segment_data;
|
||||||
};
|
};
|
||||||
}
|
} // namespace extractor
|
||||||
}
|
} // namespace osrm
|
||||||
|
|
||||||
#endif // GEOMETRY_COMPRESSOR_HPP_
|
#endif // GEOMETRY_COMPRESSOR_HPP_
|
||||||
|
|||||||
@@ -15,20 +15,25 @@ struct EdgeBasedEdge
|
|||||||
public:
|
public:
|
||||||
struct EdgeData
|
struct EdgeData
|
||||||
{
|
{
|
||||||
EdgeData() : turn_id(0), weight(0), duration(0), forward(false), backward(false) {}
|
EdgeData()
|
||||||
|
: turn_id(0), weight(0), distance(0), duration(0), forward(false), backward(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
EdgeData(const NodeID turn_id,
|
EdgeData(const NodeID turn_id,
|
||||||
const EdgeWeight weight,
|
const EdgeWeight weight,
|
||||||
|
const EdgeDistance distance,
|
||||||
const EdgeWeight duration,
|
const EdgeWeight duration,
|
||||||
const bool forward,
|
const bool forward,
|
||||||
const bool backward)
|
const bool backward)
|
||||||
: turn_id(turn_id), weight(weight), duration(duration), forward(forward),
|
: turn_id(turn_id), weight(weight), distance(distance), duration(duration),
|
||||||
backward(backward)
|
forward(forward), backward(backward)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeID turn_id; // ID of the edge based node (node based edge)
|
NodeID turn_id; // ID of the edge based node (node based edge)
|
||||||
EdgeWeight weight;
|
EdgeWeight weight;
|
||||||
|
EdgeDistance distance;
|
||||||
EdgeWeight duration : 30;
|
EdgeWeight duration : 30;
|
||||||
std::uint32_t forward : 1;
|
std::uint32_t forward : 1;
|
||||||
std::uint32_t backward : 1;
|
std::uint32_t backward : 1;
|
||||||
@@ -43,6 +48,7 @@ struct EdgeBasedEdge
|
|||||||
const NodeID edge_id,
|
const NodeID edge_id,
|
||||||
const EdgeWeight weight,
|
const EdgeWeight weight,
|
||||||
const EdgeWeight duration,
|
const EdgeWeight duration,
|
||||||
|
const EdgeDistance distance,
|
||||||
const bool forward,
|
const bool forward,
|
||||||
const bool backward);
|
const bool backward);
|
||||||
EdgeBasedEdge(const NodeID source, const NodeID target, const EdgeBasedEdge::EdgeData &data);
|
EdgeBasedEdge(const NodeID source, const NodeID target, const EdgeBasedEdge::EdgeData &data);
|
||||||
@@ -53,7 +59,7 @@ struct EdgeBasedEdge
|
|||||||
NodeID target;
|
NodeID target;
|
||||||
EdgeData data;
|
EdgeData data;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(extractor::EdgeBasedEdge) == 20,
|
static_assert(sizeof(extractor::EdgeBasedEdge) == 24,
|
||||||
"Size of extractor::EdgeBasedEdge type is "
|
"Size of extractor::EdgeBasedEdge type is "
|
||||||
"bigger than expected. This will influence "
|
"bigger than expected. This will influence "
|
||||||
"memory consumption.");
|
"memory consumption.");
|
||||||
@@ -67,9 +73,10 @@ inline EdgeBasedEdge::EdgeBasedEdge(const NodeID source,
|
|||||||
const NodeID turn_id,
|
const NodeID turn_id,
|
||||||
const EdgeWeight weight,
|
const EdgeWeight weight,
|
||||||
const EdgeWeight duration,
|
const EdgeWeight duration,
|
||||||
|
const EdgeDistance distance,
|
||||||
const bool forward,
|
const bool forward,
|
||||||
const bool backward)
|
const bool backward)
|
||||||
: source(source), target(target), data{turn_id, weight, duration, forward, backward}
|
: source(source), target(target), data{turn_id, weight, distance, duration, forward, backward}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,7 +96,7 @@ inline bool EdgeBasedEdge::operator<(const EdgeBasedEdge &other) const
|
|||||||
return std::tie(source, target, data.weight, unidirectional) <
|
return std::tie(source, target, data.weight, unidirectional) <
|
||||||
std::tie(other.source, other.target, other.data.weight, other_is_unidirectional);
|
std::tie(other.source, other.target, other.data.weight, other_is_unidirectional);
|
||||||
}
|
}
|
||||||
} // ns extractor
|
} // namespace extractor
|
||||||
} // ns osrm
|
} // namespace osrm
|
||||||
|
|
||||||
#endif /* EDGE_BASED_EDGE_HPP */
|
#endif /* EDGE_BASED_EDGE_HPP */
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ class EdgeBasedGraphFactory
|
|||||||
void GetEdgeBasedNodeSegments(std::vector<EdgeBasedNodeSegment> &nodes);
|
void GetEdgeBasedNodeSegments(std::vector<EdgeBasedNodeSegment> &nodes);
|
||||||
void GetStartPointMarkers(std::vector<bool> &node_is_startpoint);
|
void GetStartPointMarkers(std::vector<bool> &node_is_startpoint);
|
||||||
void GetEdgeBasedNodeWeights(std::vector<EdgeWeight> &output_node_weights);
|
void GetEdgeBasedNodeWeights(std::vector<EdgeWeight> &output_node_weights);
|
||||||
|
void GetEdgeBasedNodeDurations(std::vector<EdgeWeight> &output_node_durations);
|
||||||
std::uint32_t GetConnectivityChecksum() const;
|
std::uint32_t GetConnectivityChecksum() const;
|
||||||
|
|
||||||
std::uint64_t GetNumberOfEdgeBasedNodes() const;
|
std::uint64_t GetNumberOfEdgeBasedNodes() const;
|
||||||
@@ -117,6 +118,7 @@ class EdgeBasedGraphFactory
|
|||||||
//! node weights that indicate the length of the segment (node based) represented by the
|
//! node weights that indicate the length of the segment (node based) represented by the
|
||||||
//! edge-based node
|
//! edge-based node
|
||||||
std::vector<EdgeWeight> m_edge_based_node_weights;
|
std::vector<EdgeWeight> m_edge_based_node_weights;
|
||||||
|
std::vector<EdgeDuration> m_edge_based_node_durations;
|
||||||
|
|
||||||
//! list of edge based nodes (compressed segments)
|
//! list of edge based nodes (compressed segments)
|
||||||
std::vector<EdgeBasedNodeSegment> m_edge_based_node_segments;
|
std::vector<EdgeBasedNodeSegment> m_edge_based_node_segments;
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ class Extractor
|
|||||||
std::vector<EdgeBasedNodeSegment> &edge_based_node_segments,
|
std::vector<EdgeBasedNodeSegment> &edge_based_node_segments,
|
||||||
std::vector<bool> &node_is_startpoint,
|
std::vector<bool> &node_is_startpoint,
|
||||||
std::vector<EdgeWeight> &edge_based_node_weights,
|
std::vector<EdgeWeight> &edge_based_node_weights,
|
||||||
|
std::vector<EdgeDuration> &edge_based_node_durations,
|
||||||
util::DeallocatingVector<EdgeBasedEdge> &edge_based_edge_list,
|
util::DeallocatingVector<EdgeBasedEdge> &edge_based_edge_list,
|
||||||
std::uint32_t &connectivity_checksum);
|
std::uint32_t &connectivity_checksum);
|
||||||
|
|
||||||
|
|||||||
@@ -462,14 +462,28 @@ void readEdgeBasedNodeWeights(const boost::filesystem::path &path, NodeWeigtsVec
|
|||||||
storage::serialization::read(reader, "/extractor/edge_based_node_weights", weights);
|
storage::serialization::read(reader, "/extractor/edge_based_node_weights", weights);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename NodeWeigtsVectorT>
|
template <typename NodeWeigtsVectorT, typename NodeDurationsVectorT>
|
||||||
void writeEdgeBasedNodeWeights(const boost::filesystem::path &path,
|
void readEdgeBasedNodeWeightsDurations(const boost::filesystem::path &path,
|
||||||
const NodeWeigtsVectorT &weights)
|
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)
|
||||||
{
|
{
|
||||||
const auto fingerprint = storage::tar::FileWriter::GenerateFingerprint;
|
const auto fingerprint = storage::tar::FileWriter::GenerateFingerprint;
|
||||||
storage::tar::FileWriter writer{path, fingerprint};
|
storage::tar::FileWriter writer{path, fingerprint};
|
||||||
|
|
||||||
storage::serialization::write(writer, "/extractor/edge_based_node_weights", weights);
|
storage::serialization::write(writer, "/extractor/edge_based_node_weights", weights);
|
||||||
|
storage::serialization::write(writer, "/extractor/edge_based_node_durations", durations);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename RTreeT>
|
template <typename RTreeT>
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ struct ByEdgeOrByMeterValue
|
|||||||
using value_type = float;
|
using value_type = float;
|
||||||
value_type value;
|
value_type value;
|
||||||
};
|
};
|
||||||
}
|
} // namespace detail
|
||||||
|
|
||||||
struct InternalExtractorEdge
|
struct InternalExtractorEdge
|
||||||
{
|
{
|
||||||
@@ -63,7 +63,7 @@ struct InternalExtractorEdge
|
|||||||
WeightData weight_data,
|
WeightData weight_data,
|
||||||
DurationData duration_data,
|
DurationData duration_data,
|
||||||
util::Coordinate source_coordinate)
|
util::Coordinate source_coordinate)
|
||||||
: result(source, target, 0, 0, {}, -1, {}), weight_data(std::move(weight_data)),
|
: result(source, target, 0, 0, 0, {}, -1, {}), weight_data(std::move(weight_data)),
|
||||||
duration_data(std::move(duration_data)), source_coordinate(std::move(source_coordinate))
|
duration_data(std::move(duration_data)), source_coordinate(std::move(source_coordinate))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -113,7 +113,7 @@ struct InternalExtractorEdge
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
} // namespace extractor
|
||||||
}
|
} // namespace osrm
|
||||||
|
|
||||||
#endif // INTERNAL_EXTRACTOR_EDGE_HPP
|
#endif // INTERNAL_EXTRACTOR_EDGE_HPP
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ struct NodeBasedEdge
|
|||||||
NodeID target,
|
NodeID target,
|
||||||
EdgeWeight weight,
|
EdgeWeight weight,
|
||||||
EdgeDuration duration,
|
EdgeDuration duration,
|
||||||
|
EdgeDistance distance,
|
||||||
GeometryID geometry_id,
|
GeometryID geometry_id,
|
||||||
AnnotationID annotation_data,
|
AnnotationID annotation_data,
|
||||||
NodeBasedEdgeClassification flags);
|
NodeBasedEdgeClassification flags);
|
||||||
@@ -107,6 +108,7 @@ struct NodeBasedEdge
|
|||||||
NodeID target; // 32 4
|
NodeID target; // 32 4
|
||||||
EdgeWeight weight; // 32 4
|
EdgeWeight weight; // 32 4
|
||||||
EdgeDuration duration; // 32 4
|
EdgeDuration duration; // 32 4
|
||||||
|
EdgeDistance distance; // 32 4
|
||||||
GeometryID geometry_id; // 32 4
|
GeometryID geometry_id; // 32 4
|
||||||
AnnotationID annotation_data; // 32 4
|
AnnotationID annotation_data; // 32 4
|
||||||
NodeBasedEdgeClassification flags; // 32 4
|
NodeBasedEdgeClassification flags; // 32 4
|
||||||
@@ -120,6 +122,7 @@ struct NodeBasedEdgeWithOSM : NodeBasedEdge
|
|||||||
OSMNodeID target,
|
OSMNodeID target,
|
||||||
EdgeWeight weight,
|
EdgeWeight weight,
|
||||||
EdgeDuration duration,
|
EdgeDuration duration,
|
||||||
|
EdgeDistance distance,
|
||||||
GeometryID geometry_id,
|
GeometryID geometry_id,
|
||||||
AnnotationID annotation_data,
|
AnnotationID annotation_data,
|
||||||
NodeBasedEdgeClassification flags);
|
NodeBasedEdgeClassification flags);
|
||||||
@@ -137,7 +140,8 @@ inline NodeBasedEdgeClassification::NodeBasedEdgeClassification()
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline NodeBasedEdge::NodeBasedEdge()
|
inline NodeBasedEdge::NodeBasedEdge()
|
||||||
: source(SPECIAL_NODEID), target(SPECIAL_NODEID), weight(0), duration(0), annotation_data(-1)
|
: source(SPECIAL_NODEID), target(SPECIAL_NODEID), weight(0), duration(0), distance(0),
|
||||||
|
annotation_data(-1)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,11 +149,12 @@ inline NodeBasedEdge::NodeBasedEdge(NodeID source,
|
|||||||
NodeID target,
|
NodeID target,
|
||||||
EdgeWeight weight,
|
EdgeWeight weight,
|
||||||
EdgeDuration duration,
|
EdgeDuration duration,
|
||||||
|
EdgeDistance distance,
|
||||||
GeometryID geometry_id,
|
GeometryID geometry_id,
|
||||||
AnnotationID annotation_data,
|
AnnotationID annotation_data,
|
||||||
NodeBasedEdgeClassification flags)
|
NodeBasedEdgeClassification flags)
|
||||||
: source(source), target(target), weight(weight), duration(duration), geometry_id(geometry_id),
|
: source(source), target(target), weight(weight), duration(duration), distance(distance),
|
||||||
annotation_data(annotation_data), flags(flags)
|
geometry_id(geometry_id), annotation_data(annotation_data), flags(flags)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,11 +180,18 @@ inline NodeBasedEdgeWithOSM::NodeBasedEdgeWithOSM(OSMNodeID source,
|
|||||||
OSMNodeID target,
|
OSMNodeID target,
|
||||||
EdgeWeight weight,
|
EdgeWeight weight,
|
||||||
EdgeDuration duration,
|
EdgeDuration duration,
|
||||||
|
EdgeDistance distance,
|
||||||
GeometryID geometry_id,
|
GeometryID geometry_id,
|
||||||
AnnotationID annotation_data,
|
AnnotationID annotation_data,
|
||||||
NodeBasedEdgeClassification flags)
|
NodeBasedEdgeClassification flags)
|
||||||
: NodeBasedEdge(
|
: NodeBasedEdge(SPECIAL_NODEID,
|
||||||
SPECIAL_NODEID, SPECIAL_NODEID, weight, duration, geometry_id, annotation_data, flags),
|
SPECIAL_NODEID,
|
||||||
|
weight,
|
||||||
|
duration,
|
||||||
|
distance,
|
||||||
|
geometry_id,
|
||||||
|
annotation_data,
|
||||||
|
flags),
|
||||||
osm_source_id(std::move(source)), osm_target_id(std::move(target))
|
osm_source_id(std::move(source)), osm_target_id(std::move(target))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -189,12 +201,12 @@ inline NodeBasedEdgeWithOSM::NodeBasedEdgeWithOSM()
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
static_assert(sizeof(extractor::NodeBasedEdge) == 28,
|
static_assert(sizeof(extractor::NodeBasedEdge) == 32,
|
||||||
"Size of extractor::NodeBasedEdge type is "
|
"Size of extractor::NodeBasedEdge type is "
|
||||||
"bigger than expected. This will influence "
|
"bigger than expected. This will influence "
|
||||||
"memory consumption.");
|
"memory consumption.");
|
||||||
|
|
||||||
} // ns extractor
|
} // namespace extractor
|
||||||
} // ns osrm
|
} // namespace osrm
|
||||||
|
|
||||||
#endif /* NODE_BASED_EDGE_HPP */
|
#endif /* NODE_BASED_EDGE_HPP */
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#define OSRM_BINDINGS_NODE_SUPPORT_HPP
|
#define OSRM_BINDINGS_NODE_SUPPORT_HPP
|
||||||
|
|
||||||
#include "nodejs/json_v8_renderer.hpp"
|
#include "nodejs/json_v8_renderer.hpp"
|
||||||
|
#include "util/json_renderer.hpp"
|
||||||
|
|
||||||
#include "osrm/approach.hpp"
|
#include "osrm/approach.hpp"
|
||||||
#include "osrm/bearing.hpp"
|
#include "osrm/bearing.hpp"
|
||||||
@@ -24,6 +25,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -42,6 +44,13 @@ using match_parameters_ptr = std::unique_ptr<osrm::MatchParameters>;
|
|||||||
using nearest_parameters_ptr = std::unique_ptr<osrm::NearestParameters>;
|
using nearest_parameters_ptr = std::unique_ptr<osrm::NearestParameters>;
|
||||||
using table_parameters_ptr = std::unique_ptr<osrm::TableParameters>;
|
using table_parameters_ptr = std::unique_ptr<osrm::TableParameters>;
|
||||||
|
|
||||||
|
struct PluginParameters
|
||||||
|
{
|
||||||
|
bool renderJSONToBuffer = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
using ObjectOrString = typename mapbox::util::variant<osrm::json::Object, std::string>;
|
||||||
|
|
||||||
template <typename ResultT> inline v8::Local<v8::Value> render(const ResultT &result);
|
template <typename ResultT> inline v8::Local<v8::Value> render(const ResultT &result);
|
||||||
|
|
||||||
template <> v8::Local<v8::Value> inline render(const std::string &result)
|
template <> v8::Local<v8::Value> inline render(const std::string &result)
|
||||||
@@ -49,11 +58,21 @@ template <> v8::Local<v8::Value> inline render(const std::string &result)
|
|||||||
return Nan::CopyBuffer(result.data(), result.size()).ToLocalChecked();
|
return Nan::CopyBuffer(result.data(), result.size()).ToLocalChecked();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <> v8::Local<v8::Value> inline render(const osrm::json::Object &result)
|
template <> v8::Local<v8::Value> inline render(const ObjectOrString &result)
|
||||||
{
|
{
|
||||||
v8::Local<v8::Value> value;
|
if (result.is<osrm::json::Object>())
|
||||||
renderToV8(value, result);
|
{
|
||||||
return value;
|
// Convert osrm::json object tree into matching v8 object tree
|
||||||
|
v8::Local<v8::Value> value;
|
||||||
|
renderToV8(value, result.get<osrm::json::Object>());
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Return the string object as a node Buffer
|
||||||
|
return Nan::CopyBuffer(result.get<std::string>().data(), result.get<std::string>().size())
|
||||||
|
.ToLocalChecked();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void ParseResult(const osrm::Status &result_status, osrm::json::Object &result)
|
inline void ParseResult(const osrm::Status &result_status, osrm::json::Object &result)
|
||||||
@@ -814,6 +833,50 @@ inline bool parseCommonParameters(const v8::Local<v8::Object> &obj, ParamType &p
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline PluginParameters
|
||||||
|
argumentsToPluginParameters(const Nan::FunctionCallbackInfo<v8::Value> &args)
|
||||||
|
{
|
||||||
|
if (args.Length() < 3 || !args[1]->IsObject())
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
v8::Local<v8::Object> obj = Nan::To<v8::Object>(args[1]).ToLocalChecked();
|
||||||
|
if (obj->Has(Nan::New("format").ToLocalChecked()))
|
||||||
|
{
|
||||||
|
|
||||||
|
v8::Local<v8::Value> format = obj->Get(Nan::New("format").ToLocalChecked());
|
||||||
|
if (format.IsEmpty())
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!format->IsString())
|
||||||
|
{
|
||||||
|
Nan::ThrowError("format must be a string: \"object\" or \"json_buffer\"");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const Nan::Utf8String format_utf8str(format);
|
||||||
|
std::string format_str{*format_utf8str, *format_utf8str + format_utf8str.length()};
|
||||||
|
|
||||||
|
if (format_str == "object")
|
||||||
|
{
|
||||||
|
return {false};
|
||||||
|
}
|
||||||
|
else if (format_str == "json_buffer")
|
||||||
|
{
|
||||||
|
return {true};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Nan::ThrowError("format must be a string: \"object\" or \"json_buffer\"");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
inline route_parameters_ptr
|
inline route_parameters_ptr
|
||||||
argumentsToRouteParameter(const Nan::FunctionCallbackInfo<v8::Value> &args,
|
argumentsToRouteParameter(const Nan::FunctionCallbackInfo<v8::Value> &args,
|
||||||
bool requires_multiple_coordinates)
|
bool requires_multiple_coordinates)
|
||||||
@@ -1064,6 +1127,46 @@ 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;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1317,6 +1420,6 @@ argumentsToMatchParameter(const Nan::FunctionCallbackInfo<v8::Value> &args,
|
|||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // ns node_osrm
|
} // namespace node_osrm
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -14,6 +14,8 @@
|
|||||||
#include <tbb/parallel_for.h>
|
#include <tbb/parallel_for.h>
|
||||||
#include <tbb/parallel_reduce.h>
|
#include <tbb/parallel_reduce.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@@ -43,6 +45,7 @@ splitBidirectionalEdges(const std::vector<extractor::EdgeBasedEdge> &edges)
|
|||||||
edge.data.turn_id,
|
edge.data.turn_id,
|
||||||
std::max(edge.data.weight, 1),
|
std::max(edge.data.weight, 1),
|
||||||
edge.data.duration,
|
edge.data.duration,
|
||||||
|
edge.data.distance,
|
||||||
edge.data.forward,
|
edge.data.forward,
|
||||||
edge.data.backward);
|
edge.data.backward);
|
||||||
|
|
||||||
@@ -51,6 +54,7 @@ splitBidirectionalEdges(const std::vector<extractor::EdgeBasedEdge> &edges)
|
|||||||
edge.data.turn_id,
|
edge.data.turn_id,
|
||||||
std::max(edge.data.weight, 1),
|
std::max(edge.data.weight, 1),
|
||||||
edge.data.duration,
|
edge.data.duration,
|
||||||
|
edge.data.distance,
|
||||||
edge.data.backward,
|
edge.data.backward,
|
||||||
edge.data.forward);
|
edge.data.forward);
|
||||||
}
|
}
|
||||||
@@ -196,7 +200,7 @@ inline DynamicEdgeBasedGraph LoadEdgeBasedGraph(const boost::filesystem::path &p
|
|||||||
return DynamicEdgeBasedGraph(number_of_edge_based_nodes, std::move(tidied), checksum);
|
return DynamicEdgeBasedGraph(number_of_edge_based_nodes, std::move(tidied), checksum);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // ns partition
|
} // namespace partitioner
|
||||||
} // ns osrm
|
} // namespace osrm
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
#ifndef OSRM_PARTITIONER_SERILIZATION_HPP
|
#ifndef OSRM_PARTITIONER_SERILIZATION_HPP
|
||||||
#define OSRM_PARTITIONER_SERILIZATION_HPP
|
#define OSRM_PARTITIONER_SERILIZATION_HPP
|
||||||
|
|
||||||
#include "customizer/edge_based_graph.hpp"
|
|
||||||
|
|
||||||
#include "partitioner/serialization.hpp"
|
#include "partitioner/serialization.hpp"
|
||||||
|
|
||||||
#include "storage/io.hpp"
|
#include "storage/io.hpp"
|
||||||
@@ -14,39 +12,6 @@ namespace partitioner
|
|||||||
namespace files
|
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
|
// read .osrm.partition file
|
||||||
template <typename MultiLevelPartitionT>
|
template <typename MultiLevelPartitionT>
|
||||||
inline void readPartition(const boost::filesystem::path &path, MultiLevelPartitionT &mlp)
|
inline void readPartition(const boost::filesystem::path &path, MultiLevelPartitionT &mlp)
|
||||||
@@ -102,6 +67,35 @@ inline void writeCells(const boost::filesystem::path &path, CellStorageT &storag
|
|||||||
|
|
||||||
serialization::write(writer, "/mld/cellstorage", storage);
|
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,6 +1,7 @@
|
|||||||
#ifndef OSRM_PARTITIONER_MULTI_LEVEL_GRAPH_HPP
|
#ifndef OSRM_PARTITIONER_MULTI_LEVEL_GRAPH_HPP
|
||||||
#define 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 "partitioner/multi_level_partition.hpp"
|
||||||
|
|
||||||
#include "storage/shared_memory_ownership.hpp"
|
#include "storage/shared_memory_ownership.hpp"
|
||||||
@@ -42,6 +43,8 @@ class MultiLevelGraph : public util::StaticGraph<EdgeDataT, Ownership>
|
|||||||
template <typename T> using Vector = util::ViewOrVector<T, Ownership>;
|
template <typename T> using Vector = util::ViewOrVector<T, Ownership>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
using SuperT::SuperT;
|
||||||
|
|
||||||
// We limit each node to have 255 edges
|
// We limit each node to have 255 edges
|
||||||
// this is very generous, we could probably pack this
|
// this is very generous, we could probably pack this
|
||||||
using EdgeOffset = std::uint8_t;
|
using EdgeOffset = std::uint8_t;
|
||||||
@@ -146,6 +149,14 @@ class MultiLevelGraph : public util::StaticGraph<EdgeDataT, Ownership>
|
|||||||
return max_border_node_id;
|
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:
|
private:
|
||||||
template <typename ContainerT>
|
template <typename ContainerT>
|
||||||
auto GetHighestBorderLevel(const MultiLevelPartition &mlp, const ContainerT &edges) const
|
auto GetHighestBorderLevel(const MultiLevelPartition &mlp, const ContainerT &edges) const
|
||||||
@@ -218,9 +229,13 @@ class MultiLevelGraph : public util::StaticGraph<EdgeDataT, Ownership>
|
|||||||
const std::string &name,
|
const std::string &name,
|
||||||
const MultiLevelGraph<EdgeDataT, Ownership> &graph);
|
const MultiLevelGraph<EdgeDataT, Ownership> &graph);
|
||||||
|
|
||||||
|
protected:
|
||||||
Vector<EdgeOffset> node_to_edge_offset;
|
Vector<EdgeOffset> node_to_edge_offset;
|
||||||
std::uint32_t connectivity_checksum;
|
std::uint32_t connectivity_checksum;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using MultiLevelEdgeBasedGraph =
|
||||||
|
MultiLevelGraph<EdgeBasedGraphEdgeData, storage::Ownership::Container>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ namespace partitioner
|
|||||||
struct PartitionerConfig final : storage::IOConfig
|
struct PartitionerConfig final : storage::IOConfig
|
||||||
{
|
{
|
||||||
PartitionerConfig()
|
PartitionerConfig()
|
||||||
: IOConfig({".osrm", ".osrm.fileIndex", ".osrm.ebg_nodes"},
|
: IOConfig({".osrm", ".osrm.fileIndex", ".osrm.ebg_nodes", ".osrm.enw"},
|
||||||
{".osrm.hsgr", ".osrm.cnbg"},
|
{".osrm.hsgr", ".osrm.cnbg"},
|
||||||
{".osrm.ebg",
|
{".osrm.ebg",
|
||||||
".osrm.cnbg",
|
".osrm.cnbg",
|
||||||
|
|||||||
@@ -19,26 +19,6 @@ namespace partitioner
|
|||||||
namespace serialization
|
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>
|
template <storage::Ownership Ownership>
|
||||||
inline void read(storage::tar::FileReader &reader,
|
inline void read(storage::tar::FileReader &reader,
|
||||||
const std::string &name,
|
const std::string &name,
|
||||||
|
|||||||
@@ -22,11 +22,11 @@ namespace qi = boost::spirit::qi;
|
|||||||
|
|
||||||
template <typename Iterator = std::string::iterator,
|
template <typename Iterator = std::string::iterator,
|
||||||
typename Signature = void(engine::api::TableParameters &)>
|
typename Signature = void(engine::api::TableParameters &)>
|
||||||
struct TableParametersGrammar final : public BaseParametersGrammar<Iterator, Signature>
|
struct TableParametersGrammar : public BaseParametersGrammar<Iterator, Signature>
|
||||||
{
|
{
|
||||||
using BaseGrammar = BaseParametersGrammar<Iterator, Signature>;
|
using BaseGrammar = BaseParametersGrammar<Iterator, Signature>;
|
||||||
|
|
||||||
TableParametersGrammar() : BaseGrammar(root_rule)
|
TableParametersGrammar() : TableParametersGrammar(root_rule)
|
||||||
{
|
{
|
||||||
#ifdef BOOST_HAS_LONG_LONG
|
#ifdef BOOST_HAS_LONG_LONG
|
||||||
if (std::is_same<std::size_t, unsigned long long>::value)
|
if (std::is_same<std::size_t, unsigned long long>::value)
|
||||||
@@ -51,15 +51,35 @@ struct TableParametersGrammar final : public BaseParametersGrammar<Iterator, Sig
|
|||||||
table_rule = destinations_rule(qi::_r1) | sources_rule(qi::_r1);
|
table_rule = destinations_rule(qi::_r1) | sources_rule(qi::_r1);
|
||||||
|
|
||||||
root_rule = BaseGrammar::query_rule(qi::_r1) > -qi::lit(".json") >
|
root_rule = BaseGrammar::query_rule(qi::_r1) > -qi::lit(".json") >
|
||||||
-('?' > (table_rule(qi::_r1) | BaseGrammar::base_rule(qi::_r1)) % '&');
|
-('?' > (table_rule(qi::_r1) | 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:
|
private:
|
||||||
qi::rule<Iterator, Signature> root_rule;
|
qi::rule<Iterator, Signature> root_rule;
|
||||||
qi::rule<Iterator, Signature> table_rule;
|
qi::rule<Iterator, Signature> table_rule;
|
||||||
qi::rule<Iterator, Signature> sources_rule;
|
qi::rule<Iterator, Signature> sources_rule;
|
||||||
qi::rule<Iterator, Signature> destinations_rule;
|
qi::rule<Iterator, Signature> destinations_rule;
|
||||||
qi::rule<Iterator, std::size_t()> size_t_;
|
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,33 +46,39 @@ class SharedDataIndex
|
|||||||
|
|
||||||
template <typename T> auto GetBlockPtr(const std::string &name) const
|
template <typename T> auto GetBlockPtr(const std::string &name) const
|
||||||
{
|
{
|
||||||
const auto index_iter = block_to_region.find(name);
|
const auto ®ion = GetBlockRegion(name);
|
||||||
const auto ®ion = regions[index_iter->second];
|
|
||||||
return region.layout.GetBlockPtr<T>(region.memory_ptr, name);
|
return region.layout.GetBlockPtr<T>(region.memory_ptr, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T> auto GetBlockPtr(const std::string &name)
|
template <typename T> auto GetBlockPtr(const std::string &name)
|
||||||
{
|
{
|
||||||
const auto index_iter = block_to_region.find(name);
|
const auto ®ion = GetBlockRegion(name);
|
||||||
const auto ®ion = regions[index_iter->second];
|
|
||||||
return region.layout.GetBlockPtr<T>(region.memory_ptr, name);
|
return region.layout.GetBlockPtr<T>(region.memory_ptr, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t GetBlockEntries(const std::string &name) const
|
std::size_t GetBlockEntries(const std::string &name) const
|
||||||
{
|
{
|
||||||
const auto index_iter = block_to_region.find(name);
|
const auto ®ion = GetBlockRegion(name);
|
||||||
const auto ®ion = regions[index_iter->second];
|
|
||||||
return region.layout.GetBlockEntries(name);
|
return region.layout.GetBlockEntries(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t GetBlockSize(const std::string &name) const
|
std::size_t GetBlockSize(const std::string &name) const
|
||||||
{
|
{
|
||||||
const auto index_iter = block_to_region.find(name);
|
const auto ®ion = GetBlockRegion(name);
|
||||||
const auto ®ion = regions[index_iter->second];
|
|
||||||
return region.layout.GetBlockSize(name);
|
return region.layout.GetBlockSize(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
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::vector<AllocatedRegion> regions;
|
||||||
std::unordered_map<std::string, std::uint32_t> block_to_region;
|
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 read(io::BufferReader &reader, DataLayout &layout);
|
||||||
|
|
||||||
inline void write(io::BufferWriter &writer, const DataLayout &layout);
|
inline void write(io::BufferWriter &writer, const DataLayout &layout);
|
||||||
}
|
} // namespace serialization
|
||||||
|
|
||||||
namespace detail
|
namespace detail
|
||||||
{
|
{
|
||||||
@@ -52,7 +52,7 @@ inline std::string trimName(const std::string &name_prefix, const std::string &n
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} // namespace detail
|
||||||
|
|
||||||
class DataLayout
|
class DataLayout
|
||||||
{
|
{
|
||||||
@@ -165,7 +165,7 @@ struct SharedRegion
|
|||||||
static constexpr const int MAX_NAME_LENGTH = 254;
|
static constexpr const int MAX_NAME_LENGTH = 254;
|
||||||
|
|
||||||
SharedRegion() : name{0}, timestamp{0} {}
|
SharedRegion() : name{0}, timestamp{0} {}
|
||||||
SharedRegion(const std::string &name_, std::uint64_t timestamp, std::uint8_t shm_key)
|
SharedRegion(const std::string &name_, std::uint64_t timestamp, std::uint16_t shm_key)
|
||||||
: name{0}, timestamp{timestamp}, shm_key{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);
|
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];
|
char name[MAX_NAME_LENGTH + 1];
|
||||||
std::uint64_t timestamp;
|
std::uint64_t timestamp;
|
||||||
std::uint8_t shm_key;
|
std::uint16_t shm_key;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Keeps a list of all shared regions in a fixed-sized struct
|
// Keeps a list of all shared regions in a fixed-sized struct
|
||||||
// for fast access and deserialization.
|
// for fast access and deserialization.
|
||||||
struct SharedRegionRegister
|
struct SharedRegionRegister
|
||||||
{
|
{
|
||||||
using RegionID = std::uint8_t;
|
using RegionID = std::uint16_t;
|
||||||
static constexpr const RegionID INVALID_REGION_ID = std::numeric_limits<RegionID>::max();
|
static constexpr const RegionID INVALID_REGION_ID = std::numeric_limits<RegionID>::max();
|
||||||
using ShmKey = decltype(SharedRegion::shm_key);
|
using ShmKey = decltype(SharedRegion::shm_key);
|
||||||
|
|
||||||
@@ -250,12 +250,11 @@ struct SharedRegionRegister
|
|||||||
|
|
||||||
void ReleaseKey(ShmKey key) { shm_key_in_use[key] = false; }
|
void ReleaseKey(ShmKey key) { shm_key_in_use[key] = false; }
|
||||||
|
|
||||||
static constexpr const std::uint8_t MAX_SHARED_REGIONS =
|
static constexpr const std::size_t MAX_SHARED_REGIONS = 512;
|
||||||
std::numeric_limits<RegionID>::max() - 1;
|
|
||||||
static_assert(MAX_SHARED_REGIONS < std::numeric_limits<RegionID>::max(),
|
static_assert(MAX_SHARED_REGIONS < std::numeric_limits<RegionID>::max(),
|
||||||
"Number of shared memory regions needs to be less than the region id size.");
|
"Number of shared memory regions needs to be less than the region id size.");
|
||||||
|
|
||||||
static constexpr const std::uint8_t MAX_SHM_KEYS = std::numeric_limits<std::uint8_t>::max() - 1;
|
static constexpr const std::size_t MAX_SHM_KEYS = MAX_SHARED_REGIONS * 2;
|
||||||
|
|
||||||
static constexpr const char *name = "osrm-region";
|
static constexpr const char *name = "osrm-region";
|
||||||
|
|
||||||
@@ -263,7 +262,7 @@ struct SharedRegionRegister
|
|||||||
std::array<SharedRegion, MAX_SHARED_REGIONS> regions;
|
std::array<SharedRegion, MAX_SHARED_REGIONS> regions;
|
||||||
std::array<bool, MAX_SHM_KEYS> shm_key_in_use;
|
std::array<bool, MAX_SHM_KEYS> shm_key_in_use;
|
||||||
};
|
};
|
||||||
}
|
} // namespace storage
|
||||||
}
|
} // namespace osrm
|
||||||
|
|
||||||
#endif /* SHARED_DATA_TYPE_HPP */
|
#endif /* SHARED_DATA_TYPE_HPP */
|
||||||
|
|||||||
@@ -34,10 +34,10 @@ namespace storage
|
|||||||
|
|
||||||
struct OSRMLockFile
|
struct OSRMLockFile
|
||||||
{
|
{
|
||||||
boost::filesystem::path operator()()
|
template <typename IdentifierT> boost::filesystem::path operator()(const IdentifierT &id)
|
||||||
{
|
{
|
||||||
boost::filesystem::path temp_dir = boost::filesystem::temp_directory_path();
|
boost::filesystem::path temp_dir = boost::filesystem::temp_directory_path();
|
||||||
boost::filesystem::path lock_file = temp_dir / "osrm.lock";
|
boost::filesystem::path lock_file = temp_dir / ("osrm-" + std::to_string(id) + ".lock");
|
||||||
return lock_file;
|
return lock_file;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -93,7 +93,7 @@ class SharedMemory
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
OSRMLockFile lock_file;
|
OSRMLockFile lock_file;
|
||||||
boost::interprocess::xsi_key key(lock_file().string().c_str(), id);
|
boost::interprocess::xsi_key key(lock_file(id).string().c_str(), id);
|
||||||
result = RegionExists(key);
|
result = RegionExists(key);
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
@@ -106,7 +106,7 @@ class SharedMemory
|
|||||||
template <typename IdentifierT> static bool Remove(const IdentifierT id)
|
template <typename IdentifierT> static bool Remove(const IdentifierT id)
|
||||||
{
|
{
|
||||||
OSRMLockFile lock_file;
|
OSRMLockFile lock_file;
|
||||||
boost::interprocess::xsi_key key(lock_file().string().c_str(), id);
|
boost::interprocess::xsi_key key(lock_file(id).string().c_str(), id);
|
||||||
return Remove(key);
|
return Remove(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,10 +287,11 @@ class SharedMemory
|
|||||||
template <typename IdentifierT, typename LockFileT = OSRMLockFile>
|
template <typename IdentifierT, typename LockFileT = OSRMLockFile>
|
||||||
std::unique_ptr<SharedMemory> makeSharedMemory(const IdentifierT &id, const uint64_t size = 0)
|
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
|
try
|
||||||
{
|
{
|
||||||
LockFileT lock_file;
|
LockFileT lock_file;
|
||||||
if (!boost::filesystem::exists(lock_file()))
|
if (!boost::filesystem::exists(lock_file(id)))
|
||||||
{
|
{
|
||||||
if (0 == size)
|
if (0 == size)
|
||||||
{
|
{
|
||||||
@@ -298,10 +299,10 @@ std::unique_ptr<SharedMemory> makeSharedMemory(const IdentifierT &id, const uint
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
boost::filesystem::ofstream ofs(lock_file());
|
boost::filesystem::ofstream ofs(lock_file(id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return std::make_unique<SharedMemory>(lock_file(), id, size);
|
return std::make_unique<SharedMemory>(lock_file(id), id, size);
|
||||||
}
|
}
|
||||||
catch (const boost::interprocess::interprocess_exception &e)
|
catch (const boost::interprocess::interprocess_exception &e)
|
||||||
{
|
{
|
||||||
@@ -310,7 +311,7 @@ std::unique_ptr<SharedMemory> makeSharedMemory(const IdentifierT &id, const uint
|
|||||||
throw util::exception(e.what() + SOURCE_REF);
|
throw util::exception(e.what() + SOURCE_REF);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} // namespace storage
|
||||||
}
|
} // namespace osrm
|
||||||
|
|
||||||
#endif // SHARED_MEMORY_HPP
|
#endif // SHARED_MEMORY_HPP
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ template <class Lock> class InvertedLock
|
|||||||
InvertedLock(Lock &lock) : lock(lock) { lock.unlock(); }
|
InvertedLock(Lock &lock) : lock(lock) { lock.unlock(); }
|
||||||
~InvertedLock() { lock.lock(); }
|
~InvertedLock() { lock.lock(); }
|
||||||
};
|
};
|
||||||
}
|
} // namespace
|
||||||
|
|
||||||
// The shared monitor implementation based on a semaphore and mutex
|
// The shared monitor implementation based on a semaphore and mutex
|
||||||
template <typename Data> struct SharedMonitor
|
template <typename Data> struct SharedMonitor
|
||||||
@@ -146,7 +146,9 @@ template <typename Data> struct SharedMonitor
|
|||||||
// like two-turnstile reusable barrier or boost/interprocess/sync/spin/condition.hpp
|
// like two-turnstile reusable barrier or boost/interprocess/sync/spin/condition.hpp
|
||||||
// fail if a waiter is killed.
|
// fail if a waiter is killed.
|
||||||
|
|
||||||
static constexpr int buffer_size = 256;
|
// Buffer size needs to be large enough to hold all the semaphores for every
|
||||||
|
// listener you want to support.
|
||||||
|
static constexpr int buffer_size = 4096 * 4;
|
||||||
|
|
||||||
struct InternalData
|
struct InternalData
|
||||||
{
|
{
|
||||||
@@ -232,8 +234,8 @@ template <typename Data> struct SharedMonitor
|
|||||||
bi::shared_memory_object shmem;
|
bi::shared_memory_object shmem;
|
||||||
bi::mapped_region region;
|
bi::mapped_region region;
|
||||||
};
|
};
|
||||||
}
|
} // namespace storage
|
||||||
}
|
} // namespace osrm
|
||||||
|
|
||||||
#undef USE_BOOST_INTERPROCESS_CONDITION
|
#undef USE_BOOST_INTERPROCESS_CONDITION
|
||||||
|
|
||||||
|
|||||||
@@ -330,9 +330,18 @@ inline auto make_multi_level_graph_view(const SharedDataIndex &index, const std:
|
|||||||
index, name + "/edge_array");
|
index, name + "/edge_array");
|
||||||
auto node_to_offset = make_vector_view<customizer::MultiLevelEdgeBasedGraphView::EdgeOffset>(
|
auto node_to_offset = make_vector_view<customizer::MultiLevelEdgeBasedGraphView::EdgeOffset>(
|
||||||
index, name + "/node_to_edge_offset");
|
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(
|
return customizer::MultiLevelEdgeBasedGraphView(std::move(node_list),
|
||||||
std::move(node_list), std::move(edge_list), std::move(node_to_offset));
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline auto make_maneuver_overrides_views(const SharedDataIndex &index, const std::string &name)
|
inline auto make_maneuver_overrides_views(const SharedDataIndex &index, const std::string &name)
|
||||||
|
|||||||
@@ -17,13 +17,15 @@ class Updater
|
|||||||
public:
|
public:
|
||||||
Updater(UpdaterConfig config_) : config(std::move(config_)) {}
|
Updater(UpdaterConfig config_) : config(std::move(config_)) {}
|
||||||
|
|
||||||
using NumNodesAndEdges =
|
EdgeID
|
||||||
std::tuple<EdgeID, std::vector<extractor::EdgeBasedEdge>, std::uint32_t>;
|
LoadAndUpdateEdgeExpandedGraph(std::vector<extractor::EdgeBasedEdge> &edge_based_edge_list,
|
||||||
NumNodesAndEdges LoadAndUpdateEdgeExpandedGraph() const;
|
std::vector<EdgeWeight> &node_weights,
|
||||||
|
std::uint32_t &connectivity_checksum) const;
|
||||||
|
|
||||||
EdgeID
|
EdgeID
|
||||||
LoadAndUpdateEdgeExpandedGraph(std::vector<extractor::EdgeBasedEdge> &edge_based_edge_list,
|
LoadAndUpdateEdgeExpandedGraph(std::vector<extractor::EdgeBasedEdge> &edge_based_edge_list,
|
||||||
std::vector<EdgeWeight> &node_weights,
|
std::vector<EdgeWeight> &node_weights,
|
||||||
|
std::vector<EdgeDuration> &node_durations, // TODO: to be deleted
|
||||||
std::uint32_t &connectivity_checksum) const;
|
std::uint32_t &connectivity_checksum) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -53,7 +53,8 @@ struct UpdaterConfig final : storage::IOConfig
|
|||||||
".osrm.geometry",
|
".osrm.geometry",
|
||||||
".osrm.fileIndex",
|
".osrm.fileIndex",
|
||||||
".osrm.properties",
|
".osrm.properties",
|
||||||
".osrm.restrictions"},
|
".osrm.restrictions",
|
||||||
|
".osrm.enw"},
|
||||||
{},
|
{},
|
||||||
{".osrm.datasource_names"}),
|
{".osrm.datasource_names"}),
|
||||||
valid_now(0)
|
valid_now(0)
|
||||||
|
|||||||
@@ -43,6 +43,9 @@ inline double radToDeg(const double radian)
|
|||||||
//! Takes the squared euclidean distance of the input coordinates. Does not return meters!
|
//! Takes the squared euclidean distance of the input coordinates. Does not return meters!
|
||||||
std::uint64_t squaredEuclideanDistance(const Coordinate lhs, const Coordinate rhs);
|
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 haversineDistance(const Coordinate first_coordinate, const Coordinate second_coordinate);
|
||||||
|
|
||||||
double greatCircleDistance(const Coordinate first_coordinate, const Coordinate second_coordinate);
|
double greatCircleDistance(const Coordinate first_coordinate, const Coordinate second_coordinate);
|
||||||
|
|||||||
+32
-12
@@ -1,9 +1,11 @@
|
|||||||
#ifndef OSRM_UTIL_DEBUG_HPP_
|
#ifndef OSRM_UTIL_DEBUG_HPP_
|
||||||
#define OSRM_UTIL_DEBUG_HPP_
|
#define OSRM_UTIL_DEBUG_HPP_
|
||||||
|
|
||||||
|
#include "extractor/edge_based_edge.hpp"
|
||||||
#include "extractor/node_data_container.hpp"
|
#include "extractor/node_data_container.hpp"
|
||||||
#include "extractor/query_node.hpp"
|
#include "extractor/query_node.hpp"
|
||||||
#include "guidance/intersection.hpp"
|
#include "guidance/intersection.hpp"
|
||||||
|
#include "guidance/turn_instruction.hpp"
|
||||||
#include "guidance/turn_lane_data.hpp"
|
#include "guidance/turn_lane_data.hpp"
|
||||||
#include "engine/guidance/route_step.hpp"
|
#include "engine/guidance/route_step.hpp"
|
||||||
#include "util/node_based_graph.hpp"
|
#include "util/node_based_graph.hpp"
|
||||||
@@ -22,10 +24,10 @@ namespace util
|
|||||||
inline std::ostream &operator<<(std::ostream &out, const Coordinate &coordinate)
|
inline std::ostream &operator<<(std::ostream &out, const Coordinate &coordinate)
|
||||||
{
|
{
|
||||||
out << std::setprecision(12) << "{" << toFloating(coordinate.lon) << ", "
|
out << std::setprecision(12) << "{" << toFloating(coordinate.lon) << ", "
|
||||||
<< toFloating(coordinate.lon) << "}";
|
<< toFloating(coordinate.lat) << "}";
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
}
|
} // namespace util
|
||||||
|
|
||||||
namespace engine
|
namespace engine
|
||||||
{
|
{
|
||||||
@@ -60,8 +62,8 @@ inline std::ostream &operator<<(std::ostream &out, const RouteStep &step)
|
|||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
}
|
} // namespace guidance
|
||||||
}
|
} // namespace engine
|
||||||
|
|
||||||
namespace guidance
|
namespace guidance
|
||||||
{
|
{
|
||||||
@@ -74,7 +76,7 @@ inline std::ostream &operator<<(std::ostream &out, const ConnectedRoad &road)
|
|||||||
<< static_cast<std::int32_t>(road.lane_data_id) << "}";
|
<< static_cast<std::int32_t>(road.lane_data_id) << "}";
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
}
|
} // namespace guidance
|
||||||
|
|
||||||
namespace extractor
|
namespace extractor
|
||||||
{
|
{
|
||||||
@@ -93,7 +95,7 @@ inline std::ostream &operator<<(std::ostream &out, const IntersectionViewData &v
|
|||||||
<< " angle: " << view.angle << " bearing: " << view.perceived_bearing << "}";
|
<< " angle: " << view.angle << " bearing: " << view.perceived_bearing << "}";
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
}
|
} // namespace intersection
|
||||||
|
|
||||||
namespace TurnLaneType
|
namespace TurnLaneType
|
||||||
{
|
{
|
||||||
@@ -123,9 +125,9 @@ inline std::ostream &operator<<(std::ostream &out, const Mask lane_type)
|
|||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
}
|
} // namespace TurnLaneType
|
||||||
}
|
} // namespace extractor
|
||||||
}
|
} // namespace osrm
|
||||||
|
|
||||||
namespace std
|
namespace std
|
||||||
{
|
{
|
||||||
@@ -145,7 +147,7 @@ inline std::ostream &operator<<(std::ostream &out,
|
|||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
}
|
} // namespace std
|
||||||
|
|
||||||
namespace osrm
|
namespace osrm
|
||||||
{
|
{
|
||||||
@@ -184,8 +186,26 @@ inline std::ostream &operator<<(std::ostream &out, const LaneDataVector &turn_la
|
|||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
}
|
} // namespace lanes
|
||||||
}
|
} // namespace guidance
|
||||||
|
|
||||||
|
namespace extractor
|
||||||
|
{
|
||||||
|
inline std::ostream &operator<<(std::ostream &out, const EdgeBasedEdge &edge)
|
||||||
|
{
|
||||||
|
out << " EdgeBasedEdge {";
|
||||||
|
out << " source " << edge.source << ", target: " << edge.target;
|
||||||
|
out << " EdgeBasedEdgeData data {";
|
||||||
|
out << " turn_id: " << edge.data.turn_id << ", weight: " << edge.data.weight;
|
||||||
|
out << " distance: " << edge.data.distance << ", duration: " << edge.data.duration;
|
||||||
|
out << " forward: " << (edge.data.forward == 0 ? "false" : "true")
|
||||||
|
<< ", backward: " << (edge.data.backward == 0 ? "false" : "true");
|
||||||
|
out << " }";
|
||||||
|
out << "}";
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace extractor
|
||||||
|
} // namespace osrm
|
||||||
|
|
||||||
#endif /*OSRM_ENGINE_GUIDANCE_DEBUG_HPP_*/
|
#endif /*OSRM_ENGINE_GUIDANCE_DEBUG_HPP_*/
|
||||||
|
|||||||
@@ -413,6 +413,11 @@ template <typename EdgeDataT> class DynamicGraph
|
|||||||
util::inplacePermutation(node_array.begin(), node_array.end(), old_to_new_node);
|
util::inplacePermutation(node_array.begin(), node_array.end(), old_to_new_node);
|
||||||
|
|
||||||
// Build up edge permutation
|
// 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;
|
EdgeID new_edge_index = 0;
|
||||||
std::vector<EdgeID> old_to_new_edge(edge_list.size(), SPECIAL_EDGEID);
|
std::vector<EdgeID> old_to_new_edge(edge_list.size(), SPECIAL_EDGEID);
|
||||||
for (auto node : util::irange<NodeID>(0, number_of_nodes))
|
for (auto node : util::irange<NodeID>(0, number_of_nodes))
|
||||||
@@ -421,11 +426,6 @@ template <typename EdgeDataT> class DynamicGraph
|
|||||||
// move all filled edges
|
// move all filled edges
|
||||||
for (auto edge : GetAdjacentEdgeRange(node))
|
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];
|
edge_list[edge].target = old_to_new_node[edge_list[edge].target];
|
||||||
BOOST_ASSERT(edge_list[edge].target != SPECIAL_NODEID);
|
BOOST_ASSERT(edge_list[edge].target != SPECIAL_NODEID);
|
||||||
old_to_new_edge[edge] = new_edge_index++;
|
old_to_new_edge[edge] = new_edge_index++;
|
||||||
|
|||||||
@@ -0,0 +1,517 @@
|
|||||||
|
#ifndef IEEE754_HPP
|
||||||
|
#define IEEE754_HPP
|
||||||
|
/**
|
||||||
|
Copyright (C) 2014 Milo Yip
|
||||||
|
|
||||||
|
Imported from:
|
||||||
|
https://github.com/miloyip/dtoa-benchmark/blob/c4020c62754950d38a1aaaed2975b05b441d1e7d/src/milo/dtoa_milo.h
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#include "msinttypes/stdint.h"
|
||||||
|
#include <intrin.h>
|
||||||
|
#else
|
||||||
|
#include <stdint.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define UINT64_C2(h, l) ((static_cast<uint64_t>(h) << 32) | static_cast<uint64_t>(l))
|
||||||
|
|
||||||
|
namespace osrm
|
||||||
|
{
|
||||||
|
namespace util
|
||||||
|
{
|
||||||
|
namespace ieee754
|
||||||
|
{
|
||||||
|
|
||||||
|
struct DiyFp
|
||||||
|
{
|
||||||
|
DiyFp() {}
|
||||||
|
|
||||||
|
DiyFp(uint64_t f, int e) : f(f), e(e) {}
|
||||||
|
|
||||||
|
DiyFp(double d)
|
||||||
|
{
|
||||||
|
union {
|
||||||
|
double d;
|
||||||
|
uint64_t u64;
|
||||||
|
} u = {d};
|
||||||
|
|
||||||
|
int biased_e = (u.u64 & kDpExponentMask) >> kDpSignificandSize;
|
||||||
|
uint64_t significand = (u.u64 & kDpSignificandMask);
|
||||||
|
if (biased_e != 0)
|
||||||
|
{
|
||||||
|
f = significand + kDpHiddenBit;
|
||||||
|
e = biased_e - kDpExponentBias;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
f = significand;
|
||||||
|
e = kDpMinExponent + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DiyFp operator-(const DiyFp &rhs) const
|
||||||
|
{
|
||||||
|
assert(e == rhs.e);
|
||||||
|
assert(f >= rhs.f);
|
||||||
|
return DiyFp(f - rhs.f, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
DiyFp operator*(const DiyFp &rhs) const
|
||||||
|
{
|
||||||
|
#if defined(_MSC_VER) && defined(_M_AMD64)
|
||||||
|
uint64_t h;
|
||||||
|
uint64_t l = _umul128(f, rhs.f, &h);
|
||||||
|
if (l & (uint64_t(1) << 63)) // rounding
|
||||||
|
h++;
|
||||||
|
return DiyFp(h, e + rhs.e + 64);
|
||||||
|
#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__)
|
||||||
|
unsigned __int128 p =
|
||||||
|
static_cast<unsigned __int128>(f) * static_cast<unsigned __int128>(rhs.f);
|
||||||
|
uint64_t h = p >> 64;
|
||||||
|
uint64_t l = static_cast<uint64_t>(p);
|
||||||
|
if (l & (uint64_t(1) << 63)) // rounding
|
||||||
|
h++;
|
||||||
|
return DiyFp(h, e + rhs.e + 64);
|
||||||
|
#else
|
||||||
|
const uint64_t M32 = 0xFFFFFFFF;
|
||||||
|
const uint64_t a = f >> 32;
|
||||||
|
const uint64_t b = f & M32;
|
||||||
|
const uint64_t c = rhs.f >> 32;
|
||||||
|
const uint64_t d = rhs.f & M32;
|
||||||
|
const uint64_t ac = a * c;
|
||||||
|
const uint64_t bc = b * c;
|
||||||
|
const uint64_t ad = a * d;
|
||||||
|
const uint64_t bd = b * d;
|
||||||
|
uint64_t tmp = (bd >> 32) + (ad & M32) + (bc & M32);
|
||||||
|
tmp += 1U << 31; /// mult_round
|
||||||
|
return DiyFp(ac + (ad >> 32) + (bc >> 32) + (tmp >> 32), e + rhs.e + 64);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
DiyFp Normalize() const
|
||||||
|
{
|
||||||
|
#if defined(_MSC_VER) && defined(_M_AMD64)
|
||||||
|
unsigned long index;
|
||||||
|
_BitScanReverse64(&index, f);
|
||||||
|
return DiyFp(f << (63 - index), e - (63 - index));
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
int s = __builtin_clzll(f);
|
||||||
|
return DiyFp(f << s, e - s);
|
||||||
|
#else
|
||||||
|
DiyFp res = *this;
|
||||||
|
while (!(res.f & kDpHiddenBit))
|
||||||
|
{
|
||||||
|
res.f <<= 1;
|
||||||
|
res.e--;
|
||||||
|
}
|
||||||
|
res.f <<= (kDiySignificandSize - kDpSignificandSize - 1);
|
||||||
|
res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 1);
|
||||||
|
return res;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
DiyFp NormalizeBoundary() const
|
||||||
|
{
|
||||||
|
#if defined(_MSC_VER) && defined(_M_AMD64)
|
||||||
|
unsigned long index;
|
||||||
|
_BitScanReverse64(&index, f);
|
||||||
|
return DiyFp(f << (63 - index), e - (63 - index));
|
||||||
|
#else
|
||||||
|
DiyFp res = *this;
|
||||||
|
while (!(res.f & (kDpHiddenBit << 1)))
|
||||||
|
{
|
||||||
|
res.f <<= 1;
|
||||||
|
res.e--;
|
||||||
|
}
|
||||||
|
res.f <<= (kDiySignificandSize - kDpSignificandSize - 2);
|
||||||
|
res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 2);
|
||||||
|
return res;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void NormalizedBoundaries(DiyFp *minus, DiyFp *plus) const
|
||||||
|
{
|
||||||
|
DiyFp pl = DiyFp((f << 1) + 1, e - 1).NormalizeBoundary();
|
||||||
|
DiyFp mi = (f == kDpHiddenBit) ? DiyFp((f << 2) - 1, e - 2) : DiyFp((f << 1) - 1, e - 1);
|
||||||
|
mi.f <<= mi.e - pl.e;
|
||||||
|
mi.e = pl.e;
|
||||||
|
*plus = pl;
|
||||||
|
*minus = mi;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const int kDiySignificandSize = 64;
|
||||||
|
static const int kDpSignificandSize = 52;
|
||||||
|
static const int kDpExponentBias = 0x3FF + kDpSignificandSize;
|
||||||
|
static const int kDpMinExponent = -kDpExponentBias;
|
||||||
|
static const uint64_t kDpExponentMask = UINT64_C2(0x7FF00000, 0x00000000);
|
||||||
|
static const uint64_t kDpSignificandMask = UINT64_C2(0x000FFFFF, 0xFFFFFFFF);
|
||||||
|
static const uint64_t kDpHiddenBit = UINT64_C2(0x00100000, 0x00000000);
|
||||||
|
|
||||||
|
uint64_t f;
|
||||||
|
int e;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline DiyFp GetCachedPower(int e, int *K)
|
||||||
|
{
|
||||||
|
// 10^-348, 10^-340, ..., 10^340
|
||||||
|
static const uint64_t kCachedPowers_F[] = {
|
||||||
|
UINT64_C2(0xfa8fd5a0, 0x081c0288), UINT64_C2(0xbaaee17f, 0xa23ebf76),
|
||||||
|
UINT64_C2(0x8b16fb20, 0x3055ac76), UINT64_C2(0xcf42894a, 0x5dce35ea),
|
||||||
|
UINT64_C2(0x9a6bb0aa, 0x55653b2d), UINT64_C2(0xe61acf03, 0x3d1a45df),
|
||||||
|
UINT64_C2(0xab70fe17, 0xc79ac6ca), UINT64_C2(0xff77b1fc, 0xbebcdc4f),
|
||||||
|
UINT64_C2(0xbe5691ef, 0x416bd60c), UINT64_C2(0x8dd01fad, 0x907ffc3c),
|
||||||
|
UINT64_C2(0xd3515c28, 0x31559a83), UINT64_C2(0x9d71ac8f, 0xada6c9b5),
|
||||||
|
UINT64_C2(0xea9c2277, 0x23ee8bcb), UINT64_C2(0xaecc4991, 0x4078536d),
|
||||||
|
UINT64_C2(0x823c1279, 0x5db6ce57), UINT64_C2(0xc2109436, 0x4dfb5637),
|
||||||
|
UINT64_C2(0x9096ea6f, 0x3848984f), UINT64_C2(0xd77485cb, 0x25823ac7),
|
||||||
|
UINT64_C2(0xa086cfcd, 0x97bf97f4), UINT64_C2(0xef340a98, 0x172aace5),
|
||||||
|
UINT64_C2(0xb23867fb, 0x2a35b28e), UINT64_C2(0x84c8d4df, 0xd2c63f3b),
|
||||||
|
UINT64_C2(0xc5dd4427, 0x1ad3cdba), UINT64_C2(0x936b9fce, 0xbb25c996),
|
||||||
|
UINT64_C2(0xdbac6c24, 0x7d62a584), UINT64_C2(0xa3ab6658, 0x0d5fdaf6),
|
||||||
|
UINT64_C2(0xf3e2f893, 0xdec3f126), UINT64_C2(0xb5b5ada8, 0xaaff80b8),
|
||||||
|
UINT64_C2(0x87625f05, 0x6c7c4a8b), UINT64_C2(0xc9bcff60, 0x34c13053),
|
||||||
|
UINT64_C2(0x964e858c, 0x91ba2655), UINT64_C2(0xdff97724, 0x70297ebd),
|
||||||
|
UINT64_C2(0xa6dfbd9f, 0xb8e5b88f), UINT64_C2(0xf8a95fcf, 0x88747d94),
|
||||||
|
UINT64_C2(0xb9447093, 0x8fa89bcf), UINT64_C2(0x8a08f0f8, 0xbf0f156b),
|
||||||
|
UINT64_C2(0xcdb02555, 0x653131b6), UINT64_C2(0x993fe2c6, 0xd07b7fac),
|
||||||
|
UINT64_C2(0xe45c10c4, 0x2a2b3b06), UINT64_C2(0xaa242499, 0x697392d3),
|
||||||
|
UINT64_C2(0xfd87b5f2, 0x8300ca0e), UINT64_C2(0xbce50864, 0x92111aeb),
|
||||||
|
UINT64_C2(0x8cbccc09, 0x6f5088cc), UINT64_C2(0xd1b71758, 0xe219652c),
|
||||||
|
UINT64_C2(0x9c400000, 0x00000000), UINT64_C2(0xe8d4a510, 0x00000000),
|
||||||
|
UINT64_C2(0xad78ebc5, 0xac620000), UINT64_C2(0x813f3978, 0xf8940984),
|
||||||
|
UINT64_C2(0xc097ce7b, 0xc90715b3), UINT64_C2(0x8f7e32ce, 0x7bea5c70),
|
||||||
|
UINT64_C2(0xd5d238a4, 0xabe98068), UINT64_C2(0x9f4f2726, 0x179a2245),
|
||||||
|
UINT64_C2(0xed63a231, 0xd4c4fb27), UINT64_C2(0xb0de6538, 0x8cc8ada8),
|
||||||
|
UINT64_C2(0x83c7088e, 0x1aab65db), UINT64_C2(0xc45d1df9, 0x42711d9a),
|
||||||
|
UINT64_C2(0x924d692c, 0xa61be758), UINT64_C2(0xda01ee64, 0x1a708dea),
|
||||||
|
UINT64_C2(0xa26da399, 0x9aef774a), UINT64_C2(0xf209787b, 0xb47d6b85),
|
||||||
|
UINT64_C2(0xb454e4a1, 0x79dd1877), UINT64_C2(0x865b8692, 0x5b9bc5c2),
|
||||||
|
UINT64_C2(0xc83553c5, 0xc8965d3d), UINT64_C2(0x952ab45c, 0xfa97a0b3),
|
||||||
|
UINT64_C2(0xde469fbd, 0x99a05fe3), UINT64_C2(0xa59bc234, 0xdb398c25),
|
||||||
|
UINT64_C2(0xf6c69a72, 0xa3989f5c), UINT64_C2(0xb7dcbf53, 0x54e9bece),
|
||||||
|
UINT64_C2(0x88fcf317, 0xf22241e2), UINT64_C2(0xcc20ce9b, 0xd35c78a5),
|
||||||
|
UINT64_C2(0x98165af3, 0x7b2153df), UINT64_C2(0xe2a0b5dc, 0x971f303a),
|
||||||
|
UINT64_C2(0xa8d9d153, 0x5ce3b396), UINT64_C2(0xfb9b7cd9, 0xa4a7443c),
|
||||||
|
UINT64_C2(0xbb764c4c, 0xa7a44410), UINT64_C2(0x8bab8eef, 0xb6409c1a),
|
||||||
|
UINT64_C2(0xd01fef10, 0xa657842c), UINT64_C2(0x9b10a4e5, 0xe9913129),
|
||||||
|
UINT64_C2(0xe7109bfb, 0xa19c0c9d), UINT64_C2(0xac2820d9, 0x623bf429),
|
||||||
|
UINT64_C2(0x80444b5e, 0x7aa7cf85), UINT64_C2(0xbf21e440, 0x03acdd2d),
|
||||||
|
UINT64_C2(0x8e679c2f, 0x5e44ff8f), UINT64_C2(0xd433179d, 0x9c8cb841),
|
||||||
|
UINT64_C2(0x9e19db92, 0xb4e31ba9), UINT64_C2(0xeb96bf6e, 0xbadf77d9),
|
||||||
|
UINT64_C2(0xaf87023b, 0x9bf0ee6b)};
|
||||||
|
static const int16_t kCachedPowers_E[] = {
|
||||||
|
-1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954, -927, -901,
|
||||||
|
-874, -847, -821, -794, -768, -741, -715, -688, -661, -635, -608, -582, -555,
|
||||||
|
-529, -502, -475, -449, -422, -396, -369, -343, -316, -289, -263, -236, -210,
|
||||||
|
-183, -157, -130, -103, -77, -50, -24, 3, 30, 56, 83, 109, 136,
|
||||||
|
162, 189, 216, 242, 269, 295, 322, 348, 375, 402, 428, 455, 481,
|
||||||
|
508, 534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800, 827,
|
||||||
|
853, 880, 907, 933, 960, 986, 1013, 1039, 1066};
|
||||||
|
|
||||||
|
// int k = static_cast<int>(ceil((-61 - e) * 0.30102999566398114)) + 374;
|
||||||
|
double dk =
|
||||||
|
(-61 - e) * 0.30102999566398114 + 347; // dk must be positive, so can do ceiling in positive
|
||||||
|
int k = static_cast<int>(dk);
|
||||||
|
if (k != dk)
|
||||||
|
k++;
|
||||||
|
|
||||||
|
unsigned index = static_cast<unsigned>((k >> 3) + 1);
|
||||||
|
*K = -(-348 + static_cast<int>(index << 3)); // decimal exponent no need lookup table
|
||||||
|
|
||||||
|
assert(index < sizeof(kCachedPowers_F) / sizeof(kCachedPowers_F[0]));
|
||||||
|
return DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void
|
||||||
|
GrisuRound(char *buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w)
|
||||||
|
{
|
||||||
|
while (rest < wp_w && delta - rest >= ten_kappa && (rest + ten_kappa < wp_w || /// closer
|
||||||
|
wp_w - rest > rest + ten_kappa - wp_w))
|
||||||
|
{
|
||||||
|
buffer[len - 1]--;
|
||||||
|
rest += ten_kappa;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unsigned CountDecimalDigit32(uint32_t n)
|
||||||
|
{
|
||||||
|
// Simple pure C++ implementation was faster than __builtin_clz version in this situation.
|
||||||
|
if (n < 10)
|
||||||
|
return 1;
|
||||||
|
if (n < 100)
|
||||||
|
return 2;
|
||||||
|
if (n < 1000)
|
||||||
|
return 3;
|
||||||
|
if (n < 10000)
|
||||||
|
return 4;
|
||||||
|
if (n < 100000)
|
||||||
|
return 5;
|
||||||
|
if (n < 1000000)
|
||||||
|
return 6;
|
||||||
|
if (n < 10000000)
|
||||||
|
return 7;
|
||||||
|
if (n < 100000000)
|
||||||
|
return 8;
|
||||||
|
if (n < 1000000000)
|
||||||
|
return 9;
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void
|
||||||
|
DigitGen(const DiyFp &W, const DiyFp &Mp, uint64_t delta, char *buffer, int *len, int *K)
|
||||||
|
{
|
||||||
|
static const uint32_t kPow10[] = {
|
||||||
|
1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000};
|
||||||
|
const DiyFp one(uint64_t(1) << -Mp.e, Mp.e);
|
||||||
|
const DiyFp wp_w = Mp - W;
|
||||||
|
uint32_t p1 = static_cast<uint32_t>(Mp.f >> -one.e);
|
||||||
|
uint64_t p2 = Mp.f & (one.f - 1);
|
||||||
|
int kappa = static_cast<int>(CountDecimalDigit32(p1));
|
||||||
|
*len = 0;
|
||||||
|
|
||||||
|
while (kappa > 0)
|
||||||
|
{
|
||||||
|
uint32_t d;
|
||||||
|
switch (kappa)
|
||||||
|
{
|
||||||
|
case 10:
|
||||||
|
d = p1 / 1000000000;
|
||||||
|
p1 %= 1000000000;
|
||||||
|
break;
|
||||||
|
case 9:
|
||||||
|
d = p1 / 100000000;
|
||||||
|
p1 %= 100000000;
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
d = p1 / 10000000;
|
||||||
|
p1 %= 10000000;
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
d = p1 / 1000000;
|
||||||
|
p1 %= 1000000;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
d = p1 / 100000;
|
||||||
|
p1 %= 100000;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
d = p1 / 10000;
|
||||||
|
p1 %= 10000;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
d = p1 / 1000;
|
||||||
|
p1 %= 1000;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
d = p1 / 100;
|
||||||
|
p1 %= 100;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
d = p1 / 10;
|
||||||
|
p1 %= 10;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
d = p1;
|
||||||
|
p1 = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
__assume(0);
|
||||||
|
#elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
|
||||||
|
__builtin_unreachable();
|
||||||
|
#else
|
||||||
|
d = 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
if (d || *len)
|
||||||
|
buffer[(*len)++] = '0' + static_cast<char>(d);
|
||||||
|
kappa--;
|
||||||
|
uint64_t tmp = (static_cast<uint64_t>(p1) << -one.e) + p2;
|
||||||
|
if (tmp <= delta)
|
||||||
|
{
|
||||||
|
*K += kappa;
|
||||||
|
GrisuRound(
|
||||||
|
buffer, *len, delta, tmp, static_cast<uint64_t>(kPow10[kappa]) << -one.e, wp_w.f);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// kappa = 0
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
p2 *= 10;
|
||||||
|
delta *= 10;
|
||||||
|
char d = static_cast<char>(p2 >> -one.e);
|
||||||
|
if (d || *len)
|
||||||
|
buffer[(*len)++] = '0' + d;
|
||||||
|
p2 &= one.f - 1;
|
||||||
|
kappa--;
|
||||||
|
if (p2 < delta)
|
||||||
|
{
|
||||||
|
*K += kappa;
|
||||||
|
GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * kPow10[-kappa]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void Grisu2(double value, char *buffer, int *length, int *K)
|
||||||
|
{
|
||||||
|
const DiyFp v(value);
|
||||||
|
DiyFp w_m, w_p;
|
||||||
|
v.NormalizedBoundaries(&w_m, &w_p);
|
||||||
|
|
||||||
|
const DiyFp c_mk = GetCachedPower(w_p.e, K);
|
||||||
|
const DiyFp W = v.Normalize() * c_mk;
|
||||||
|
DiyFp Wp = w_p * c_mk;
|
||||||
|
DiyFp Wm = w_m * c_mk;
|
||||||
|
Wm.f++;
|
||||||
|
Wp.f--;
|
||||||
|
DigitGen(W, Wp, Wp.f - Wm.f, buffer, length, K);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const char *GetDigitsLut()
|
||||||
|
{
|
||||||
|
static const char cDigitsLut[200] = {
|
||||||
|
'0', '0', '0', '1', '0', '2', '0', '3', '0', '4', '0', '5', '0', '6', '0', '7', '0',
|
||||||
|
'8', '0', '9', '1', '0', '1', '1', '1', '2', '1', '3', '1', '4', '1', '5', '1', '6',
|
||||||
|
'1', '7', '1', '8', '1', '9', '2', '0', '2', '1', '2', '2', '2', '3', '2', '4', '2',
|
||||||
|
'5', '2', '6', '2', '7', '2', '8', '2', '9', '3', '0', '3', '1', '3', '2', '3', '3',
|
||||||
|
'3', '4', '3', '5', '3', '6', '3', '7', '3', '8', '3', '9', '4', '0', '4', '1', '4',
|
||||||
|
'2', '4', '3', '4', '4', '4', '5', '4', '6', '4', '7', '4', '8', '4', '9', '5', '0',
|
||||||
|
'5', '1', '5', '2', '5', '3', '5', '4', '5', '5', '5', '6', '5', '7', '5', '8', '5',
|
||||||
|
'9', '6', '0', '6', '1', '6', '2', '6', '3', '6', '4', '6', '5', '6', '6', '6', '7',
|
||||||
|
'6', '8', '6', '9', '7', '0', '7', '1', '7', '2', '7', '3', '7', '4', '7', '5', '7',
|
||||||
|
'6', '7', '7', '7', '8', '7', '9', '8', '0', '8', '1', '8', '2', '8', '3', '8', '4',
|
||||||
|
'8', '5', '8', '6', '8', '7', '8', '8', '8', '9', '9', '0', '9', '1', '9', '2', '9',
|
||||||
|
'3', '9', '4', '9', '5', '9', '6', '9', '7', '9', '8', '9', '9'};
|
||||||
|
return cDigitsLut;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void WriteExponent(int K, char *buffer)
|
||||||
|
{
|
||||||
|
if (K < 0)
|
||||||
|
{
|
||||||
|
*buffer++ = '-';
|
||||||
|
K = -K;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (K >= 100)
|
||||||
|
{
|
||||||
|
*buffer++ = '0' + static_cast<char>(K / 100);
|
||||||
|
K %= 100;
|
||||||
|
const char *d = GetDigitsLut() + K * 2;
|
||||||
|
*buffer++ = d[0];
|
||||||
|
*buffer++ = d[1];
|
||||||
|
}
|
||||||
|
else if (K >= 10)
|
||||||
|
{
|
||||||
|
const char *d = GetDigitsLut() + K * 2;
|
||||||
|
*buffer++ = d[0];
|
||||||
|
*buffer++ = d[1];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
*buffer++ = '0' + static_cast<char>(K);
|
||||||
|
|
||||||
|
*buffer = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void Prettify(char *buffer, int length, int k)
|
||||||
|
{
|
||||||
|
const int kk = length + k; // 10^(kk-1) <= v < 10^kk
|
||||||
|
|
||||||
|
if (length <= kk && kk <= 21)
|
||||||
|
{
|
||||||
|
// 1234e7 -> 12340000000
|
||||||
|
for (int i = length; i < kk; i++)
|
||||||
|
buffer[i] = '0';
|
||||||
|
buffer[kk] = '.';
|
||||||
|
buffer[kk + 1] = '0';
|
||||||
|
buffer[kk + 2] = '\0';
|
||||||
|
}
|
||||||
|
else if (0 < kk && kk <= 21)
|
||||||
|
{
|
||||||
|
// 1234e-2 -> 12.34
|
||||||
|
memmove(&buffer[kk + 1], &buffer[kk], length - kk);
|
||||||
|
buffer[kk] = '.';
|
||||||
|
buffer[length + 1] = '\0';
|
||||||
|
}
|
||||||
|
else if (-6 < kk && kk <= 0)
|
||||||
|
{
|
||||||
|
// 1234e-6 -> 0.001234
|
||||||
|
const int offset = 2 - kk;
|
||||||
|
memmove(&buffer[offset], &buffer[0], length);
|
||||||
|
buffer[0] = '0';
|
||||||
|
buffer[1] = '.';
|
||||||
|
for (int i = 2; i < offset; i++)
|
||||||
|
buffer[i] = '0';
|
||||||
|
buffer[length + offset] = '\0';
|
||||||
|
}
|
||||||
|
else if (length == 1)
|
||||||
|
{
|
||||||
|
// 1e30
|
||||||
|
buffer[1] = 'e';
|
||||||
|
WriteExponent(kk - 1, &buffer[2]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 1234e30 -> 1.234e33
|
||||||
|
memmove(&buffer[2], &buffer[1], length - 1);
|
||||||
|
buffer[1] = '.';
|
||||||
|
buffer[length + 1] = 'e';
|
||||||
|
WriteExponent(kk - 1, &buffer[0 + length + 2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void dtoa_milo(double value, char *buffer)
|
||||||
|
{
|
||||||
|
// Not handling NaN and inf
|
||||||
|
assert(!isnan(value));
|
||||||
|
assert(!isinf(value));
|
||||||
|
|
||||||
|
if (value == 0)
|
||||||
|
{
|
||||||
|
buffer[0] = '0';
|
||||||
|
buffer[1] = '.';
|
||||||
|
buffer[2] = '0';
|
||||||
|
buffer[3] = '\0';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (value < 0)
|
||||||
|
{
|
||||||
|
*buffer++ = '-';
|
||||||
|
value = -value;
|
||||||
|
}
|
||||||
|
int length, K;
|
||||||
|
Grisu2(value, buffer, &length, &K);
|
||||||
|
Prettify(buffer, length, K);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace ieee754
|
||||||
|
} // namespace util
|
||||||
|
} // namespace osrm
|
||||||
|
|
||||||
|
#endif // IEEE754_HPP
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
#define JSON_RENDERER_HPP
|
#define JSON_RENDERER_HPP
|
||||||
|
|
||||||
#include "util/cast.hpp"
|
#include "util/cast.hpp"
|
||||||
|
#include "util/ieee754.hpp"
|
||||||
#include "util/string_util.hpp"
|
#include "util/string_util.hpp"
|
||||||
|
|
||||||
#include "osrm/json_container.hpp"
|
#include "osrm/json_container.hpp"
|
||||||
@@ -21,6 +22,11 @@ namespace util
|
|||||||
namespace json
|
namespace json
|
||||||
{
|
{
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
constexpr int MAX_FLOAT_STRING_LENGTH = 256;
|
||||||
|
}
|
||||||
|
|
||||||
struct Renderer
|
struct Renderer
|
||||||
{
|
{
|
||||||
explicit Renderer(std::ostream &_out) : out(_out) {}
|
explicit Renderer(std::ostream &_out) : out(_out) {}
|
||||||
@@ -34,8 +40,31 @@ struct Renderer
|
|||||||
|
|
||||||
void operator()(const Number &number) const
|
void operator()(const Number &number) const
|
||||||
{
|
{
|
||||||
out.precision(10);
|
char buffer[MAX_FLOAT_STRING_LENGTH] = {'\0'};
|
||||||
out << number.value;
|
ieee754::dtoa_milo(number.value, buffer);
|
||||||
|
|
||||||
|
// Trucate to 10 decimal places
|
||||||
|
int pos = 0;
|
||||||
|
int decimalpos = 0;
|
||||||
|
while (decimalpos == 0 && pos < MAX_FLOAT_STRING_LENGTH && buffer[pos] != 0)
|
||||||
|
{
|
||||||
|
if (buffer[pos] == '.')
|
||||||
|
{
|
||||||
|
decimalpos = pos;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
while (pos < MAX_FLOAT_STRING_LENGTH && buffer[pos] != 0)
|
||||||
|
{
|
||||||
|
if (pos - decimalpos == 10)
|
||||||
|
{
|
||||||
|
buffer[pos] = '\0';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
out << buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator()(const Object &object) const
|
void operator()(const Object &object) const
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#include <tbb/parallel_sort.h>
|
#include <tbb/parallel_sort.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
@@ -20,24 +21,27 @@ namespace util
|
|||||||
struct NodeBasedEdgeData
|
struct NodeBasedEdgeData
|
||||||
{
|
{
|
||||||
NodeBasedEdgeData()
|
NodeBasedEdgeData()
|
||||||
: weight(INVALID_EDGE_WEIGHT), duration(INVALID_EDGE_WEIGHT), geometry_id({0, false}),
|
: weight(INVALID_EDGE_WEIGHT), duration(INVALID_EDGE_WEIGHT),
|
||||||
reversed(false), annotation_data(-1)
|
distance(INVALID_EDGE_DISTANCE), geometry_id({0, false}), reversed(false),
|
||||||
|
annotation_data(-1)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeBasedEdgeData(EdgeWeight weight,
|
NodeBasedEdgeData(EdgeWeight weight,
|
||||||
EdgeWeight duration,
|
EdgeWeight duration,
|
||||||
|
EdgeDistance distance,
|
||||||
GeometryID geometry_id,
|
GeometryID geometry_id,
|
||||||
bool reversed,
|
bool reversed,
|
||||||
extractor::NodeBasedEdgeClassification flags,
|
extractor::NodeBasedEdgeClassification flags,
|
||||||
AnnotationID annotation_data)
|
AnnotationID annotation_data)
|
||||||
: weight(weight), duration(duration), geometry_id(geometry_id), reversed(reversed),
|
: weight(weight), duration(duration), distance(distance), geometry_id(geometry_id),
|
||||||
flags(flags), annotation_data(annotation_data)
|
reversed(reversed), flags(flags), annotation_data(annotation_data)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
EdgeWeight weight;
|
EdgeWeight weight;
|
||||||
EdgeWeight duration;
|
EdgeWeight duration;
|
||||||
|
EdgeDistance distance;
|
||||||
GeometryID geometry_id;
|
GeometryID geometry_id;
|
||||||
bool reversed : 1;
|
bool reversed : 1;
|
||||||
extractor::NodeBasedEdgeClassification flags;
|
extractor::NodeBasedEdgeClassification flags;
|
||||||
@@ -80,18 +84,24 @@ NodeBasedDynamicGraphFromEdges(NodeID number_of_nodes,
|
|||||||
const extractor::NodeBasedEdge &input_edge) {
|
const extractor::NodeBasedEdge &input_edge) {
|
||||||
output_edge.data.weight = input_edge.weight;
|
output_edge.data.weight = input_edge.weight;
|
||||||
output_edge.data.duration = input_edge.duration;
|
output_edge.data.duration = input_edge.duration;
|
||||||
|
output_edge.data.distance = input_edge.distance;
|
||||||
output_edge.data.flags = input_edge.flags;
|
output_edge.data.flags = input_edge.flags;
|
||||||
output_edge.data.annotation_data = input_edge.annotation_data;
|
output_edge.data.annotation_data = input_edge.annotation_data;
|
||||||
|
|
||||||
BOOST_ASSERT(output_edge.data.weight > 0);
|
BOOST_ASSERT(output_edge.data.weight >= 0);
|
||||||
BOOST_ASSERT(output_edge.data.duration > 0);
|
BOOST_ASSERT(output_edge.data.duration >= 0);
|
||||||
|
if (output_edge.data.distance <= 0)
|
||||||
|
{
|
||||||
|
std::cout << "output_edge.data.distance " << output_edge.data.distance << std::endl;
|
||||||
|
}
|
||||||
|
BOOST_ASSERT(output_edge.data.distance >= 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
tbb::parallel_sort(edges_list.begin(), edges_list.end());
|
tbb::parallel_sort(edges_list.begin(), edges_list.end());
|
||||||
|
|
||||||
return NodeBasedDynamicGraph(number_of_nodes, edges_list);
|
return NodeBasedDynamicGraph(number_of_nodes, edges_list);
|
||||||
}
|
}
|
||||||
}
|
} // namespace util
|
||||||
}
|
} // namespace osrm
|
||||||
|
|
||||||
#endif // NODE_BASED_GRAPH_HPP
|
#endif // NODE_BASED_GRAPH_HPP
|
||||||
|
|||||||
@@ -226,12 +226,14 @@ class QueryHeap
|
|||||||
Data &GetData(NodeID node)
|
Data &GetData(NodeID node)
|
||||||
{
|
{
|
||||||
const auto index = node_index.peek_index(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;
|
return inserted_nodes[index].data;
|
||||||
}
|
}
|
||||||
|
|
||||||
Data const &GetData(NodeID node) const
|
Data const &GetData(NodeID node) const
|
||||||
{
|
{
|
||||||
const auto index = node_index.peek_index(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;
|
return inserted_nodes[index].data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -312,7 +312,7 @@ class StaticGraph
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// private:
|
protected:
|
||||||
NodeIterator number_of_nodes;
|
NodeIterator number_of_nodes;
|
||||||
EdgeIterator number_of_edges;
|
EdgeIterator number_of_edges;
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ struct osm_way_id
|
|||||||
struct duplicated_node
|
struct duplicated_node
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
}
|
} // namespace tag
|
||||||
using OSMNodeID = osrm::Alias<std::uint64_t, tag::osm_node_id>;
|
using OSMNodeID = osrm::Alias<std::uint64_t, tag::osm_node_id>;
|
||||||
static_assert(std::is_pod<OSMNodeID>(), "OSMNodeID is not a valid alias");
|
static_assert(std::is_pod<OSMNodeID>(), "OSMNodeID is not a valid alias");
|
||||||
using OSMWayID = osrm::Alias<std::uint64_t, tag::osm_way_id>;
|
using OSMWayID = osrm::Alias<std::uint64_t, tag::osm_way_id>;
|
||||||
@@ -75,6 +75,7 @@ using NameID = std::uint32_t;
|
|||||||
using AnnotationID = std::uint32_t;
|
using AnnotationID = std::uint32_t;
|
||||||
using EdgeWeight = std::int32_t;
|
using EdgeWeight = std::int32_t;
|
||||||
using EdgeDuration = std::int32_t;
|
using EdgeDuration = std::int32_t;
|
||||||
|
using EdgeDistance = float;
|
||||||
using SegmentWeight = std::uint32_t;
|
using SegmentWeight = std::uint32_t;
|
||||||
using SegmentDuration = std::uint32_t;
|
using SegmentDuration = std::uint32_t;
|
||||||
using TurnPenalty = std::int16_t; // turn penalty in 100ms units
|
using TurnPenalty = std::int16_t; // turn penalty in 100ms units
|
||||||
@@ -112,7 +113,9 @@ static const SegmentWeight MAX_SEGMENT_WEIGHT = INVALID_SEGMENT_WEIGHT - 1;
|
|||||||
static const SegmentDuration MAX_SEGMENT_DURATION = INVALID_SEGMENT_DURATION - 1;
|
static const SegmentDuration MAX_SEGMENT_DURATION = INVALID_SEGMENT_DURATION - 1;
|
||||||
static const EdgeWeight INVALID_EDGE_WEIGHT = std::numeric_limits<EdgeWeight>::max();
|
static const EdgeWeight INVALID_EDGE_WEIGHT = std::numeric_limits<EdgeWeight>::max();
|
||||||
static const EdgeDuration MAXIMAL_EDGE_DURATION = std::numeric_limits<EdgeDuration>::max();
|
static const EdgeDuration MAXIMAL_EDGE_DURATION = std::numeric_limits<EdgeDuration>::max();
|
||||||
|
static const EdgeDistance MAXIMAL_EDGE_DISTANCE = std::numeric_limits<EdgeDistance>::max();
|
||||||
static const TurnPenalty INVALID_TURN_PENALTY = std::numeric_limits<TurnPenalty>::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
|
// 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
|
// within the code base. For now we have to ensure that we don't case 30 bit to -1 and break any
|
||||||
|
|||||||
@@ -9,31 +9,6 @@ namespace util
|
|||||||
{
|
{
|
||||||
namespace vector_tile
|
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
|
// Vector tiles are 4096 virtual pixels on each side
|
||||||
const constexpr double EXTENT = 4096.0;
|
const constexpr double EXTENT = 4096.0;
|
||||||
const constexpr double BUFFER = 128.0;
|
const constexpr double BUFFER = 128.0;
|
||||||
|
|||||||
+8
-2
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "osrm",
|
"name": "osrm",
|
||||||
"version": "5.17.3",
|
"version": "5.20.0-alpha.2",
|
||||||
"private": false,
|
"private": false,
|
||||||
"description": "The Open Source Routing Machine is a high performance routing engine written in C++14 designed to run on OpenStreetMap data.",
|
"description": "The Open Source Routing Machine is a high performance routing engine written in C++14 designed to run on OpenStreetMap data.",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "node ./node_modules/eslint/bin/eslint.js -c ./.eslintrc features/step_definitions/ features/support/",
|
"lint": "node ./node_modules/eslint/bin/eslint.js -c ./.eslintrc features/step_definitions/ features/support/",
|
||||||
"test": "npm run lint && node ./node_modules/cucumber/bin/cucumber.js features/ -p verify && node ./node_modules/cucumber/bin/cucumber.js features/ -p mld",
|
"test": "npm run lint && node ./node_modules/cucumber/bin/cucumber.js features/ -p verify",
|
||||||
"clean": "rm -rf test/cache",
|
"clean": "rm -rf test/cache",
|
||||||
"docs": "./scripts/build_api_docs.sh",
|
"docs": "./scripts/build_api_docs.sh",
|
||||||
"install": "node-pre-gyp install --fallback-to-build=false || ./scripts/node_install.sh",
|
"install": "node-pre-gyp install --fallback-to-build=false || ./scripts/node_install.sh",
|
||||||
@@ -38,19 +38,25 @@
|
|||||||
"node": ">=4.0.0"
|
"node": ">=4.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"ansi-escape-sequences": "^4.0.0",
|
||||||
"aws-sdk": "~2.0.31",
|
"aws-sdk": "~2.0.31",
|
||||||
"babel-plugin-transform-class-properties": "^6.24.1",
|
"babel-plugin-transform-class-properties": "^6.24.1",
|
||||||
"chalk": "^1.1.3",
|
"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",
|
"cucumber": "^1.2.1",
|
||||||
"d3-queue": "^2.0.3",
|
"d3-queue": "^2.0.3",
|
||||||
"docbox": "^1.0.6",
|
"docbox": "^1.0.6",
|
||||||
"documentation": "^4.0.0-rc.1",
|
"documentation": "^4.0.0-rc.1",
|
||||||
"eslint": "^2.4.0",
|
"eslint": "^2.4.0",
|
||||||
"faucet": "^0.0.1",
|
"faucet": "^0.0.1",
|
||||||
|
"jsonpath": "^1.0.0",
|
||||||
"node-timeout": "0.0.4",
|
"node-timeout": "0.0.4",
|
||||||
"polyline": "^0.2.0",
|
"polyline": "^0.2.0",
|
||||||
"request": "^2.69.0",
|
"request": "^2.69.0",
|
||||||
"tape": "^4.7.0",
|
"tape": "^4.7.0",
|
||||||
|
"turf": "^3.0.14",
|
||||||
"xmlbuilder": "^4.2.1"
|
"xmlbuilder": "^4.2.1"
|
||||||
},
|
},
|
||||||
"bundleDependencies": [
|
"bundleDependencies": [
|
||||||
|
|||||||
+18
-15
@@ -38,20 +38,10 @@ function setup()
|
|||||||
mode.pushing_bike
|
mode.pushing_bike
|
||||||
},
|
},
|
||||||
|
|
||||||
barrier_whitelist = Set {
|
barrier_blacklist = Set {
|
||||||
'sump_buster',
|
'yes',
|
||||||
'bus_trap',
|
'wall',
|
||||||
'cycle_barrier',
|
'fence'
|
||||||
'bollard',
|
|
||||||
'entrance',
|
|
||||||
'cattle_grid',
|
|
||||||
'border_control',
|
|
||||||
'toll_booth',
|
|
||||||
'sally_port',
|
|
||||||
'gate',
|
|
||||||
'lift_gate',
|
|
||||||
'no',
|
|
||||||
'block'
|
|
||||||
},
|
},
|
||||||
|
|
||||||
access_tag_whitelist = Set {
|
access_tag_whitelist = Set {
|
||||||
@@ -193,6 +183,16 @@ function setup()
|
|||||||
sett = 10
|
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 = {
|
tracktype_speeds = {
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -235,7 +235,7 @@ function process_node(profile, node, result)
|
|||||||
else
|
else
|
||||||
local barrier = node:get_value_by_key("barrier")
|
local barrier = node:get_value_by_key("barrier")
|
||||||
if barrier and "" ~= barrier then
|
if barrier and "" ~= barrier then
|
||||||
if not profile.barrier_whitelist[barrier] then
|
if profile.barrier_blacklist[barrier] then
|
||||||
result.barrier = true
|
result.barrier = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -650,6 +650,9 @@ function process_way(profile, way, result)
|
|||||||
-- set name, ref and pronunciation
|
-- set name, ref and pronunciation
|
||||||
WayHandlers.names,
|
WayHandlers.names,
|
||||||
|
|
||||||
|
-- set classes
|
||||||
|
WayHandlers.classes,
|
||||||
|
|
||||||
-- set weight properties of the way
|
-- set weight properties of the way
|
||||||
WayHandlers.weights
|
WayHandlers.weights
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-3
@@ -42,6 +42,10 @@ function setup()
|
|||||||
vehicle_height = 2.5, -- in meters, 2.5m is the height of van
|
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
|
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
|
-- a list of suffixes to suppress in name change instructions. The suffixes also include common substrings of each other
|
||||||
suffix_list = {
|
suffix_list = {
|
||||||
'N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW', 'North', 'South', 'West', 'East', 'Nor', 'Sou', 'We', 'Ea'
|
'N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW', 'North', 'South', 'West', 'East', 'Nor', 'Sou', 'We', 'Ea'
|
||||||
@@ -116,9 +120,6 @@ function setup()
|
|||||||
|
|
||||||
-- classes to support for exclude flags
|
-- classes to support for exclude flags
|
||||||
excludable = Sequence {
|
excludable = Sequence {
|
||||||
Set {'toll'},
|
|
||||||
Set {'motorway'},
|
|
||||||
Set {'ferry'}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
avoid = Set {
|
avoid = Set {
|
||||||
@@ -276,6 +277,7 @@ function setup()
|
|||||||
["de:rural"] = 100,
|
["de:rural"] = 100,
|
||||||
["de:motorway"] = 0,
|
["de:motorway"] = 0,
|
||||||
["dk:rural"] = 80,
|
["dk:rural"] = 80,
|
||||||
|
["fr:rural"] = 80,
|
||||||
["gb:nsl_single"] = (60*1609)/1000,
|
["gb:nsl_single"] = (60*1609)/1000,
|
||||||
["gb:nsl_dual"] = (70*1609)/1000,
|
["gb:nsl_dual"] = (70*1609)/1000,
|
||||||
["gb:motorway"] = (70*1609)/1000,
|
["gb:motorway"] = (70*1609)/1000,
|
||||||
@@ -387,6 +389,8 @@ function process_way(profile, way, result, relations)
|
|||||||
WayHandlers.avoid_ways,
|
WayHandlers.avoid_ways,
|
||||||
WayHandlers.handle_height,
|
WayHandlers.handle_height,
|
||||||
WayHandlers.handle_width,
|
WayHandlers.handle_width,
|
||||||
|
WayHandlers.handle_length,
|
||||||
|
WayHandlers.handle_weight,
|
||||||
|
|
||||||
-- determine access status by checking our hierarchy of
|
-- determine access status by checking our hierarchy of
|
||||||
-- access tags, e.g: motorcar, motor_vehicle, vehicle
|
-- access tags, e.g: motorcar, motor_vehicle, vehicle
|
||||||
|
|||||||
+5
-14
@@ -24,19 +24,10 @@ function setup()
|
|||||||
default_speed = walking_speed,
|
default_speed = walking_speed,
|
||||||
oneway_handling = 'specific', -- respect 'oneway:foot' but not 'oneway'
|
oneway_handling = 'specific', -- respect 'oneway:foot' but not 'oneway'
|
||||||
|
|
||||||
barrier_whitelist = Set {
|
barrier_blacklist = Set {
|
||||||
'cycle_barrier',
|
'yes',
|
||||||
'bollard',
|
'wall',
|
||||||
'entrance',
|
'fence'
|
||||||
'cattle_grid',
|
|
||||||
'border_control',
|
|
||||||
'toll_booth',
|
|
||||||
'sally_port',
|
|
||||||
'gate',
|
|
||||||
'lift_gate',
|
|
||||||
'no',
|
|
||||||
'kerb',
|
|
||||||
'block'
|
|
||||||
},
|
},
|
||||||
|
|
||||||
access_tag_whitelist = Set {
|
access_tag_whitelist = Set {
|
||||||
@@ -157,7 +148,7 @@ function process_node(profile, node, result)
|
|||||||
local bollard = node:get_value_by_key("bollard")
|
local bollard = node:get_value_by_key("bollard")
|
||||||
local rising_bollard = bollard and "rising" == bollard
|
local rising_bollard = bollard and "rising" == bollard
|
||||||
|
|
||||||
if not profile.barrier_whitelist[barrier] and not rising_bollard then
|
if profile.barrier_blacklist[barrier] and not rising_bollard then
|
||||||
result.barrier = true
|
result.barrier = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
+20
-26
@@ -5,6 +5,7 @@ Measure = {}
|
|||||||
-- measurements conversion constants
|
-- measurements conversion constants
|
||||||
local inch_to_meters = 0.0254
|
local inch_to_meters = 0.0254
|
||||||
local feet_to_inches = 12
|
local feet_to_inches = 12
|
||||||
|
local pound_to_kilograms = 0.45359237
|
||||||
|
|
||||||
--- Parse string as a height in meters.
|
--- Parse string as a height in meters.
|
||||||
--- according to http://wiki.openstreetmap.org/wiki/Key:maxheight
|
--- according to http://wiki.openstreetmap.org/wiki/Key:maxheight
|
||||||
@@ -25,33 +26,19 @@ function Measure.parse_value_meters(value)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- according to http://wiki.openstreetmap.org/wiki/Map_Features/Units#Explicit_specifications
|
--- Parse weight value in kilograms.
|
||||||
local tonns_parse_patterns = Sequence {
|
--- according to https://wiki.openstreetmap.org/wiki/Key:maxweight
|
||||||
"%d+",
|
|
||||||
"%d+.%d+",
|
|
||||||
"%d+.%d+ ?t"
|
|
||||||
}
|
|
||||||
|
|
||||||
local kg_parse_patterns = Sequence {
|
|
||||||
"%d+ ?kg"
|
|
||||||
}
|
|
||||||
|
|
||||||
--- Parse weight value in kilograms
|
|
||||||
function Measure.parse_value_kilograms(value)
|
function Measure.parse_value_kilograms(value)
|
||||||
-- try to parse kilograms
|
local n = tonumber(value:gsub(",", "."):match("%d+%.?%d*"))
|
||||||
for i, templ in ipairs(kg_parse_patterns) do
|
if n then
|
||||||
m = string.match(value, templ)
|
if string.match(value, "lbs") then
|
||||||
if m then
|
n = n * pound_to_kilograms
|
||||||
return tonumber(m)
|
elseif string.match(value, "kg") then
|
||||||
end
|
-- n = n
|
||||||
end
|
else -- Default, metric tons
|
||||||
|
n = n * 1000
|
||||||
-- try to parse tonns
|
|
||||||
for i, templ in ipairs(tonns_parse_patterns) do
|
|
||||||
m = string.match(value, templ)
|
|
||||||
if m then
|
|
||||||
return tonumber(m) * 1000
|
|
||||||
end
|
end
|
||||||
|
return n
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -83,7 +70,14 @@ function Measure.get_max_width(raw_value)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get maxweight of specified way in kilogramms
|
--- 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.
|
||||||
function Measure.get_max_weight(raw_value)
|
function Measure.get_max_weight(raw_value)
|
||||||
if raw_value then
|
if raw_value then
|
||||||
return Measure.parse_value_kilograms(raw_value)
|
return Measure.parse_value_kilograms(raw_value)
|
||||||
|
|||||||
@@ -306,37 +306,46 @@ end
|
|||||||
|
|
||||||
-- add class information
|
-- add class information
|
||||||
function WayHandlers.classes(profile,way,result,data)
|
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_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 forward_route, backward_route = Tags.get_forward_backward_by_key(way, data, "route")
|
||||||
local tunnel = way:get_value_by_key("tunnel")
|
local tunnel = way:get_value_by_key("tunnel")
|
||||||
|
|
||||||
if tunnel and tunnel ~= "no" then
|
if allowed_classes["tunnel"] and tunnel and tunnel ~= "no" then
|
||||||
result.forward_classes["tunnel"] = true
|
result.forward_classes["tunnel"] = true
|
||||||
result.backward_classes["tunnel"] = true
|
result.backward_classes["tunnel"] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
if forward_toll == "yes" then
|
if allowed_classes["toll"] and forward_toll == "yes" then
|
||||||
result.forward_classes["toll"] = true
|
result.forward_classes["toll"] = true
|
||||||
end
|
end
|
||||||
if backward_toll == "yes" then
|
if allowed_classes["toll"] and backward_toll == "yes" then
|
||||||
result.backward_classes["toll"] = true
|
result.backward_classes["toll"] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
if forward_route == "ferry" then
|
if allowed_classes["ferry"] and forward_route == "ferry" then
|
||||||
result.forward_classes["ferry"] = true
|
result.forward_classes["ferry"] = true
|
||||||
end
|
end
|
||||||
if backward_route == "ferry" then
|
if allowed_classes["ferry"] and backward_route == "ferry" then
|
||||||
result.backward_classes["ferry"] = true
|
result.backward_classes["ferry"] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
if result.forward_restricted then
|
if allowed_classes["restricted"] and result.forward_restricted then
|
||||||
result.forward_classes["restricted"] = true
|
result.forward_classes["restricted"] = true
|
||||||
end
|
end
|
||||||
if result.backward_restricted then
|
if allowed_classes["restricted"] and result.backward_restricted then
|
||||||
result.backward_classes["restricted"] = true
|
result.backward_classes["restricted"] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
if data.highway == "motorway" or data.highway == "motorway_link" then
|
if allowed_classes["motorway"] and (data.highway == "motorway" or data.highway == "motorway_link") then
|
||||||
result.forward_classes["motorway"] = true
|
result.forward_classes["motorway"] = true
|
||||||
result.backward_classes["motorway"] = true
|
result.backward_classes["motorway"] = true
|
||||||
end
|
end
|
||||||
@@ -502,6 +511,38 @@ function WayHandlers.handle_width(profile,way,result,data)
|
|||||||
end
|
end
|
||||||
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
|
-- handle oneways tags
|
||||||
function WayHandlers.oneway(profile,way,result,data)
|
function WayHandlers.oneway(profile,way,result,data)
|
||||||
if not profile.oneway_handling then
|
if not profile.oneway_handling then
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
-- Secondary road: 18km/h = 18000m/3600s = 100m/20s
|
-- Secondary road: 18km/h = 18000m/3600s = 100m/20s
|
||||||
-- Tertiary road: 12km/h = 12000m/3600s = 100m/30s
|
-- Tertiary road: 12km/h = 12000m/3600s = 100m/30s
|
||||||
|
|
||||||
api_version = 3
|
api_version = 4
|
||||||
|
|
||||||
function setup()
|
function setup()
|
||||||
return {
|
return {
|
||||||
@@ -14,7 +14,7 @@ function setup()
|
|||||||
max_speed_for_map_matching = 30/3.6, --km -> m/s
|
max_speed_for_map_matching = 30/3.6, --km -> m/s
|
||||||
weight_name = 'duration',
|
weight_name = 'duration',
|
||||||
process_call_tagless_node = false,
|
process_call_tagless_node = false,
|
||||||
uturn_penalty = 20,
|
u_turn_penalty = 20,
|
||||||
traffic_light_penalty = 7, -- seconds
|
traffic_light_penalty = 7, -- seconds
|
||||||
use_turn_restrictions = true
|
use_turn_restrictions = true
|
||||||
},
|
},
|
||||||
@@ -32,7 +32,7 @@ function setup()
|
|||||||
primary = 36,
|
primary = 36,
|
||||||
secondary = 18,
|
secondary = 18,
|
||||||
tertiary = 12,
|
tertiary = 12,
|
||||||
steps = 6,
|
steps = 6
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
@@ -128,9 +128,9 @@ function process_way (profile, way, result)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function process_turn (profile, turn)
|
function process_turn (profile, turn)
|
||||||
if turn.direction_modifier == direction_modifier.uturn then
|
if turn.is_u_turn then
|
||||||
turn.duration = profile.properties.uturn_penalty
|
turn.duration = turn.duration + profile.properties.u_turn_penalty
|
||||||
turn.weight = profile.properties.uturn_penalty
|
turn.weight = turn.weight + profile.properties.u_turn_penalty
|
||||||
end
|
end
|
||||||
if turn.has_traffic_light then
|
if turn.has_traffic_light then
|
||||||
turn.duration = turn.duration + profile.properties.traffic_light_penalty
|
turn.duration = turn.duration + profile.properties.traffic_light_penalty
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ lonlat = lambda x: (coord2float(x['lon']['__value']), coord2float(x['lat']['__va
|
|||||||
|
|
||||||
def call(this, method, *args):
|
def call(this, method, *args):
|
||||||
"""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)))
|
command = '(*({})({})).{}({})'.format(this.type.target().pointer(), this.address, method, ','.join((str(x) for x in args)))
|
||||||
return gdb.parse_and_eval(command)
|
return gdb.parse_and_eval(command)
|
||||||
|
|
||||||
@@ -234,7 +236,6 @@ class SVGPrinter (gdb.Command):
|
|||||||
mld_facade = facade.cast(gdb.lookup_type('osrm::engine::datafacade::ContiguousInternalMemoryAlgorithmDataFacade<osrm::engine::routing_algorithms::mld::Algorithm>'))
|
mld_facade = facade.cast(gdb.lookup_type('osrm::engine::datafacade::ContiguousInternalMemoryAlgorithmDataFacade<osrm::engine::routing_algorithms::mld::Algorithm>'))
|
||||||
mld_partition = mld_facade['mld_partition']
|
mld_partition = mld_facade['mld_partition']
|
||||||
mld_levels = call(mld_partition, 'GetNumberOfLevels')
|
mld_levels = call(mld_partition, 'GetNumberOfLevels')
|
||||||
print (mld_level, mld_levels)
|
|
||||||
if mld_level < mld_levels:
|
if mld_level < mld_levels:
|
||||||
sentinel_node = call(mld_partition['partition'], 'size') - 1 # GetSentinelNode
|
sentinel_node = call(mld_partition['partition'], 'size') - 1 # GetSentinelNode
|
||||||
number_of_cells = call(mld_partition, 'GetCell', mld_level, sentinel_node) # GetNumberOfCells
|
number_of_cells = call(mld_partition, 'GetCell', mld_level, sentinel_node) # GetNumberOfCells
|
||||||
@@ -272,6 +273,7 @@ class SVGPrinter (gdb.Command):
|
|||||||
for node in nodes:
|
for node in nodes:
|
||||||
geometry_id = call(facade, 'GetGeometryIndex', node)
|
geometry_id = call(facade, 'GetGeometryIndex', node)
|
||||||
direction = 'forward' if geometry_id['forward'] else 'reverse'
|
direction = 'forward' if geometry_id['forward'] else 'reverse'
|
||||||
|
print (geometry_id, direction)
|
||||||
geometry = SVGPrinter.getByGeometryId(facade, geometry_id, 'Geometry')
|
geometry = SVGPrinter.getByGeometryId(facade, geometry_id, 'Geometry')
|
||||||
weights = SVGPrinter.getByGeometryId(facade, geometry_id, 'Weights')
|
weights = SVGPrinter.getByGeometryId(facade, geometry_id, 'Weights')
|
||||||
|
|
||||||
|
|||||||
+60
-45
@@ -10,13 +10,12 @@ const clu = require('command-line-usage');
|
|||||||
const ansi = require('ansi-escape-sequences');
|
const ansi = require('ansi-escape-sequences');
|
||||||
const turf = require('turf');
|
const turf = require('turf');
|
||||||
const jp = require('jsonpath');
|
const jp = require('jsonpath');
|
||||||
|
const csv_stringify = require('csv-stringify/lib/sync');
|
||||||
|
|
||||||
const run_query = (query_options, filters, callback) => {
|
const run_query = (query_options, filters, callback) => {
|
||||||
let tic = () => 0.;
|
let tic = () => 0.;
|
||||||
http.request(query_options, function (res) {
|
http.request(query_options, function (res) {
|
||||||
let body = '', ttfb = tic();
|
let body = '', ttfb = tic();
|
||||||
if (res.statusCode != 200)
|
|
||||||
return callback(query_options.path, res.statusCode, ttfb);
|
|
||||||
|
|
||||||
res.setEncoding('utf8');
|
res.setEncoding('utf8');
|
||||||
res.on('data', function (chunk) {
|
res.on('data', function (chunk) {
|
||||||
@@ -35,27 +34,51 @@ const run_query = (query_options, filters, callback) => {
|
|||||||
}).end();
|
}).end();
|
||||||
};
|
};
|
||||||
|
|
||||||
function generate_points(polygon, number) {
|
function generate_points(polygon, number, coordinates_number, max_distance) {
|
||||||
let query_points = [];
|
let query_points = [];
|
||||||
while (query_points.length < number) {
|
while (query_points.length < number) {
|
||||||
var chunk = turf
|
let points = [];
|
||||||
.random('points', number, { bbox: turf.bbox(polygon)})
|
|
||||||
.features
|
while(points.length < coordinates_number) {
|
||||||
.map(x => x.geometry.coordinates)
|
let chunk = turf
|
||||||
.filter(pt => turf.inside(pt, polygon));
|
.random('points', coordinates_number, { bbox: turf.bbox(polygon)})
|
||||||
query_points = query_points.concat(chunk);
|
.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);
|
||||||
}
|
}
|
||||||
return query_points.slice(0, number);
|
|
||||||
|
return query_points;
|
||||||
}
|
}
|
||||||
|
|
||||||
function generate_queries(options, query_points, coordinates_number) {
|
function generate_queries(options, query_points) {
|
||||||
let queries = [];
|
let queries = query_points.map(points => {
|
||||||
for (let chunk = 0; chunk < query_points.length; chunk += coordinates_number)
|
return options.path.replace(/{}/g, x => points.pop().join(','));
|
||||||
{
|
});
|
||||||
let points = query_points.slice(chunk, chunk + coordinates_number);
|
|
||||||
let query = options.path.replace(/{}/g, x => points.pop().join(','));
|
|
||||||
queries.push(query);
|
|
||||||
}
|
|
||||||
return queries;
|
return queries;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,27 +96,22 @@ function BoundingBox(x) {
|
|||||||
}
|
}
|
||||||
const optionsList = [
|
const optionsList = [
|
||||||
{name: 'help', alias: 'h', type: Boolean, description: 'Display this usage guide.', defaultValue: false},
|
{name: 'help', alias: 'h', type: Boolean, description: 'Display this usage guide.', defaultValue: false},
|
||||||
{name: 'server', alias: 's', type: ServerDetails, defaultValue: ServerDetails('localhost:5000'),
|
{name: 'server', alias: 's', type: ServerDetails, defaultValue: ServerDetails('localhost:5000'), description: 'OSRM routing server', typeLabel: '{underline hostname[:port]}'},
|
||||||
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: 'path', alias: 'p', type: String, defaultValue: '/route/v1/driving/{};{}',
|
{name: 'filter', alias: 'f', type: String, defaultValue: ['$.routes[0].weight'], multiple: true, description: 'JSONPath filters, default "$.routes[0].weight"', typeLabel: '{underline filter}'},
|
||||||
description: 'OSRM query path with {} coordinate placeholders, default /route/v1/driving/{};{}', typeLabel: '[underline]{path}'},
|
{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: 'filter', alias: 'f', type: String, defaultValue: ['$.routes[0].weight'], multiple: true,
|
{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}'},
|
||||||
description: 'JSONPath filters, default "$.routes[0].weight"', typeLabel: '[underline]{filter}'},
|
{name: 'number', alias: 'n', type: Number, defaultValue: 10, description: 'number of query points, default 10', typeLabel: '{underline number}'},
|
||||||
{name: 'bounding-box', alias: 'b', type: BoundingBox, defaultValue: BoundingBox('5.86442,47.2654,15.0508,55.1478'), multiple: true,
|
{name: 'max-distance', alias: 'd', type: Number, defaultValue: -1, description: 'maximal distance between coordinates', typeLabel: '{underline number}'},
|
||||||
description: 'queries bounding box, default "5.86442,47.2654,15.0508,55.1478"', typeLabel: '[underline]{west,south,east,north}'},
|
{name: 'queries-files', alias: 'q', type: String, description: 'CSV file with queries in the first row', typeLabel: '{underline file}'},
|
||||||
{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);
|
const options = cla(optionsList);
|
||||||
if (options.help) {
|
if (options.help) {
|
||||||
const banner =
|
const banner =`\
|
||||||
String.raw` ____ _______ __ ___ ___ __ ___ ___ _________ ` + '\n' +
|
____ _______ __ ______ __ ___ ___ _________
|
||||||
String.raw` / __ \/ __/ _ \/ |/ / / _ \/ / / / |/ / |/ / __/ _ \ ` + '\n' +
|
/ __ \\\\/ __/ _ \\\\/ |/ / _ \\\\/ / / / |/ / |/ / __/ _ \\\\
|
||||||
String.raw`/ /_/ /\ \/ , _/ /|_/ / / , _/ /_/ / / / _// , _/ ` + '\n' +
|
/ /_/ /\\\\ \\\\/ , _/ /|_/ / , _/ /_/ / / / _// , _/
|
||||||
String.raw`\____/___/_/|_/_/ /_/ /_/|_|\____/_/|_/_/|_/___/_/|_| `;
|
\\\\____/___/_/|_/_/ /_/_/|_|\\\\____/_/|_/_/|_/___/_/|_|`;
|
||||||
const usage = clu([
|
const usage = clu([
|
||||||
{ content: ansi.format(banner, 'green'), raw: true },
|
{ content: ansi.format(banner, 'green'), raw: true },
|
||||||
{ header: 'Run OSRM queries and collect results'/*, content: 'Generates something [italic]{very} important.'*/ },
|
{ header: 'Run OSRM queries and collect results'/*, content: 'Generates something [italic]{very} important.'*/ },
|
||||||
@@ -114,8 +132,8 @@ if (options.hasOwnProperty('queries-files')) {
|
|||||||
} else {
|
} else {
|
||||||
const polygon = options['bounding-box'].map(x => x.poly).reduce((x,y) => turf.union(x, y));
|
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 coordinates_number = (options.path.match(/{}/g) || []).length;
|
||||||
const query_points = generate_points(polygon, coordinates_number * options.number);
|
const query_points = generate_points(polygon, options.number, coordinates_number, options['max-distance']);
|
||||||
queries = generate_queries(options, query_points, coordinates_number);
|
queries = generate_queries(options, query_points);
|
||||||
}
|
}
|
||||||
queries = queries.map(q => { return {hostname: options.server.hostname, port: options.server.port, path: q}; });
|
queries = queries.map(q => { return {hostname: options.server.hostname, port: options.server.port, path: q}; });
|
||||||
|
|
||||||
@@ -123,11 +141,8 @@ queries = queries.map(q => { return {hostname: options.server.hostname, port: op
|
|||||||
http.globalAgent.maxSockets = options['max-sockets'];
|
http.globalAgent.maxSockets = options['max-sockets'];
|
||||||
queries.map(query => {
|
queries.map(query => {
|
||||||
run_query(query, options.filter, (query, code, ttfb, total, results) => {
|
run_query(query, options.filter, (query, code, ttfb, total, results) => {
|
||||||
let str = `"${query}",${code}`;
|
let data = results ? JSON.stringify(results[0]) : '';
|
||||||
if (ttfb !== undefined) str += `,${ttfb}`;
|
let record = [[query, code, ttfb, total, data]];
|
||||||
if (total !== undefined) str += `,${total}`;
|
process.stdout.write(csv_stringify(record));
|
||||||
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,79 +9,50 @@ set -o nounset
|
|||||||
# structure will be lost.
|
# structure will be lost.
|
||||||
# http://git.661346.n2.nabble.com/subtree-merges-lose-prefix-after-rebase-td7332850.html
|
# http://git.661346.n2.nabble.com/subtree-merges-lose-prefix-after-rebase-td7332850.html
|
||||||
|
|
||||||
OSMIUM_REPO="https://github.com/osmcode/libosmium.git"
|
OSMIUM_PATH="osmcode/libosmium"
|
||||||
OSMIUM_TAG=v2.13.1
|
OSMIUM_TAG=v2.14.0
|
||||||
|
|
||||||
VARIANT_REPO="https://github.com/mapbox/variant.git"
|
VARIANT_PATH="mapbox/variant"
|
||||||
VARIANT_TAG=v1.1.3
|
VARIANT_TAG=v1.1.3
|
||||||
|
|
||||||
SOL_REPO="https://github.com/ThePhD/sol2.git"
|
SOL_PATH="ThePhD/sol2"
|
||||||
SOL_TAG=v2.17.5
|
SOL_TAG=v2.17.5
|
||||||
|
|
||||||
RAPIDJSON_REPO="https://github.com/miloyip/rapidjson.git"
|
RAPIDJSON_PATH="Tencent/rapidjson"
|
||||||
RAPIDJSON_TAG=v1.1.0
|
RAPIDJSON_TAG=v1.1.0
|
||||||
|
|
||||||
MICROTAR_REPO="https://github.com/rxi/microtar"
|
MICROTAR_PATH="rxi/microtar"
|
||||||
MICROTAR_TAG=v0.1.0
|
MICROTAR_TAG=v0.1.0
|
||||||
|
|
||||||
VARIANT_LATEST=$(curl "https://api.github.com/repos/mapbox/variant/releases/latest" | jq ".tag_name")
|
PROTOZERO_PATH="mapbox/protozero"
|
||||||
OSMIUM_LATEST=$(curl "https://api.github.com/repos/osmcode/libosmium/releases/latest" | jq ".tag_name")
|
PROTOZERO_TAG=v1.6.2
|
||||||
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")
|
|
||||||
|
|
||||||
echo "Latest osmium release is $OSMIUM_LATEST, pulling in \"$OSMIUM_TAG\""
|
VTZERO_PATH="mapbox/vtzero"
|
||||||
echo "Latest variant release is $VARIANT_LATEST, pulling in \"$VARIANT_TAG\""
|
VTZERO_TAG=v1.0.1
|
||||||
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\""
|
|
||||||
|
|
||||||
read -p "Update osmium (y/n) " ok
|
function update_subtree () {
|
||||||
if [[ $ok =~ [yY] ]]
|
name=${1^^}
|
||||||
then
|
path=$(tmpvar=${name}_PATH && echo ${!tmpvar})
|
||||||
if [ -d "third_party/libosmium" ]; then
|
tag=$(tmpvar=${name}_TAG && echo ${!tmpvar})
|
||||||
git subtree pull -P third_party/libosmium/ $OSMIUM_REPO $OSMIUM_TAG --squash
|
dir=$(basename $path)
|
||||||
else
|
repo="https://github.com/${path}.git"
|
||||||
git subtree add -P third_party/libosmium/ $OSMIUM_REPO $OSMIUM_TAG --squash
|
latest=$(curl -s "https://api.github.com/repos/${path}/releases/latest" | jq ".tag_name")
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
read -p "Update variant (y/n) " ok
|
echo "Latest $1 release is ${latest}, pulling in \"${tag}\""
|
||||||
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 sol2 (y/n) " ok
|
read -p "Update ${1} (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
|
|
||||||
|
|
||||||
read -p "Update rapidjson (y/n) " ok
|
if [[ $ok =~ [yY] ]]
|
||||||
if [[ $ok =~ [yY] ]]
|
then
|
||||||
then
|
if [ -d "third_party/$dir" ]; then
|
||||||
if [ -d "third_party/rapidjson" ]; then
|
git subtree pull -P third_party/$dir ${repo} ${tag} --squash
|
||||||
git subtree pull -P third_party/rapidjson/ $RAPIDJSON_REPO $RAPIDJSON_TAG --squash
|
else
|
||||||
else
|
git subtree add -P third_party/$dir ${repo} ${tag} --squash
|
||||||
git subtree add -P third_party/rapidjson/ $RAPIDJSON_REPO $RAPIDJSON_TAG --squash
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
}
|
||||||
|
|
||||||
read -p "Update microtar (y/n) " ok
|
## Update dependencies
|
||||||
if [[ $ok =~ [yY] ]]
|
for dep in osmium variant sol rapidjson microtar protozero vtzero ; do
|
||||||
then
|
update_subtree $dep
|
||||||
if [ -d "third_party/microtar" ]; then
|
done
|
||||||
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,6 +75,12 @@ int Contractor::Run()
|
|||||||
EdgeID number_of_edge_based_nodes = updater.LoadAndUpdateEdgeExpandedGraph(
|
EdgeID number_of_edge_based_nodes = updater.LoadAndUpdateEdgeExpandedGraph(
|
||||||
edge_based_edge_list, node_weights, connectivity_checksum);
|
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
|
// Contracting the edge-expanded graph
|
||||||
|
|
||||||
TIMER_START(contraction);
|
TIMER_START(contraction);
|
||||||
@@ -96,6 +102,7 @@ int Contractor::Run()
|
|||||||
QueryGraph query_graph;
|
QueryGraph query_graph;
|
||||||
std::vector<std::vector<bool>> edge_filters;
|
std::vector<std::vector<bool>> edge_filters;
|
||||||
std::vector<std::vector<bool>> cores;
|
std::vector<std::vector<bool>> cores;
|
||||||
|
|
||||||
std::tie(query_graph, edge_filters) = contractExcludableGraph(
|
std::tie(query_graph, edge_filters) = contractExcludableGraph(
|
||||||
toContractorGraph(number_of_edge_based_nodes, std::move(edge_based_edge_list)),
|
toContractorGraph(number_of_edge_based_nodes, std::move(edge_based_edge_list)),
|
||||||
std::move(node_weights),
|
std::move(node_weights),
|
||||||
|
|||||||
@@ -215,6 +215,7 @@ void ContractNode(ContractorThreadData *data,
|
|||||||
target,
|
target,
|
||||||
path_weight,
|
path_weight,
|
||||||
in_data.duration + out_data.duration,
|
in_data.duration + out_data.duration,
|
||||||
|
in_data.distance + out_data.distance,
|
||||||
out_data.originalEdges + in_data.originalEdges,
|
out_data.originalEdges + in_data.originalEdges,
|
||||||
node,
|
node,
|
||||||
SHORTCUT_ARC,
|
SHORTCUT_ARC,
|
||||||
@@ -225,6 +226,7 @@ void ContractNode(ContractorThreadData *data,
|
|||||||
source,
|
source,
|
||||||
path_weight,
|
path_weight,
|
||||||
in_data.duration + out_data.duration,
|
in_data.duration + out_data.duration,
|
||||||
|
in_data.distance + out_data.distance,
|
||||||
out_data.originalEdges + in_data.originalEdges,
|
out_data.originalEdges + in_data.originalEdges,
|
||||||
node,
|
node,
|
||||||
SHORTCUT_ARC,
|
SHORTCUT_ARC,
|
||||||
@@ -280,6 +282,7 @@ void ContractNode(ContractorThreadData *data,
|
|||||||
target,
|
target,
|
||||||
path_weight,
|
path_weight,
|
||||||
in_data.duration + out_data.duration,
|
in_data.duration + out_data.duration,
|
||||||
|
in_data.distance + out_data.distance,
|
||||||
out_data.originalEdges + in_data.originalEdges,
|
out_data.originalEdges + in_data.originalEdges,
|
||||||
node,
|
node,
|
||||||
SHORTCUT_ARC,
|
SHORTCUT_ARC,
|
||||||
@@ -290,6 +293,7 @@ void ContractNode(ContractorThreadData *data,
|
|||||||
source,
|
source,
|
||||||
path_weight,
|
path_weight,
|
||||||
in_data.duration + out_data.duration,
|
in_data.duration + out_data.duration,
|
||||||
|
in_data.distance + out_data.distance,
|
||||||
out_data.originalEdges + in_data.originalEdges,
|
out_data.originalEdges + in_data.originalEdges,
|
||||||
node,
|
node,
|
||||||
SHORTCUT_ARC,
|
SHORTCUT_ARC,
|
||||||
@@ -556,7 +560,7 @@ bool IsNodeIndependent(const util::XORFastHash<> &hash,
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
} // namespace
|
||||||
|
|
||||||
std::vector<bool> contractGraph(ContractorGraph &graph,
|
std::vector<bool> contractGraph(ContractorGraph &graph,
|
||||||
std::vector<bool> node_is_uncontracted_,
|
std::vector<bool> node_is_uncontracted_,
|
||||||
|
|||||||
@@ -74,24 +74,28 @@ void printUnreachableStatistics(const Partition &partition,
|
|||||||
|
|
||||||
auto LoadAndUpdateEdgeExpandedGraph(const CustomizationConfig &config,
|
auto LoadAndUpdateEdgeExpandedGraph(const CustomizationConfig &config,
|
||||||
const partitioner::MultiLevelPartition &mlp,
|
const partitioner::MultiLevelPartition &mlp,
|
||||||
|
std::vector<EdgeWeight> &node_weights,
|
||||||
|
std::vector<EdgeDuration> &node_durations,
|
||||||
std::uint32_t &connectivity_checksum)
|
std::uint32_t &connectivity_checksum)
|
||||||
{
|
{
|
||||||
updater::Updater updater(config.updater_config);
|
updater::Updater updater(config.updater_config);
|
||||||
|
|
||||||
EdgeID num_nodes;
|
|
||||||
std::vector<extractor::EdgeBasedEdge> edge_based_edge_list;
|
std::vector<extractor::EdgeBasedEdge> edge_based_edge_list;
|
||||||
std::tie(num_nodes, edge_based_edge_list, connectivity_checksum) =
|
EdgeID num_nodes = updater.LoadAndUpdateEdgeExpandedGraph(
|
||||||
updater.LoadAndUpdateEdgeExpandedGraph();
|
edge_based_edge_list, node_weights, node_durations, connectivity_checksum);
|
||||||
|
|
||||||
auto directed = partitioner::splitBidirectionalEdges(edge_based_edge_list);
|
auto directed = partitioner::splitBidirectionalEdges(edge_based_edge_list);
|
||||||
auto tidied =
|
|
||||||
partitioner::prepareEdgesForUsageInGraph<StaticEdgeBasedGraphEdge>(std::move(directed));
|
auto tidied = partitioner::prepareEdgesForUsageInGraph<
|
||||||
auto edge_based_graph = customizer::MultiLevelEdgeBasedGraph(mlp, num_nodes, std::move(tidied));
|
typename partitioner::MultiLevelEdgeBasedGraph::InputEdge>(std::move(directed));
|
||||||
|
|
||||||
|
auto edge_based_graph =
|
||||||
|
partitioner::MultiLevelEdgeBasedGraph(mlp, num_nodes, std::move(tidied));
|
||||||
|
|
||||||
return edge_based_graph;
|
return edge_based_graph;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<CellMetric> customizeFilteredMetrics(const MultiLevelEdgeBasedGraph &graph,
|
std::vector<CellMetric> customizeFilteredMetrics(const partitioner::MultiLevelEdgeBasedGraph &graph,
|
||||||
const partitioner::CellStorage &storage,
|
const partitioner::CellStorage &storage,
|
||||||
const CellCustomizer &customizer,
|
const CellCustomizer &customizer,
|
||||||
const std::vector<std::vector<bool>> &node_filters)
|
const std::vector<std::vector<bool>> &node_filters)
|
||||||
@@ -119,8 +123,13 @@ int Customizer::Run(const CustomizationConfig &config)
|
|||||||
partitioner::MultiLevelPartition mlp;
|
partitioner::MultiLevelPartition mlp;
|
||||||
partitioner::files::readPartition(config.GetPath(".osrm.partition"), 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;
|
std::uint32_t connectivity_checksum = 0;
|
||||||
auto graph = LoadAndUpdateEdgeExpandedGraph(config, mlp, connectivity_checksum);
|
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; });
|
||||||
util::Log() << "Loaded edge based graph: " << graph.GetNumberOfEdges() << " edges, "
|
util::Log() << "Loaded edge based graph: " << graph.GetNumberOfEdges() << " edges, "
|
||||||
<< graph.GetNumberOfNodes() << " nodes";
|
<< graph.GetNumberOfNodes() << " nodes";
|
||||||
|
|
||||||
@@ -157,7 +166,10 @@ int Customizer::Run(const CustomizationConfig &config)
|
|||||||
util::Log() << "MLD customization writing took " << TIMER_SEC(writing_mld_data) << " seconds";
|
util::Log() << "MLD customization writing took " << TIMER_SEC(writing_mld_data) << " seconds";
|
||||||
|
|
||||||
TIMER_START(writing_graph);
|
TIMER_START(writing_graph);
|
||||||
partitioner::files::writeGraph(config.GetPath(".osrm.mldgr"), graph, connectivity_checksum);
|
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);
|
||||||
TIMER_STOP(writing_graph);
|
TIMER_STOP(writing_graph);
|
||||||
util::Log() << "Graph writing took " << TIMER_SEC(writing_graph) << " seconds";
|
util::Log() << "Graph writing took " << TIMER_SEC(writing_graph) << " seconds";
|
||||||
|
|
||||||
|
|||||||
@@ -621,6 +621,7 @@ RouteSteps collapseSegregatedTurnInstructions(RouteSteps steps)
|
|||||||
// else if the current step is segregated and the next step is not segregated
|
// 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
|
// and the next step is not a roundabout then combine with turn adjustment
|
||||||
else if (curr_step->is_segregated && !next_step->is_segregated &&
|
else if (curr_step->is_segregated && !next_step->is_segregated &&
|
||||||
|
!hasRoundaboutType(curr_step->maneuver.instruction) &&
|
||||||
!hasRoundaboutType(next_step->maneuver.instruction))
|
!hasRoundaboutType(next_step->maneuver.instruction))
|
||||||
{
|
{
|
||||||
// Determine if u-turn
|
// Determine if u-turn
|
||||||
|
|||||||
@@ -81,16 +81,21 @@ Status TablePlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms,
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto snapped_phantoms = SnapPhantomNodes(phantom_nodes);
|
auto snapped_phantoms = SnapPhantomNodes(phantom_nodes);
|
||||||
auto result_table =
|
|
||||||
algorithms.ManyToManySearch(snapped_phantoms, params.sources, params.destinations);
|
|
||||||
|
|
||||||
if (result_table.empty())
|
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()))
|
||||||
{
|
{
|
||||||
return Error("NoTable", "No table found", result);
|
return Error("NoTable", "No table found", result);
|
||||||
}
|
}
|
||||||
|
|
||||||
api::TableAPI table_api{facade, params};
|
api::TableAPI table_api{facade, params};
|
||||||
table_api.MakeResponse(result_table, snapped_phantoms, result);
|
table_api.MakeResponse(result_tables_pair, snapped_phantoms, result);
|
||||||
|
|
||||||
return Status::Ok;
|
return Status::Ok;
|
||||||
}
|
}
|
||||||
|
|||||||
+212
-540
@@ -1,5 +1,6 @@
|
|||||||
#include "guidance/turn_instruction.hpp"
|
#include "guidance/turn_instruction.hpp"
|
||||||
|
|
||||||
|
#include "engine/plugins/plugin_base.hpp"
|
||||||
#include "engine/plugins/plugin_base.hpp"
|
#include "engine/plugins/plugin_base.hpp"
|
||||||
#include "engine/plugins/tile.hpp"
|
#include "engine/plugins/tile.hpp"
|
||||||
|
|
||||||
@@ -8,21 +9,19 @@
|
|||||||
#include "util/vector_tile.hpp"
|
#include "util/vector_tile.hpp"
|
||||||
#include "util/web_mercator.hpp"
|
#include "util/web_mercator.hpp"
|
||||||
|
|
||||||
#include "engine/api/json_factory.hpp"
|
|
||||||
|
|
||||||
#include <boost/geometry.hpp>
|
#include <boost/geometry.hpp>
|
||||||
#include <boost/geometry/geometries/geometries.hpp>
|
#include <boost/geometry/geometries/geometries.hpp>
|
||||||
#include <boost/geometry/geometries/point_xy.hpp>
|
#include <boost/geometry/geometries/point_xy.hpp>
|
||||||
#include <boost/geometry/multi/geometries/multi_linestring.hpp>
|
#include <boost/geometry/multi/geometries/multi_linestring.hpp>
|
||||||
|
|
||||||
#include <protozero/pbf_writer.hpp>
|
#include <vtzero/builder.hpp>
|
||||||
#include <protozero/varint.hpp>
|
#include <vtzero/geometry.hpp>
|
||||||
|
#include <vtzero/index.hpp>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <unordered_set>
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -41,45 +40,7 @@ constexpr const static int MIN_ZOOM_FOR_TURNS = 15;
|
|||||||
namespace
|
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;
|
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
|
// Simple container class for WGS84 coordinates
|
||||||
template <typename T> struct Point final
|
template <typename T> struct Point final
|
||||||
{
|
{
|
||||||
@@ -132,58 +93,15 @@ 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,
|
point_t(util::vector_tile::EXTENT + util::vector_tile::BUFFER,
|
||||||
util::vector_tile::EXTENT + util::vector_tile::BUFFER));
|
util::vector_tile::EXTENT + util::vector_tile::BUFFER));
|
||||||
|
|
||||||
// from mapnik-vector-tile
|
/**
|
||||||
// Encodes a linestring using protobuf zigzag encoding
|
* Return the x1,y1,x2,y2 pixel coordinates of a line in a given
|
||||||
inline bool encodeLinestring(const FixedLine &line,
|
* tile.
|
||||||
protozero::packed_field_uint32 &geometry,
|
*
|
||||||
std::int32_t &start_x,
|
* @param start the first coordinate of the line
|
||||||
std::int32_t &start_y)
|
* @param target the last coordinate of the line
|
||||||
{
|
* @param tile_bbox the boundaries of the tile, in mercator coordinates
|
||||||
const std::size_t line_size = line.size();
|
* @return a FixedLine with coordinates relative to the tile_bbox.
|
||||||
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 floatLineToTileLine(const FloatLine &geo_line, const BBox &tile_bbox)
|
||||||
{
|
{
|
||||||
linestring_t unclipped_line;
|
linestring_t unclipped_line;
|
||||||
@@ -361,6 +279,144 @@ std::vector<NodeID> getSegregatedNodes(const DataFacadeBase &facade,
|
|||||||
return result;
|
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,
|
void encodeVectorTile(const DataFacadeBase &facade,
|
||||||
unsigned x,
|
unsigned x,
|
||||||
unsigned y,
|
unsigned y,
|
||||||
@@ -371,117 +427,25 @@ void encodeVectorTile(const DataFacadeBase &facade,
|
|||||||
const std::vector<NodeID> &segregated_nodes,
|
const std::vector<NodeID> &segregated_nodes,
|
||||||
std::string &pbf_buffer)
|
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) {
|
const auto get_geometry_id = [&facade](auto edge) {
|
||||||
return facade.GetGeometryIndex(edge.forward_segment_id.id).id;
|
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
|
// Convert tile coordinates into mercator coordinates
|
||||||
double min_mercator_lon, min_mercator_lat, max_mercator_lon, max_mercator_lat;
|
double min_mercator_lon, min_mercator_lat, max_mercator_lon, max_mercator_lat;
|
||||||
util::web_mercator::xyzToMercator(
|
util::web_mercator::xyzToMercator(
|
||||||
x, y, z, min_mercator_lon, min_mercator_lat, max_mercator_lon, max_mercator_lat);
|
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};
|
const BBox tile_bbox{min_mercator_lon, min_mercator_lat, max_mercator_lon, max_mercator_lat};
|
||||||
|
|
||||||
// Protobuf serializes blocks when objects go out of scope, hence
|
// XXX leaving in some superfluous scopes to make diff easier to read.
|
||||||
// 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
|
// Begin the layer features block
|
||||||
{
|
{
|
||||||
|
SpeedLayer speeds_layer{tile};
|
||||||
|
|
||||||
// Each feature gets a unique id, starting at 1
|
// Each feature gets a unique id, starting at 1
|
||||||
unsigned id = 1;
|
unsigned id = 1;
|
||||||
for (const auto &edge_index : sorted_edge_indexes)
|
for (const auto &edge_index : sorted_edge_indexes)
|
||||||
@@ -516,7 +480,6 @@ void encodeVectorTile(const DataFacadeBase &facade,
|
|||||||
const auto reverse_duration =
|
const auto reverse_duration =
|
||||||
reverse_duration_range[reverse_duration_range.size() -
|
reverse_duration_range[reverse_duration_range.size() -
|
||||||
edge.fwd_segment_position - 1];
|
edge.fwd_segment_position - 1];
|
||||||
|
|
||||||
const auto forward_datasource_idx =
|
const auto forward_datasource_idx =
|
||||||
forward_datasource_range(edge.fwd_segment_position);
|
forward_datasource_range(edge.fwd_segment_position);
|
||||||
const auto reverse_datasource_idx = reverse_datasource_range(
|
const auto reverse_datasource_idx = reverse_datasource_range(
|
||||||
@@ -526,84 +489,10 @@ void encodeVectorTile(const DataFacadeBase &facade,
|
|||||||
const auto name_id = facade.GetNameIndex(edge.forward_segment_id.id);
|
const auto name_id = facade.GetNameIndex(edge.forward_segment_id.id);
|
||||||
auto name = facade.GetNameForID(name_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 this is a valid forward edge, go ahead and add it to the tile
|
||||||
if (forward_duration != 0 && edge.forward_segment_id.enabled)
|
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
|
// 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 =
|
std::uint32_t speed_kmh_idx =
|
||||||
static_cast<std::uint32_t>(round(length / forward_duration * 10 * 3.6));
|
static_cast<std::uint32_t>(round(length / forward_duration * 10 * 3.6));
|
||||||
|
|
||||||
@@ -616,15 +505,19 @@ void encodeVectorTile(const DataFacadeBase &facade,
|
|||||||
auto tile_line = coordinatesToTileLine(a, b, tile_bbox);
|
auto tile_line = coordinatesToTileLine(a, b, tile_bbox);
|
||||||
if (!tile_line.empty())
|
if (!tile_line.empty())
|
||||||
{
|
{
|
||||||
encode_tile_line(tile_line,
|
SpeedLayerFeatureBuilder fbuilder{speeds_layer, id};
|
||||||
speed_kmh_idx,
|
fbuilder.add_linestring_from_container(tile_line);
|
||||||
line_int_index.indexOf(forward_rate),
|
|
||||||
line_int_index.indexOf(forward_weight),
|
fbuilder.set_speed(speed_kmh_idx);
|
||||||
line_int_index.indexOf(forward_duration),
|
fbuilder.set_is_small(component_id.is_tiny);
|
||||||
forward_datasource_idx,
|
fbuilder.set_datasource(
|
||||||
line_string_index.indexOf(name),
|
facade.GetDatasourceName(forward_datasource_idx).to_string());
|
||||||
start_x,
|
fbuilder.set_weight(forward_weight / 10.0);
|
||||||
start_y);
|
fbuilder.set_duration(forward_duration / 10.0);
|
||||||
|
fbuilder.set_name(name);
|
||||||
|
fbuilder.set_rate(forward_rate / 10.0);
|
||||||
|
|
||||||
|
fbuilder.commit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -632,12 +525,7 @@ void encodeVectorTile(const DataFacadeBase &facade,
|
|||||||
// properties
|
// properties
|
||||||
if (reverse_duration != 0 && edge.reverse_segment_id.enabled)
|
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
|
// 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 =
|
std::uint32_t speed_kmh_idx =
|
||||||
static_cast<std::uint32_t>(round(length / reverse_duration * 10 * 3.6));
|
static_cast<std::uint32_t>(round(length / reverse_duration * 10 * 3.6));
|
||||||
|
|
||||||
@@ -650,232 +538,52 @@ void encodeVectorTile(const DataFacadeBase &facade,
|
|||||||
auto tile_line = coordinatesToTileLine(b, a, tile_bbox);
|
auto tile_line = coordinatesToTileLine(b, a, tile_bbox);
|
||||||
if (!tile_line.empty())
|
if (!tile_line.empty())
|
||||||
{
|
{
|
||||||
encode_tile_line(tile_line,
|
SpeedLayerFeatureBuilder fbuilder{speeds_layer, id};
|
||||||
speed_kmh_idx,
|
fbuilder.add_linestring_from_container(tile_line);
|
||||||
line_int_index.indexOf(reverse_rate),
|
|
||||||
line_int_index.indexOf(reverse_weight),
|
fbuilder.set_speed(speed_kmh_idx);
|
||||||
line_int_index.indexOf(reverse_duration),
|
fbuilder.set_is_small(component_id.is_tiny);
|
||||||
reverse_datasource_idx,
|
fbuilder.set_datasource(
|
||||||
line_string_index.indexOf(name),
|
facade.GetDatasourceName(reverse_datasource_idx).to_string());
|
||||||
start_x,
|
fbuilder.set_weight(reverse_weight / 10.0);
|
||||||
start_y);
|
fbuilder.set_duration(reverse_duration / 10.0);
|
||||||
|
fbuilder.set_name(name);
|
||||||
|
fbuilder.set_rate(reverse_rate / 10.0);
|
||||||
|
|
||||||
|
fbuilder.commit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
// 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)
|
// for tiles of z<16, and tiles that don't show any intersections)
|
||||||
if (!all_turn_data.empty())
|
if (!all_turn_data.empty())
|
||||||
{
|
{
|
||||||
|
TurnsLayer turns_layer{tile};
|
||||||
struct EncodedTurnData
|
uint64_t id = 0;
|
||||||
|
for (const auto &turn_data : all_turn_data)
|
||||||
{
|
{
|
||||||
util::Coordinate coordinate;
|
const auto tile_point = coordinatesToTilePoint(turn_data.coordinate, tile_bbox);
|
||||||
std::size_t angle_index;
|
if (boost::geometry::within(point_t(tile_point.x, tile_point.y), clip_box))
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
const auto tile_point = coordinatesToTilePoint(turndata.coordinate, tile_bbox);
|
TurnsLayerFeatureBuilder fbuilder{turns_layer, ++id};
|
||||||
if (!boost::geometry::within(point_t(tile_point.x, tile_point.y), clip_box))
|
fbuilder.add_point(tile_point);
|
||||||
{
|
|
||||||
continue;
|
fbuilder.set_bearing_in(turn_data.in_angle);
|
||||||
}
|
fbuilder.set_turn_angle(turn_data.turn_angle);
|
||||||
encode_tile_point(tile_point, turndata);
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
// 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;
|
std::vector<NodeID> internal_nodes;
|
||||||
internal_nodes.reserve(edges.size() * 2);
|
internal_nodes.reserve(edges.size() * 2);
|
||||||
for (const auto &edge : edges)
|
for (const auto &edge : edges)
|
||||||
@@ -887,6 +595,8 @@ void encodeVectorTile(const DataFacadeBase &facade,
|
|||||||
auto new_end = std::unique(internal_nodes.begin(), internal_nodes.end());
|
auto new_end = std::unique(internal_nodes.begin(), internal_nodes.end());
|
||||||
internal_nodes.resize(new_end - internal_nodes.begin());
|
internal_nodes.resize(new_end - internal_nodes.begin());
|
||||||
|
|
||||||
|
vtzero::layer_builder osmnodes_layer{tile, "osmnodes"};
|
||||||
|
|
||||||
for (const auto &internal_node : internal_nodes)
|
for (const auto &internal_node : internal_nodes)
|
||||||
{
|
{
|
||||||
const auto coord = facade.GetCoordinateOfNode(internal_node);
|
const auto coord = facade.GetCoordinateOfNode(internal_node);
|
||||||
@@ -895,32 +605,19 @@ void encodeVectorTile(const DataFacadeBase &facade,
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
protozero::pbf_writer feature_writer(point_layer_writer,
|
|
||||||
util::vector_tile::FEATURE_TAG);
|
vtzero::point_feature_builder fbuilder{osmnodes_layer};
|
||||||
// Field 3 is the "geometry type" field. Value 1 is "point"
|
fbuilder.set_id(
|
||||||
feature_writer.add_enum(util::vector_tile::GEOMETRY_TAG,
|
static_cast<OSMNodeID::value_type>(facade.GetOSMNodeIDOfNode(internal_node)));
|
||||||
util::vector_tile::GEOMETRY_TYPE_POINT); // geometry type
|
fbuilder.add_point(tile_point);
|
||||||
const auto osmid =
|
fbuilder.commit();
|
||||||
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
|
||||||
{
|
{
|
||||||
protozero::pbf_writer line_layer_writer(tile_writer, util::vector_tile::LAYER_TAG);
|
vtzero::layer_builder internal_nodes_layer{tile, "internal-nodes"};
|
||||||
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)
|
for (auto edgeNodeID : segregated_nodes)
|
||||||
{
|
{
|
||||||
auto const geomIndex = facade.GetGeometryIndex(edgeNodeID);
|
auto const geomIndex = facade.GetGeometryIndex(edgeNodeID);
|
||||||
@@ -937,46 +634,21 @@ void encodeVectorTile(const DataFacadeBase &facade,
|
|||||||
points.push_back(facade.GetCoordinateOfNode(nodeID));
|
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);
|
auto tile_lines = coordinatesToTileLine(points, tile_bbox);
|
||||||
if (!tile_lines.empty())
|
if (!tile_lines.empty())
|
||||||
{
|
{
|
||||||
for (auto const &tl : tile_lines)
|
vtzero::linestring_feature_builder fbuilder{internal_nodes_layer};
|
||||||
|
for (auto const &tile_line : tile_lines)
|
||||||
{
|
{
|
||||||
encode_tile_line(tl, start_x, start_y);
|
fbuilder.add_linestring_from_container(tile_line);
|
||||||
}
|
}
|
||||||
|
fbuilder.commit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// protozero serializes data during object destructors, so once the scope closes,
|
|
||||||
// our result buffer will have all the tile data encoded into it.
|
tile.serialize(pbf_buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+24
-19
@@ -131,7 +131,7 @@ void ManipulateTableForFSE(const std::size_t source_id,
|
|||||||
result_table.SetValue(destination_id, i, INVALID_EDGE_WEIGHT);
|
result_table.SetValue(destination_id, i, INVALID_EDGE_WEIGHT);
|
||||||
}
|
}
|
||||||
|
|
||||||
// set destination->source to zero so rountrip treats source and
|
// set destination->source to zero so roundtrip treats source and
|
||||||
// destination as one location
|
// destination as one location
|
||||||
result_table.SetValue(destination_id, source_id, 0);
|
result_table.SetValue(destination_id, source_id, 0);
|
||||||
|
|
||||||
@@ -216,61 +216,66 @@ Status TripPlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms,
|
|||||||
BOOST_ASSERT(snapped_phantoms.size() == number_of_locations);
|
BOOST_ASSERT(snapped_phantoms.size() == number_of_locations);
|
||||||
|
|
||||||
// compute the duration table of all phantom nodes
|
// compute the duration table of all phantom nodes
|
||||||
auto result_table = util::DistTableWrapper<EdgeWeight>(
|
auto result_duration_table = util::DistTableWrapper<EdgeWeight>(
|
||||||
algorithms.ManyToManySearch(snapped_phantoms, {}, {}), number_of_locations);
|
algorithms
|
||||||
|
.ManyToManySearch(
|
||||||
|
snapped_phantoms, {}, {}, /*requestDistance*/ false, /*requestDuration*/ true)
|
||||||
|
.first,
|
||||||
|
number_of_locations);
|
||||||
|
|
||||||
if (result_table.size() == 0)
|
if (result_duration_table.size() == 0)
|
||||||
{
|
{
|
||||||
return Status::Error;
|
return Status::Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
const constexpr std::size_t BF_MAX_FEASABLE = 10;
|
const constexpr std::size_t BF_MAX_FEASABLE = 10;
|
||||||
BOOST_ASSERT_MSG(result_table.size() == number_of_locations * number_of_locations,
|
BOOST_ASSERT_MSG(result_duration_table.size() == number_of_locations * number_of_locations,
|
||||||
"Distance Table has wrong size");
|
"Distance Table has wrong size");
|
||||||
|
|
||||||
if (!IsStronglyConnectedComponent(result_table))
|
if (!IsStronglyConnectedComponent(result_duration_table))
|
||||||
{
|
{
|
||||||
return Error("NoTrips", "No trip visiting all destinations possible.", json_result);
|
return Error("NoTrips", "No trip visiting all destinations possible.", json_result);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fixed_start && fixed_end)
|
if (fixed_start && fixed_end)
|
||||||
{
|
{
|
||||||
ManipulateTableForFSE(source_id, destination_id, result_table);
|
ManipulateTableForFSE(source_id, destination_id, result_duration_table);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<NodeID> trip;
|
std::vector<NodeID> duration_trip;
|
||||||
trip.reserve(number_of_locations);
|
duration_trip.reserve(number_of_locations);
|
||||||
// get an optimized order in which the destinations should be visited
|
// get an optimized order in which the destinations should be visited
|
||||||
if (number_of_locations < BF_MAX_FEASABLE)
|
if (number_of_locations < BF_MAX_FEASABLE)
|
||||||
{
|
{
|
||||||
trip = trip::BruteForceTrip(number_of_locations, result_table);
|
duration_trip = trip::BruteForceTrip(number_of_locations, result_duration_table);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
trip = trip::FarthestInsertionTrip(number_of_locations, result_table);
|
duration_trip = trip::FarthestInsertionTrip(number_of_locations, result_duration_table);
|
||||||
}
|
}
|
||||||
|
|
||||||
// rotate result such that roundtrip starts at node with index 0
|
// rotate result such that roundtrip starts at node with index 0
|
||||||
// thist first if covers scenarios: !fixed_end || fixed_start || (fixed_start && fixed_end)
|
// thist first if covers scenarios: !fixed_end || fixed_start || (fixed_start && fixed_end)
|
||||||
if (!fixed_end || fixed_start)
|
if (!fixed_end || fixed_start)
|
||||||
{
|
{
|
||||||
auto desired_start_index = std::find(std::begin(trip), std::end(trip), 0);
|
auto desired_start_index = std::find(std::begin(duration_trip), std::end(duration_trip), 0);
|
||||||
BOOST_ASSERT(desired_start_index != std::end(trip));
|
BOOST_ASSERT(desired_start_index != std::end(duration_trip));
|
||||||
std::rotate(std::begin(trip), desired_start_index, std::end(trip));
|
std::rotate(std::begin(duration_trip), desired_start_index, std::end(duration_trip));
|
||||||
}
|
}
|
||||||
else if (fixed_end && !fixed_start && parameters.roundtrip)
|
else if (fixed_end && !fixed_start && parameters.roundtrip)
|
||||||
{
|
{
|
||||||
auto desired_start_index = std::find(std::begin(trip), std::end(trip), destination_id);
|
auto desired_start_index =
|
||||||
BOOST_ASSERT(desired_start_index != std::end(trip));
|
std::find(std::begin(duration_trip), std::end(duration_trip), destination_id);
|
||||||
std::rotate(std::begin(trip), desired_start_index, std::end(trip));
|
BOOST_ASSERT(desired_start_index != std::end(duration_trip));
|
||||||
|
std::rotate(std::begin(duration_trip), desired_start_index, std::end(duration_trip));
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the route when visiting all destinations in optimized order
|
// get the route when visiting all destinations in optimized order
|
||||||
InternalRouteResult route =
|
InternalRouteResult route =
|
||||||
ComputeRoute(algorithms, snapped_phantoms, trip, parameters.roundtrip);
|
ComputeRoute(algorithms, snapped_phantoms, duration_trip, parameters.roundtrip);
|
||||||
|
|
||||||
// get api response
|
// get api response
|
||||||
const std::vector<std::vector<NodeID>> trips = {trip};
|
const std::vector<std::vector<NodeID>> trips = {duration_trip};
|
||||||
const std::vector<InternalRouteResult> routes = {route};
|
const std::vector<InternalRouteResult> routes = {route};
|
||||||
api::TripAPI trip_api{facade, parameters};
|
api::TripAPI trip_api{facade, parameters};
|
||||||
trip_api.MakeResponse(trips, routes, snapped_phantoms, json_result);
|
trip_api.MakeResponse(trips, routes, snapped_phantoms, json_result);
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ void alternativeRoutingStep(const DataFacade<Algorithm> &facade,
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// check whether there is a loop present at the node
|
// check whether there is a loop present at the node
|
||||||
const auto loop_weight = getLoopWeight<false>(facade, node);
|
const auto loop_weight = std::get<0>(getLoopWeight<false>(facade, node));
|
||||||
const EdgeWeight new_weight_with_loop = new_weight + loop_weight;
|
const EdgeWeight new_weight_with_loop = new_weight + loop_weight;
|
||||||
if (loop_weight != INVALID_EDGE_WEIGHT &&
|
if (loop_weight != INVALID_EDGE_WEIGHT &&
|
||||||
new_weight_with_loop <= *upper_bound_to_shortest_path_weight)
|
new_weight_with_loop <= *upper_bound_to_shortest_path_weight)
|
||||||
@@ -558,7 +558,7 @@ bool viaNodeCandidatePassesTTest(SearchEngineData<Algorithm> &engine_working_dat
|
|||||||
}
|
}
|
||||||
return (upper_bound <= t_test_path_weight);
|
return (upper_bound <= t_test_path_weight);
|
||||||
}
|
}
|
||||||
} // anon. namespace
|
} // namespace
|
||||||
|
|
||||||
InternalManyRoutesResult alternativePathSearch(SearchEngineData<Algorithm> &engine_working_data,
|
InternalManyRoutesResult alternativePathSearch(SearchEngineData<Algorithm> &engine_working_data,
|
||||||
const DataFacade<Algorithm> &facade,
|
const DataFacade<Algorithm> &facade,
|
||||||
@@ -853,4 +853,4 @@ InternalManyRoutesResult alternativePathSearch(SearchEngineData<Algorithm> &engi
|
|||||||
|
|
||||||
} // namespace routing_algorithms
|
} // namespace routing_algorithms
|
||||||
} // namespace engine
|
} // namespace engine
|
||||||
} // namespace osrm}
|
} // namespace osrm
|
||||||
|
|||||||
@@ -470,10 +470,10 @@ RandIt filterUnpackedPathsBySharing(RandIt first,
|
|||||||
if (shortest_path.edges.empty())
|
if (shortest_path.edges.empty())
|
||||||
return last;
|
return last;
|
||||||
|
|
||||||
std::unordered_set<EdgeID> edges;
|
std::unordered_set<NodeID> nodes;
|
||||||
edges.reserve(size * shortest_path.edges.size() * (1.25));
|
nodes.reserve(size * shortest_path.nodes.size() * (1.25));
|
||||||
|
|
||||||
edges.insert(begin(shortest_path.edges), end(shortest_path.edges));
|
nodes.insert(begin(shortest_path.nodes), end(shortest_path.nodes));
|
||||||
|
|
||||||
const auto over_sharing_limit = [&](auto &unpacked) {
|
const auto over_sharing_limit = [&](auto &unpacked) {
|
||||||
if (unpacked.edges.empty())
|
if (unpacked.edges.empty())
|
||||||
@@ -482,20 +482,20 @@ RandIt filterUnpackedPathsBySharing(RandIt first,
|
|||||||
}
|
}
|
||||||
|
|
||||||
EdgeWeight total_duration = 0;
|
EdgeWeight total_duration = 0;
|
||||||
const auto add_if_seen = [&](const EdgeWeight duration, const EdgeID edge) {
|
const auto add_if_seen = [&](const EdgeWeight duration, const NodeID node) {
|
||||||
auto edge_duration = facade.GetEdgeData(edge).duration;
|
auto node_duration = facade.GetNodeDuration(node);
|
||||||
total_duration += edge_duration;
|
total_duration += node_duration;
|
||||||
if (edges.count(edge) > 0)
|
if (nodes.count(node) > 0)
|
||||||
{
|
{
|
||||||
return duration + edge_duration;
|
return duration + node_duration;
|
||||||
}
|
}
|
||||||
return duration;
|
return duration;
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto shared_weight =
|
const auto shared_duration = std::accumulate(
|
||||||
std::accumulate(begin(unpacked.edges), end(unpacked.edges), EdgeWeight{0}, add_if_seen);
|
begin(unpacked.nodes), end(unpacked.nodes), EdgeDuration{0}, add_if_seen);
|
||||||
|
|
||||||
unpacked.sharing = shared_weight / static_cast<double>(total_duration);
|
unpacked.sharing = shared_duration / static_cast<double>(total_duration);
|
||||||
BOOST_ASSERT(unpacked.sharing >= 0.);
|
BOOST_ASSERT(unpacked.sharing >= 0.);
|
||||||
BOOST_ASSERT(unpacked.sharing <= 1.);
|
BOOST_ASSERT(unpacked.sharing <= 1.);
|
||||||
|
|
||||||
@@ -505,7 +505,7 @@ RandIt filterUnpackedPathsBySharing(RandIt first,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
edges.insert(begin(unpacked.edges), end(unpacked.edges));
|
nodes.insert(begin(unpacked.nodes), end(unpacked.nodes));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
#include "engine/routing_algorithms/direct_shortest_path.hpp"
|
#include "engine/routing_algorithms/direct_shortest_path.hpp"
|
||||||
|
|
||||||
#include "engine/routing_algorithms/routing_base.hpp"
|
#include "engine/routing_algorithms/routing_base.hpp"
|
||||||
#include "engine/routing_algorithms/routing_base_ch.hpp"
|
#include "engine/routing_algorithms/routing_base_ch.hpp"
|
||||||
#include "engine/routing_algorithms/routing_base_mld.hpp"
|
#include "engine/routing_algorithms/routing_base_mld.hpp"
|
||||||
|
|||||||
@@ -21,18 +21,21 @@ namespace ch
|
|||||||
inline bool addLoopWeight(const DataFacade<ch::Algorithm> &facade,
|
inline bool addLoopWeight(const DataFacade<ch::Algorithm> &facade,
|
||||||
const NodeID node,
|
const NodeID node,
|
||||||
EdgeWeight &weight,
|
EdgeWeight &weight,
|
||||||
EdgeDuration &duration)
|
EdgeDuration &duration,
|
||||||
|
EdgeDistance &distance)
|
||||||
{ // Special case for CH when contractor creates a loop edge node->node
|
{ // Special case for CH when contractor creates a loop edge node->node
|
||||||
BOOST_ASSERT(weight < 0);
|
BOOST_ASSERT(weight < 0);
|
||||||
|
|
||||||
const auto loop_weight = ch::getLoopWeight<false>(facade, node);
|
const auto loop_weight = ch::getLoopWeight<false>(facade, node);
|
||||||
if (loop_weight != INVALID_EDGE_WEIGHT)
|
if (std::get<0>(loop_weight) != INVALID_EDGE_WEIGHT)
|
||||||
{
|
{
|
||||||
const auto new_weight_with_loop = weight + loop_weight;
|
const auto new_weight_with_loop = weight + std::get<0>(loop_weight);
|
||||||
if (new_weight_with_loop >= 0)
|
if (new_weight_with_loop >= 0)
|
||||||
{
|
{
|
||||||
weight = new_weight_with_loop;
|
weight = new_weight_with_loop;
|
||||||
duration += ch::getLoopWeight<true>(facade, node);
|
auto result = ch::getLoopWeight<true>(facade, node);
|
||||||
|
duration += std::get<0>(result);
|
||||||
|
distance += std::get<1>(result);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -46,6 +49,7 @@ void relaxOutgoingEdges(const DataFacade<Algorithm> &facade,
|
|||||||
const NodeID node,
|
const NodeID node,
|
||||||
const EdgeWeight weight,
|
const EdgeWeight weight,
|
||||||
const EdgeDuration duration,
|
const EdgeDuration duration,
|
||||||
|
const EdgeDistance distance,
|
||||||
typename SearchEngineData<Algorithm>::ManyToManyQueryHeap &query_heap,
|
typename SearchEngineData<Algorithm>::ManyToManyQueryHeap &query_heap,
|
||||||
const PhantomNode &)
|
const PhantomNode &)
|
||||||
{
|
{
|
||||||
@@ -60,24 +64,26 @@ void relaxOutgoingEdges(const DataFacade<Algorithm> &facade,
|
|||||||
if (DIRECTION == FORWARD_DIRECTION ? data.forward : data.backward)
|
if (DIRECTION == FORWARD_DIRECTION ? data.forward : data.backward)
|
||||||
{
|
{
|
||||||
const NodeID to = facade.GetTarget(edge);
|
const NodeID to = facade.GetTarget(edge);
|
||||||
|
|
||||||
const auto edge_weight = data.weight;
|
const auto edge_weight = data.weight;
|
||||||
|
|
||||||
const auto edge_duration = data.duration;
|
const auto edge_duration = data.duration;
|
||||||
|
const auto edge_distance = data.distance;
|
||||||
|
|
||||||
BOOST_ASSERT_MSG(edge_weight > 0, "edge_weight invalid");
|
BOOST_ASSERT_MSG(edge_weight > 0, "edge_weight invalid");
|
||||||
const auto to_weight = weight + edge_weight;
|
const auto to_weight = weight + edge_weight;
|
||||||
const auto to_duration = duration + edge_duration;
|
const auto to_duration = duration + edge_duration;
|
||||||
|
const auto to_distance = distance + edge_distance;
|
||||||
|
|
||||||
// New Node discovered -> Add to Heap + Node Info Storage
|
// New Node discovered -> Add to Heap + Node Info Storage
|
||||||
if (!query_heap.WasInserted(to))
|
if (!query_heap.WasInserted(to))
|
||||||
{
|
{
|
||||||
query_heap.Insert(to, to_weight, {node, to_duration});
|
query_heap.Insert(to, to_weight, {node, to_duration, to_distance});
|
||||||
}
|
}
|
||||||
// Found a shorter Path -> Update weight and set new parent
|
// Found a shorter Path -> Update weight and set new parent
|
||||||
else if (std::tie(to_weight, to_duration) <
|
else if (std::tie(to_weight, to_duration) <
|
||||||
std::tie(query_heap.GetKey(to), query_heap.GetData(to).duration))
|
std::tie(query_heap.GetKey(to), query_heap.GetData(to).duration))
|
||||||
{
|
{
|
||||||
query_heap.GetData(to) = {node, to_duration};
|
query_heap.GetData(to) = {node, to_duration, to_distance};
|
||||||
query_heap.DecreaseKey(to, to_weight);
|
query_heap.DecreaseKey(to, to_weight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -85,17 +91,20 @@ void relaxOutgoingEdges(const DataFacade<Algorithm> &facade,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void forwardRoutingStep(const DataFacade<Algorithm> &facade,
|
void forwardRoutingStep(const DataFacade<Algorithm> &facade,
|
||||||
const unsigned row_idx,
|
const std::size_t row_index,
|
||||||
const unsigned number_of_targets,
|
const std::size_t number_of_targets,
|
||||||
typename SearchEngineData<Algorithm>::ManyToManyQueryHeap &query_heap,
|
typename SearchEngineData<Algorithm>::ManyToManyQueryHeap &query_heap,
|
||||||
const std::vector<NodeBucket> &search_space_with_buckets,
|
const std::vector<NodeBucket> &search_space_with_buckets,
|
||||||
std::vector<EdgeWeight> &weights_table,
|
std::vector<EdgeWeight> &weights_table,
|
||||||
std::vector<EdgeDuration> &durations_table,
|
std::vector<EdgeDuration> &durations_table,
|
||||||
|
std::vector<EdgeDistance> &distances_table,
|
||||||
|
std::vector<NodeID> &middle_nodes_table,
|
||||||
const PhantomNode &phantom_node)
|
const PhantomNode &phantom_node)
|
||||||
{
|
{
|
||||||
const auto node = query_heap.DeleteMin();
|
const auto node = query_heap.DeleteMin();
|
||||||
const auto source_weight = query_heap.GetKey(node);
|
const auto source_weight = query_heap.GetKey(node);
|
||||||
const auto source_duration = query_heap.GetData(node).duration;
|
const auto source_duration = query_heap.GetData(node).duration;
|
||||||
|
const auto source_distance = query_heap.GetData(node).distance;
|
||||||
|
|
||||||
// Check if each encountered node has an entry
|
// Check if each encountered node has an entry
|
||||||
const auto &bucket_list = std::equal_range(search_space_with_buckets.begin(),
|
const auto &bucket_list = std::equal_range(search_space_with_buckets.begin(),
|
||||||
@@ -105,38 +114,45 @@ void forwardRoutingStep(const DataFacade<Algorithm> &facade,
|
|||||||
for (const auto ¤t_bucket : boost::make_iterator_range(bucket_list))
|
for (const auto ¤t_bucket : boost::make_iterator_range(bucket_list))
|
||||||
{
|
{
|
||||||
// Get target id from bucket entry
|
// Get target id from bucket entry
|
||||||
const auto column_idx = current_bucket.column_index;
|
const auto column_index = current_bucket.column_index;
|
||||||
const auto target_weight = current_bucket.weight;
|
const auto target_weight = current_bucket.weight;
|
||||||
const auto target_duration = current_bucket.duration;
|
const auto target_duration = current_bucket.duration;
|
||||||
|
const auto target_distance = current_bucket.distance;
|
||||||
|
|
||||||
auto ¤t_weight = weights_table[row_idx * number_of_targets + column_idx];
|
auto ¤t_weight = weights_table[row_index * number_of_targets + column_index];
|
||||||
auto ¤t_duration = durations_table[row_idx * number_of_targets + column_idx];
|
auto ¤t_duration = durations_table[row_index * number_of_targets + column_index];
|
||||||
|
auto ¤t_distance = distances_table[row_index * number_of_targets + column_index];
|
||||||
|
|
||||||
// Check if new weight is better
|
// Check if new weight is better
|
||||||
auto new_weight = source_weight + target_weight;
|
auto new_weight = source_weight + target_weight;
|
||||||
auto new_duration = source_duration + target_duration;
|
auto new_duration = source_duration + target_duration;
|
||||||
|
auto new_distance = source_distance + target_distance;
|
||||||
|
|
||||||
if (new_weight < 0)
|
if (new_weight < 0)
|
||||||
{
|
{
|
||||||
if (addLoopWeight(facade, node, new_weight, new_duration))
|
if (addLoopWeight(facade, node, new_weight, new_duration, new_distance))
|
||||||
{
|
{
|
||||||
current_weight = std::min(current_weight, new_weight);
|
current_weight = std::min(current_weight, new_weight);
|
||||||
current_duration = std::min(current_duration, new_duration);
|
current_duration = std::min(current_duration, new_duration);
|
||||||
|
current_distance = std::min(current_distance, new_distance);
|
||||||
|
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))
|
else if (std::tie(new_weight, new_duration) < std::tie(current_weight, current_duration))
|
||||||
{
|
{
|
||||||
current_weight = new_weight;
|
current_weight = new_weight;
|
||||||
current_duration = new_duration;
|
current_duration = new_duration;
|
||||||
|
current_distance = new_distance;
|
||||||
|
middle_nodes_table[row_index * number_of_targets + column_index] = node;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
relaxOutgoingEdges<FORWARD_DIRECTION>(
|
relaxOutgoingEdges<FORWARD_DIRECTION>(
|
||||||
facade, node, source_weight, source_duration, query_heap, phantom_node);
|
facade, node, source_weight, source_duration, source_distance, query_heap, phantom_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
void backwardRoutingStep(const DataFacade<Algorithm> &facade,
|
void backwardRoutingStep(const DataFacade<Algorithm> &facade,
|
||||||
const unsigned column_idx,
|
const unsigned column_index,
|
||||||
typename SearchEngineData<Algorithm>::ManyToManyQueryHeap &query_heap,
|
typename SearchEngineData<Algorithm>::ManyToManyQueryHeap &query_heap,
|
||||||
std::vector<NodeBucket> &search_space_with_buckets,
|
std::vector<NodeBucket> &search_space_with_buckets,
|
||||||
const PhantomNode &phantom_node)
|
const PhantomNode &phantom_node)
|
||||||
@@ -144,36 +160,200 @@ void backwardRoutingStep(const DataFacade<Algorithm> &facade,
|
|||||||
const auto node = query_heap.DeleteMin();
|
const auto node = query_heap.DeleteMin();
|
||||||
const auto target_weight = query_heap.GetKey(node);
|
const auto target_weight = query_heap.GetKey(node);
|
||||||
const auto target_duration = query_heap.GetData(node).duration;
|
const auto target_duration = query_heap.GetData(node).duration;
|
||||||
|
const auto target_distance = query_heap.GetData(node).distance;
|
||||||
|
const auto parent = query_heap.GetData(node).parent;
|
||||||
|
|
||||||
// Store settled nodes in search space bucket
|
// Store settled nodes in search space bucket
|
||||||
search_space_with_buckets.emplace_back(node, column_idx, target_weight, target_duration);
|
search_space_with_buckets.emplace_back(
|
||||||
|
node, parent, column_index, target_weight, target_duration, target_distance);
|
||||||
|
|
||||||
relaxOutgoingEdges<REVERSE_DIRECTION>(
|
relaxOutgoingEdges<REVERSE_DIRECTION>(
|
||||||
facade, node, target_weight, target_duration, query_heap, phantom_node);
|
facade, node, target_weight, target_duration, target_distance, query_heap, phantom_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ch
|
} // namespace ch
|
||||||
|
|
||||||
template <>
|
void retrievePackedPathFromSearchSpace(const NodeID middle_node_id,
|
||||||
std::vector<EdgeDuration> manyToManySearch(SearchEngineData<ch::Algorithm> &engine_working_data,
|
const unsigned column_index,
|
||||||
const DataFacade<ch::Algorithm> &facade,
|
const std::vector<NodeBucket> &search_space_with_buckets,
|
||||||
const std::vector<PhantomNode> &phantom_nodes,
|
std::vector<NodeID> &packed_leg)
|
||||||
const std::vector<std::size_t> &source_indices,
|
|
||||||
const std::vector<std::size_t> &target_indices)
|
|
||||||
{
|
{
|
||||||
|
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 (std::get<0>(weight) != INVALID_EDGE_WEIGHT)
|
||||||
|
packed_leg.push_back(packed_leg.front());
|
||||||
|
}
|
||||||
|
if (!packed_leg.empty())
|
||||||
|
{
|
||||||
|
auto annotation =
|
||||||
|
ch::calculateEBGNodeAnnotations(facade, packed_leg.begin(), packed_leg.end());
|
||||||
|
|
||||||
|
distances_table[row_index * number_of_targets + column_index] = annotation;
|
||||||
|
|
||||||
|
// 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 == packed_leg.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
|
||||||
|
EdgeDistance offset = source_phantom.GetForwardDistance();
|
||||||
|
distances_table[row_index * number_of_targets + column_index] -= offset;
|
||||||
|
}
|
||||||
|
else if (source_phantom.reverse_segment_id.id == packed_leg.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
|
||||||
|
EdgeDistance offset = source_phantom.GetReverseDistance();
|
||||||
|
distances_table[row_index * number_of_targets + column_index] -= offset;
|
||||||
|
}
|
||||||
|
if (target_phantom.forward_segment_id.id == packed_leg.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
|
||||||
|
EdgeDistance offset = target_phantom.GetForwardDistance();
|
||||||
|
distances_table[row_index * number_of_targets + column_index] += offset;
|
||||||
|
}
|
||||||
|
else if (target_phantom.reverse_segment_id.id == packed_leg.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
|
||||||
|
EdgeDistance offset = target_phantom.GetReverseDistance();
|
||||||
|
distances_table[row_index * number_of_targets + column_index] += offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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
|
||||||
|
EdgeDistance offset =
|
||||||
|
target_phantom.GetForwardDistance() - source_phantom.GetForwardDistance();
|
||||||
|
distances_table[row_index * number_of_targets + column_index] = offset;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// s<--- <-- offsets
|
||||||
|
// t<--------- <-- subtract source offset from target offset
|
||||||
|
// ...... <-- want this distance as result
|
||||||
|
// entry 0---1---2---3--- <-- 3 is exit node
|
||||||
|
EdgeDistance offset =
|
||||||
|
target_phantom.GetReverseDistance() - source_phantom.GetReverseDistance();
|
||||||
|
distances_table[row_index * number_of_targets + column_index] = offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
(void)calculate_duration; // TODO: stub to use when computing durations become optional
|
||||||
|
|
||||||
const auto number_of_sources = source_indices.size();
|
const auto number_of_sources = source_indices.size();
|
||||||
const auto number_of_targets = target_indices.size();
|
const auto number_of_targets = target_indices.size();
|
||||||
const auto number_of_entries = number_of_sources * number_of_targets;
|
const auto number_of_entries = number_of_sources * number_of_targets;
|
||||||
|
|
||||||
std::vector<EdgeWeight> weights_table(number_of_entries, INVALID_EDGE_WEIGHT);
|
std::vector<EdgeWeight> weights_table(number_of_entries, INVALID_EDGE_WEIGHT);
|
||||||
std::vector<EdgeDuration> durations_table(number_of_entries, MAXIMAL_EDGE_DURATION);
|
std::vector<EdgeDuration> durations_table(number_of_entries, MAXIMAL_EDGE_DURATION);
|
||||||
|
std::vector<EdgeDistance> distances_table(number_of_entries, MAXIMAL_EDGE_DISTANCE);
|
||||||
|
std::vector<NodeID> middle_nodes_table(number_of_entries, SPECIAL_NODEID);
|
||||||
|
|
||||||
std::vector<NodeBucket> search_space_with_buckets;
|
std::vector<NodeBucket> search_space_with_buckets;
|
||||||
|
|
||||||
// Populate buckets with paths from all accessible nodes to destinations via backward searches
|
// Populate buckets with paths from all accessible nodes to destinations via backward searches
|
||||||
for (std::uint32_t column_idx = 0; column_idx < target_indices.size(); ++column_idx)
|
for (std::uint32_t column_index = 0; column_index < target_indices.size(); ++column_index)
|
||||||
{
|
{
|
||||||
const auto index = target_indices[column_idx];
|
const auto index = target_indices[column_index];
|
||||||
const auto &phantom = phantom_nodes[index];
|
const auto &phantom = phantom_nodes[index];
|
||||||
|
|
||||||
engine_working_data.InitializeOrClearManyToManyThreadLocalStorage(
|
engine_working_data.InitializeOrClearManyToManyThreadLocalStorage(
|
||||||
@@ -184,7 +364,8 @@ std::vector<EdgeDuration> manyToManySearch(SearchEngineData<ch::Algorithm> &engi
|
|||||||
// Explore search space
|
// Explore search space
|
||||||
while (!query_heap.Empty())
|
while (!query_heap.Empty())
|
||||||
{
|
{
|
||||||
backwardRoutingStep(facade, column_idx, query_heap, search_space_with_buckets, phantom);
|
backwardRoutingStep(
|
||||||
|
facade, column_index, query_heap, search_space_with_buckets, phantom);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,32 +373,54 @@ std::vector<EdgeDuration> manyToManySearch(SearchEngineData<ch::Algorithm> &engi
|
|||||||
std::sort(search_space_with_buckets.begin(), search_space_with_buckets.end());
|
std::sort(search_space_with_buckets.begin(), search_space_with_buckets.end());
|
||||||
|
|
||||||
// Find shortest paths from sources to all accessible nodes
|
// Find shortest paths from sources to all accessible nodes
|
||||||
for (std::uint32_t row_idx = 0; row_idx < source_indices.size(); ++row_idx)
|
for (std::uint32_t row_index = 0; row_index < source_indices.size(); ++row_index)
|
||||||
{
|
{
|
||||||
const auto index = source_indices[row_idx];
|
const auto source_index = source_indices[row_index];
|
||||||
const auto &phantom = phantom_nodes[index];
|
const auto &source_phantom = phantom_nodes[source_index];
|
||||||
|
|
||||||
// Clear heap and insert source nodes
|
// Clear heap and insert source nodes
|
||||||
engine_working_data.InitializeOrClearManyToManyThreadLocalStorage(
|
engine_working_data.InitializeOrClearManyToManyThreadLocalStorage(
|
||||||
facade.GetNumberOfNodes());
|
facade.GetNumberOfNodes());
|
||||||
auto &query_heap = *(engine_working_data.many_to_many_heap);
|
auto &query_heap = *(engine_working_data.many_to_many_heap);
|
||||||
insertSourceInHeap(query_heap, phantom);
|
insertSourceInHeap(query_heap, source_phantom);
|
||||||
|
|
||||||
// Explore search space
|
// Explore search space
|
||||||
while (!query_heap.Empty())
|
while (!query_heap.Empty())
|
||||||
{
|
{
|
||||||
forwardRoutingStep(facade,
|
forwardRoutingStep(facade,
|
||||||
row_idx,
|
row_index,
|
||||||
number_of_targets,
|
number_of_targets,
|
||||||
query_heap,
|
query_heap,
|
||||||
search_space_with_buckets,
|
search_space_with_buckets,
|
||||||
weights_table,
|
weights_table,
|
||||||
durations_table,
|
durations_table,
|
||||||
phantom);
|
distances_table,
|
||||||
|
middle_nodes_table,
|
||||||
|
source_phantom);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (calculate_distance)
|
||||||
|
{
|
||||||
|
distances_table.resize(number_of_entries, INVALID_EDGE_DISTANCE);
|
||||||
|
// TODO: this is a hack to work around stuff
|
||||||
|
if (number_of_entries == 0)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return durations_table;
|
return std::make_pair(durations_table, distances_table);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace routing_algorithms
|
} // namespace routing_algorithms
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#include "engine/routing_algorithms/many_to_many.hpp"
|
#include "engine/routing_algorithms/many_to_many.hpp"
|
||||||
#include "engine/routing_algorithms/routing_base.hpp"
|
#include "engine/routing_algorithms/routing_base_mld.hpp"
|
||||||
|
|
||||||
#include <boost/assert.hpp>
|
#include <boost/assert.hpp>
|
||||||
#include <boost/range/iterator_range_core.hpp>
|
#include <boost/range/iterator_range_core.hpp>
|
||||||
@@ -19,22 +19,8 @@ namespace routing_algorithms
|
|||||||
namespace mld
|
namespace mld
|
||||||
{
|
{
|
||||||
|
|
||||||
template <typename MultiLevelPartition>
|
using PackedEdge = std::tuple</*from*/ NodeID, /*to*/ NodeID, /*from_clique_arc*/ bool>;
|
||||||
inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition,
|
using PackedPath = std::vector<PackedEdge>;
|
||||||
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>
|
template <typename MultiLevelPartition>
|
||||||
inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition,
|
inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition,
|
||||||
@@ -50,43 +36,12 @@ inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition,
|
|||||||
return node_level;
|
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>
|
template <bool DIRECTION, typename... Args>
|
||||||
void relaxOutgoingEdges(const DataFacade<mld::Algorithm> &facade,
|
void relaxOutgoingEdges(const DataFacade<mld::Algorithm> &facade,
|
||||||
const NodeID node,
|
const NodeID node,
|
||||||
const EdgeWeight weight,
|
const EdgeWeight weight,
|
||||||
const EdgeDuration duration,
|
const EdgeDuration duration,
|
||||||
|
const EdgeDistance /* distance TODO use this */,
|
||||||
typename SearchEngineData<mld::Algorithm>::ManyToManyQueryHeap &query_heap,
|
typename SearchEngineData<mld::Algorithm>::ManyToManyQueryHeap &query_heap,
|
||||||
Args... args)
|
Args... args)
|
||||||
{
|
{
|
||||||
@@ -123,12 +78,14 @@ void relaxOutgoingEdges(const DataFacade<mld::Algorithm> &facade,
|
|||||||
const auto to_duration = duration + shortcut_durations.front();
|
const auto to_duration = duration + shortcut_durations.front();
|
||||||
if (!query_heap.WasInserted(to))
|
if (!query_heap.WasInserted(to))
|
||||||
{
|
{
|
||||||
query_heap.Insert(to, to_weight, {node, true, to_duration});
|
query_heap.Insert(to, to_weight, {node, true, to_duration, 0});
|
||||||
}
|
}
|
||||||
else if (std::tie(to_weight, to_duration) <
|
else if (std::tie(to_weight, to_duration, node) <
|
||||||
std::tie(query_heap.GetKey(to), query_heap.GetData(to).duration))
|
std::tie(query_heap.GetKey(to),
|
||||||
|
query_heap.GetData(to).duration,
|
||||||
|
query_heap.GetData(to).parent))
|
||||||
{
|
{
|
||||||
query_heap.GetData(to) = {node, true, to_duration};
|
query_heap.GetData(to) = {node, true, to_duration, 0};
|
||||||
query_heap.DecreaseKey(to, to_weight);
|
query_heap.DecreaseKey(to, to_weight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -153,12 +110,14 @@ void relaxOutgoingEdges(const DataFacade<mld::Algorithm> &facade,
|
|||||||
const auto to_duration = duration + shortcut_durations.front();
|
const auto to_duration = duration + shortcut_durations.front();
|
||||||
if (!query_heap.WasInserted(to))
|
if (!query_heap.WasInserted(to))
|
||||||
{
|
{
|
||||||
query_heap.Insert(to, to_weight, {node, true, to_duration});
|
query_heap.Insert(to, to_weight, {node, true, to_duration, 0});
|
||||||
}
|
}
|
||||||
else if (std::tie(to_weight, to_duration) <
|
else if (std::tie(to_weight, to_duration, node) <
|
||||||
std::tie(query_heap.GetKey(to), query_heap.GetData(to).duration))
|
std::tie(query_heap.GetKey(to),
|
||||||
|
query_heap.GetData(to).duration,
|
||||||
|
query_heap.GetData(to).parent))
|
||||||
{
|
{
|
||||||
query_heap.GetData(to) = {node, true, to_duration};
|
query_heap.GetData(to) = {node, true, to_duration, 0};
|
||||||
query_heap.DecreaseKey(to, to_weight);
|
query_heap.DecreaseKey(to, to_weight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -172,7 +131,8 @@ void relaxOutgoingEdges(const DataFacade<mld::Algorithm> &facade,
|
|||||||
for (const auto edge : facade.GetBorderEdgeRange(level, node))
|
for (const auto edge : facade.GetBorderEdgeRange(level, node))
|
||||||
{
|
{
|
||||||
const auto &data = facade.GetEdgeData(edge);
|
const auto &data = facade.GetEdgeData(edge);
|
||||||
if (DIRECTION == FORWARD_DIRECTION ? data.forward : data.backward)
|
if ((DIRECTION == FORWARD_DIRECTION) ? facade.IsForwardEdge(edge)
|
||||||
|
: facade.IsBackwardEdge(edge))
|
||||||
{
|
{
|
||||||
const NodeID to = facade.GetTarget(edge);
|
const NodeID to = facade.GetTarget(edge);
|
||||||
if (facade.ExcludeNode(to))
|
if (facade.ExcludeNode(to))
|
||||||
@@ -180,23 +140,29 @@ void relaxOutgoingEdges(const DataFacade<mld::Algorithm> &facade,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto edge_weight = data.weight;
|
const auto turn_id = data.turn_id;
|
||||||
const auto edge_duration = data.duration;
|
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);
|
||||||
|
|
||||||
BOOST_ASSERT_MSG(edge_weight > 0, "edge_weight invalid");
|
BOOST_ASSERT_MSG(node_weight + turn_weight > 0, "edge weight is invalid");
|
||||||
const auto to_weight = weight + edge_weight;
|
const auto to_weight = weight + turn_weight;
|
||||||
const auto to_duration = duration + edge_duration;
|
const auto to_duration = duration + turn_duration;
|
||||||
|
|
||||||
// New Node discovered -> Add to Heap + Node Info Storage
|
// New Node discovered -> Add to Heap + Node Info Storage
|
||||||
if (!query_heap.WasInserted(to))
|
if (!query_heap.WasInserted(to))
|
||||||
{
|
{
|
||||||
query_heap.Insert(to, to_weight, {node, false, to_duration});
|
query_heap.Insert(to, to_weight, {node, false, to_duration, 0});
|
||||||
}
|
}
|
||||||
// Found a shorter Path -> Update weight and set new parent
|
// Found a shorter Path -> Update weight and set new parent
|
||||||
else if (std::tie(to_weight, to_duration) <
|
else if (std::tie(to_weight, to_duration, node) <
|
||||||
std::tie(query_heap.GetKey(to), query_heap.GetData(to).duration))
|
std::tie(query_heap.GetKey(to),
|
||||||
|
query_heap.GetData(to).duration,
|
||||||
|
query_heap.GetData(to).parent))
|
||||||
{
|
{
|
||||||
query_heap.GetData(to) = {node, false, to_duration};
|
query_heap.GetData(to) = {node, false, to_duration, 0};
|
||||||
query_heap.DecreaseKey(to, to_weight);
|
query_heap.DecreaseKey(to, to_weight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -207,17 +173,21 @@ void relaxOutgoingEdges(const DataFacade<mld::Algorithm> &facade,
|
|||||||
// Unidirectional multi-layer Dijkstra search for 1-to-N and N-to-1 matrices
|
// Unidirectional multi-layer Dijkstra search for 1-to-N and N-to-1 matrices
|
||||||
//
|
//
|
||||||
template <bool DIRECTION>
|
template <bool DIRECTION>
|
||||||
std::vector<EdgeDuration> oneToManySearch(SearchEngineData<Algorithm> &engine_working_data,
|
std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
|
||||||
const DataFacade<Algorithm> &facade,
|
oneToManySearch(SearchEngineData<Algorithm> &engine_working_data,
|
||||||
const std::vector<PhantomNode> &phantom_nodes,
|
const DataFacade<Algorithm> &facade,
|
||||||
std::size_t phantom_index,
|
const std::vector<PhantomNode> &phantom_nodes,
|
||||||
const std::vector<std::size_t> &phantom_indices)
|
std::size_t phantom_index,
|
||||||
|
const std::vector<std::size_t> &phantom_indices,
|
||||||
|
const bool /* calculate_distance */)
|
||||||
{
|
{
|
||||||
std::vector<EdgeWeight> weights(phantom_indices.size(), INVALID_EDGE_WEIGHT);
|
std::vector<EdgeWeight> weights(phantom_indices.size(), INVALID_EDGE_WEIGHT);
|
||||||
std::vector<EdgeDuration> durations(phantom_indices.size(), MAXIMAL_EDGE_DURATION);
|
std::vector<EdgeDuration> durations(phantom_indices.size(), MAXIMAL_EDGE_DURATION);
|
||||||
|
std::vector<EdgeDistance> distances_table(phantom_indices.size(), MAXIMAL_EDGE_DISTANCE);
|
||||||
|
std::vector<NodeID> middle_nodes_table(phantom_indices.size(), SPECIAL_NODEID);
|
||||||
|
|
||||||
// Collect destination (source) nodes into a map
|
// Collect destination (source) nodes into a map
|
||||||
std::unordered_multimap<NodeID, std::tuple<std::size_t, EdgeWeight, EdgeDuration>>
|
std::unordered_multimap<NodeID, std::tuple<std::size_t, EdgeWeight, EdgeDuration, EdgeDistance>>
|
||||||
target_nodes_index;
|
target_nodes_index;
|
||||||
target_nodes_index.reserve(phantom_indices.size());
|
target_nodes_index.reserve(phantom_indices.size());
|
||||||
for (std::size_t index = 0; index < phantom_indices.size(); ++index)
|
for (std::size_t index = 0; index < phantom_indices.size(); ++index)
|
||||||
@@ -232,13 +202,15 @@ std::vector<EdgeDuration> oneToManySearch(SearchEngineData<Algorithm> &engine_wo
|
|||||||
{phantom_node.forward_segment_id.id,
|
{phantom_node.forward_segment_id.id,
|
||||||
std::make_tuple(index,
|
std::make_tuple(index,
|
||||||
phantom_node.GetForwardWeightPlusOffset(),
|
phantom_node.GetForwardWeightPlusOffset(),
|
||||||
phantom_node.GetForwardDuration())});
|
phantom_node.GetForwardDuration(),
|
||||||
|
phantom_node.GetForwardDistance())});
|
||||||
if (phantom_node.IsValidReverseTarget())
|
if (phantom_node.IsValidReverseTarget())
|
||||||
target_nodes_index.insert(
|
target_nodes_index.insert(
|
||||||
{phantom_node.reverse_segment_id.id,
|
{phantom_node.reverse_segment_id.id,
|
||||||
std::make_tuple(index,
|
std::make_tuple(index,
|
||||||
phantom_node.GetReverseWeightPlusOffset(),
|
phantom_node.GetReverseWeightPlusOffset(),
|
||||||
phantom_node.GetReverseDuration())});
|
phantom_node.GetReverseDuration(),
|
||||||
|
phantom_node.GetReverseDistance())});
|
||||||
}
|
}
|
||||||
else if (DIRECTION == REVERSE_DIRECTION)
|
else if (DIRECTION == REVERSE_DIRECTION)
|
||||||
{
|
{
|
||||||
@@ -247,13 +219,15 @@ std::vector<EdgeDuration> oneToManySearch(SearchEngineData<Algorithm> &engine_wo
|
|||||||
{phantom_node.forward_segment_id.id,
|
{phantom_node.forward_segment_id.id,
|
||||||
std::make_tuple(index,
|
std::make_tuple(index,
|
||||||
-phantom_node.GetForwardWeightPlusOffset(),
|
-phantom_node.GetForwardWeightPlusOffset(),
|
||||||
-phantom_node.GetForwardDuration())});
|
-phantom_node.GetForwardDuration(),
|
||||||
|
-phantom_node.GetForwardDistance())});
|
||||||
if (phantom_node.IsValidReverseSource())
|
if (phantom_node.IsValidReverseSource())
|
||||||
target_nodes_index.insert(
|
target_nodes_index.insert(
|
||||||
{phantom_node.reverse_segment_id.id,
|
{phantom_node.reverse_segment_id.id,
|
||||||
std::make_tuple(index,
|
std::make_tuple(index,
|
||||||
-phantom_node.GetReverseWeightPlusOffset(),
|
-phantom_node.GetReverseWeightPlusOffset(),
|
||||||
-phantom_node.GetReverseDuration())});
|
-phantom_node.GetReverseDuration(),
|
||||||
|
-phantom_node.GetReverseDistance())});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -263,52 +237,75 @@ std::vector<EdgeDuration> oneToManySearch(SearchEngineData<Algorithm> &engine_wo
|
|||||||
auto &query_heap = *(engine_working_data.many_to_many_heap);
|
auto &query_heap = *(engine_working_data.many_to_many_heap);
|
||||||
|
|
||||||
// Check if node is in the destinations list and update weights/durations
|
// Check if node is in the destinations list and update weights/durations
|
||||||
auto update_values = [&](NodeID node, EdgeWeight weight, EdgeDuration duration) {
|
auto update_values =
|
||||||
auto candidates = target_nodes_index.equal_range(node);
|
[&](NodeID node, EdgeWeight weight, EdgeDuration duration, EdgeDistance distance) {
|
||||||
for (auto it = candidates.first; it != candidates.second;)
|
auto candidates = target_nodes_index.equal_range(node);
|
||||||
{
|
for (auto it = candidates.first; it != candidates.second;)
|
||||||
std::size_t index;
|
|
||||||
EdgeWeight target_weight;
|
|
||||||
EdgeDuration target_duration;
|
|
||||||
std::tie(index, target_weight, target_duration) = it->second;
|
|
||||||
|
|
||||||
const auto path_weight = weight + target_weight;
|
|
||||||
if (path_weight >= 0)
|
|
||||||
{
|
{
|
||||||
const auto path_duration = duration + target_duration;
|
std::size_t index;
|
||||||
|
EdgeWeight target_weight;
|
||||||
|
EdgeDuration target_duration;
|
||||||
|
EdgeDuration target_distance;
|
||||||
|
std::tie(index, target_weight, target_duration, target_distance) = it->second;
|
||||||
|
|
||||||
if (std::tie(path_weight, path_duration) <
|
const auto path_weight = weight + target_weight;
|
||||||
std::tie(weights[index], durations[index]))
|
if (path_weight >= 0)
|
||||||
{
|
{
|
||||||
weights[index] = path_weight;
|
const auto path_duration = duration + target_duration;
|
||||||
durations[index] = path_duration;
|
const auto path_distance = distance + target_distance;
|
||||||
|
|
||||||
|
if (std::tie(path_weight, path_duration) <
|
||||||
|
std::tie(weights[index], durations[index]))
|
||||||
|
{
|
||||||
|
weights[index] = path_weight;
|
||||||
|
durations[index] = path_duration;
|
||||||
|
distances_table[index] = path_distance;
|
||||||
|
middle_nodes_table[index] = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove node from destinations list
|
||||||
|
it = target_nodes_index.erase(it);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
++it;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove node from destinations list
|
|
||||||
it = target_nodes_index.erase(it);
|
|
||||||
}
|
}
|
||||||
else
|
};
|
||||||
{
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Check a single path result and insert adjacent nodes into heap
|
|
||||||
auto insert_node = [&](NodeID node, EdgeWeight initial_weight, EdgeDuration initial_duration) {
|
|
||||||
|
|
||||||
|
auto insert_node = [&](NodeID node,
|
||||||
|
EdgeWeight initial_weight,
|
||||||
|
EdgeDuration initial_duration,
|
||||||
|
EdgeDistance initial_distance) {
|
||||||
// Update single node paths
|
// Update single node paths
|
||||||
update_values(node, initial_weight, initial_duration);
|
update_values(node, initial_weight, initial_duration, initial_distance);
|
||||||
|
|
||||||
|
query_heap.Insert(node, initial_weight, {node, initial_duration, initial_distance});
|
||||||
|
|
||||||
// Place adjacent nodes into heap
|
// Place adjacent nodes into heap
|
||||||
for (auto edge : facade.GetAdjacentEdgeRange(node))
|
for (auto edge : facade.GetAdjacentEdgeRange(node))
|
||||||
{
|
{
|
||||||
const auto &data = facade.GetEdgeData(edge);
|
const auto &data = facade.GetEdgeData(edge);
|
||||||
if (DIRECTION == FORWARD_DIRECTION ? data.forward : data.backward)
|
const auto to = facade.GetTarget(edge);
|
||||||
|
|
||||||
|
if (facade.ExcludeNode(to))
|
||||||
{
|
{
|
||||||
query_heap.Insert(facade.GetTarget(edge),
|
continue;
|
||||||
data.weight + initial_weight,
|
}
|
||||||
{node, data.duration + initial_duration});
|
|
||||||
|
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);
|
||||||
|
const auto edge_distance = initial_distance;
|
||||||
|
|
||||||
|
query_heap.Insert(to, edge_weight, {node, edge_duration, edge_distance});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -318,28 +315,39 @@ std::vector<EdgeDuration> oneToManySearch(SearchEngineData<Algorithm> &engine_wo
|
|||||||
|
|
||||||
if (DIRECTION == FORWARD_DIRECTION)
|
if (DIRECTION == FORWARD_DIRECTION)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (phantom_node.IsValidForwardSource())
|
if (phantom_node.IsValidForwardSource())
|
||||||
|
{
|
||||||
insert_node(phantom_node.forward_segment_id.id,
|
insert_node(phantom_node.forward_segment_id.id,
|
||||||
-phantom_node.GetForwardWeightPlusOffset(),
|
-phantom_node.GetForwardWeightPlusOffset(),
|
||||||
-phantom_node.GetForwardDuration());
|
-phantom_node.GetForwardDuration(),
|
||||||
|
-phantom_node.GetForwardDistance());
|
||||||
|
}
|
||||||
|
|
||||||
if (phantom_node.IsValidReverseSource())
|
if (phantom_node.IsValidReverseSource())
|
||||||
|
{
|
||||||
insert_node(phantom_node.reverse_segment_id.id,
|
insert_node(phantom_node.reverse_segment_id.id,
|
||||||
-phantom_node.GetReverseWeightPlusOffset(),
|
-phantom_node.GetReverseWeightPlusOffset(),
|
||||||
-phantom_node.GetReverseDuration());
|
-phantom_node.GetReverseDuration(),
|
||||||
|
-phantom_node.GetReverseDistance());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (DIRECTION == REVERSE_DIRECTION)
|
else if (DIRECTION == REVERSE_DIRECTION)
|
||||||
{
|
{
|
||||||
if (phantom_node.IsValidForwardTarget())
|
if (phantom_node.IsValidForwardTarget())
|
||||||
|
{
|
||||||
insert_node(phantom_node.forward_segment_id.id,
|
insert_node(phantom_node.forward_segment_id.id,
|
||||||
phantom_node.GetForwardWeightPlusOffset(),
|
phantom_node.GetForwardWeightPlusOffset(),
|
||||||
phantom_node.GetForwardDuration());
|
phantom_node.GetForwardDuration(),
|
||||||
|
phantom_node.GetForwardDistance());
|
||||||
|
}
|
||||||
|
|
||||||
if (phantom_node.IsValidReverseTarget())
|
if (phantom_node.IsValidReverseTarget())
|
||||||
|
{
|
||||||
insert_node(phantom_node.reverse_segment_id.id,
|
insert_node(phantom_node.reverse_segment_id.id,
|
||||||
phantom_node.GetReverseWeightPlusOffset(),
|
phantom_node.GetReverseWeightPlusOffset(),
|
||||||
phantom_node.GetReverseDuration());
|
phantom_node.GetReverseDuration(),
|
||||||
|
phantom_node.GetReverseDistance());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -349,22 +357,146 @@ std::vector<EdgeDuration> oneToManySearch(SearchEngineData<Algorithm> &engine_wo
|
|||||||
const auto node = query_heap.DeleteMin();
|
const auto node = query_heap.DeleteMin();
|
||||||
const auto weight = query_heap.GetKey(node);
|
const auto weight = query_heap.GetKey(node);
|
||||||
const auto duration = query_heap.GetData(node).duration;
|
const auto duration = query_heap.GetData(node).duration;
|
||||||
|
const auto distance = query_heap.GetData(node).distance;
|
||||||
|
|
||||||
// Update values
|
// Update values
|
||||||
update_values(node, weight, duration);
|
update_values(node, weight, duration, distance);
|
||||||
|
|
||||||
// Relax outgoing edges
|
// Relax outgoing edges
|
||||||
relaxOutgoingEdges<DIRECTION>(facade,
|
relaxOutgoingEdges<DIRECTION>(facade,
|
||||||
node,
|
node,
|
||||||
weight,
|
weight,
|
||||||
duration,
|
duration,
|
||||||
|
distance,
|
||||||
query_heap,
|
query_heap,
|
||||||
phantom_nodes,
|
phantom_nodes,
|
||||||
phantom_index,
|
phantom_index,
|
||||||
phantom_indices);
|
phantom_indices);
|
||||||
}
|
}
|
||||||
|
|
||||||
return durations;
|
// TODO: re-enable this if we need to fallback
|
||||||
|
// if (calculate_distance)
|
||||||
|
if (false)
|
||||||
|
{
|
||||||
|
// 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -379,11 +511,13 @@ void forwardRoutingStep(const DataFacade<Algorithm> &facade,
|
|||||||
const std::vector<NodeBucket> &search_space_with_buckets,
|
const std::vector<NodeBucket> &search_space_with_buckets,
|
||||||
std::vector<EdgeWeight> &weights_table,
|
std::vector<EdgeWeight> &weights_table,
|
||||||
std::vector<EdgeDuration> &durations_table,
|
std::vector<EdgeDuration> &durations_table,
|
||||||
|
std::vector<NodeID> &middle_nodes_table,
|
||||||
const PhantomNode &phantom_node)
|
const PhantomNode &phantom_node)
|
||||||
{
|
{
|
||||||
const auto node = query_heap.DeleteMin();
|
const auto node = query_heap.DeleteMin();
|
||||||
const auto source_weight = query_heap.GetKey(node);
|
const auto source_weight = query_heap.GetKey(node);
|
||||||
const auto source_duration = query_heap.GetData(node).duration;
|
const auto source_duration = query_heap.GetData(node).duration;
|
||||||
|
const auto source_distance = query_heap.GetData(node).distance;
|
||||||
|
|
||||||
// Check if each encountered node has an entry
|
// Check if each encountered node has an entry
|
||||||
const auto &bucket_list = std::equal_range(search_space_with_buckets.begin(),
|
const auto &bucket_list = std::equal_range(search_space_with_buckets.begin(),
|
||||||
@@ -415,11 +549,12 @@ void forwardRoutingStep(const DataFacade<Algorithm> &facade,
|
|||||||
{
|
{
|
||||||
current_weight = new_weight;
|
current_weight = new_weight;
|
||||||
current_duration = new_duration;
|
current_duration = new_duration;
|
||||||
|
middle_nodes_table[location] = node;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
relaxOutgoingEdges<DIRECTION>(
|
relaxOutgoingEdges<DIRECTION>(
|
||||||
facade, node, source_weight, source_duration, query_heap, phantom_node);
|
facade, node, source_weight, source_duration, source_distance, query_heap, phantom_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <bool DIRECTION>
|
template <bool DIRECTION>
|
||||||
@@ -432,23 +567,247 @@ void backwardRoutingStep(const DataFacade<Algorithm> &facade,
|
|||||||
const auto node = query_heap.DeleteMin();
|
const auto node = query_heap.DeleteMin();
|
||||||
const auto target_weight = query_heap.GetKey(node);
|
const auto target_weight = query_heap.GetKey(node);
|
||||||
const auto target_duration = query_heap.GetData(node).duration;
|
const auto target_duration = query_heap.GetData(node).duration;
|
||||||
|
const auto target_distance = query_heap.GetData(node).distance;
|
||||||
|
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
|
// Store settled nodes in search space bucket
|
||||||
search_space_with_buckets.emplace_back(node, column_idx, target_weight, target_duration);
|
search_space_with_buckets.emplace_back(
|
||||||
|
node, parent, from_clique_arc, column_idx, target_weight, target_duration);
|
||||||
|
|
||||||
const auto &partition = facade.GetMultiLevelPartition();
|
const auto &partition = facade.GetMultiLevelPartition();
|
||||||
const auto maximal_level = partition.GetNumberOfLevels() - 1;
|
const auto maximal_level = partition.GetNumberOfLevels() - 1;
|
||||||
|
|
||||||
relaxOutgoingEdges<!DIRECTION>(
|
relaxOutgoingEdges<!DIRECTION>(facade,
|
||||||
facade, node, target_weight, target_duration, query_heap, phantom_node, maximal_level);
|
node,
|
||||||
|
target_weight,
|
||||||
|
target_duration,
|
||||||
|
target_distance,
|
||||||
|
query_heap,
|
||||||
|
phantom_node,
|
||||||
|
maximal_level);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <bool DIRECTION>
|
template <bool DIRECTION>
|
||||||
std::vector<EdgeDuration> manyToManySearch(SearchEngineData<Algorithm> &engine_working_data,
|
void retrievePackedPathFromSearchSpace(NodeID middle_node_id,
|
||||||
const DataFacade<Algorithm> &facade,
|
const unsigned column_idx,
|
||||||
const std::vector<PhantomNode> &phantom_nodes,
|
const std::vector<NodeBucket> &search_space_with_buckets,
|
||||||
const std::vector<std::size_t> &source_indices,
|
PackedPath &path)
|
||||||
const std::vector<std::size_t> &target_indices)
|
{
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
const auto number_of_sources = source_indices.size();
|
const auto number_of_sources = source_indices.size();
|
||||||
const auto number_of_targets = target_indices.size();
|
const auto number_of_targets = target_indices.size();
|
||||||
@@ -456,6 +815,8 @@ std::vector<EdgeDuration> manyToManySearch(SearchEngineData<Algorithm> &engine_w
|
|||||||
|
|
||||||
std::vector<EdgeWeight> weights_table(number_of_entries, INVALID_EDGE_WEIGHT);
|
std::vector<EdgeWeight> weights_table(number_of_entries, INVALID_EDGE_WEIGHT);
|
||||||
std::vector<EdgeDuration> durations_table(number_of_entries, MAXIMAL_EDGE_DURATION);
|
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;
|
std::vector<NodeBucket> search_space_with_buckets;
|
||||||
|
|
||||||
@@ -463,22 +824,22 @@ std::vector<EdgeDuration> manyToManySearch(SearchEngineData<Algorithm> &engine_w
|
|||||||
for (std::uint32_t column_idx = 0; column_idx < target_indices.size(); ++column_idx)
|
for (std::uint32_t column_idx = 0; column_idx < target_indices.size(); ++column_idx)
|
||||||
{
|
{
|
||||||
const auto index = target_indices[column_idx];
|
const auto index = target_indices[column_idx];
|
||||||
const auto &phantom = phantom_nodes[index];
|
const auto &target_phantom = phantom_nodes[index];
|
||||||
|
|
||||||
engine_working_data.InitializeOrClearManyToManyThreadLocalStorage(
|
engine_working_data.InitializeOrClearManyToManyThreadLocalStorage(
|
||||||
facade.GetNumberOfNodes(), facade.GetMaxBorderNodeID() + 1);
|
facade.GetNumberOfNodes(), facade.GetMaxBorderNodeID() + 1);
|
||||||
auto &query_heap = *(engine_working_data.many_to_many_heap);
|
auto &query_heap = *(engine_working_data.many_to_many_heap);
|
||||||
|
|
||||||
if (DIRECTION == FORWARD_DIRECTION)
|
if (DIRECTION == FORWARD_DIRECTION)
|
||||||
insertTargetInHeap(query_heap, phantom);
|
insertTargetInHeap(query_heap, target_phantom);
|
||||||
else
|
else
|
||||||
insertSourceInHeap(query_heap, phantom);
|
insertSourceInHeap(query_heap, target_phantom);
|
||||||
|
|
||||||
// explore search space
|
// explore search space
|
||||||
while (!query_heap.Empty())
|
while (!query_heap.Empty())
|
||||||
{
|
{
|
||||||
backwardRoutingStep<DIRECTION>(
|
backwardRoutingStep<DIRECTION>(
|
||||||
facade, column_idx, query_heap, search_space_with_buckets, phantom);
|
facade, column_idx, query_heap, search_space_with_buckets, target_phantom);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -488,18 +849,19 @@ std::vector<EdgeDuration> manyToManySearch(SearchEngineData<Algorithm> &engine_w
|
|||||||
// Find shortest paths from sources to all accessible nodes
|
// Find shortest paths from sources to all accessible nodes
|
||||||
for (std::uint32_t row_idx = 0; row_idx < source_indices.size(); ++row_idx)
|
for (std::uint32_t row_idx = 0; row_idx < source_indices.size(); ++row_idx)
|
||||||
{
|
{
|
||||||
const auto index = source_indices[row_idx];
|
const auto source_index = source_indices[row_idx];
|
||||||
const auto &phantom = phantom_nodes[index];
|
const auto &source_phantom = phantom_nodes[source_index];
|
||||||
|
|
||||||
// Clear heap and insert source nodes
|
// Clear heap and insert source nodes
|
||||||
engine_working_data.InitializeOrClearManyToManyThreadLocalStorage(
|
engine_working_data.InitializeOrClearManyToManyThreadLocalStorage(
|
||||||
facade.GetNumberOfNodes(), facade.GetMaxBorderNodeID() + 1);
|
facade.GetNumberOfNodes(), facade.GetMaxBorderNodeID() + 1);
|
||||||
|
|
||||||
auto &query_heap = *(engine_working_data.many_to_many_heap);
|
auto &query_heap = *(engine_working_data.many_to_many_heap);
|
||||||
|
|
||||||
if (DIRECTION == FORWARD_DIRECTION)
|
if (DIRECTION == FORWARD_DIRECTION)
|
||||||
insertSourceInHeap(query_heap, phantom);
|
insertSourceInHeap(query_heap, source_phantom);
|
||||||
else
|
else
|
||||||
insertTargetInHeap(query_heap, phantom);
|
insertTargetInHeap(query_heap, source_phantom);
|
||||||
|
|
||||||
// Explore search space
|
// Explore search space
|
||||||
while (!query_heap.Empty())
|
while (!query_heap.Empty())
|
||||||
@@ -512,11 +874,29 @@ std::vector<EdgeDuration> manyToManySearch(SearchEngineData<Algorithm> &engine_w
|
|||||||
search_space_with_buckets,
|
search_space_with_buckets,
|
||||||
weights_table,
|
weights_table,
|
||||||
durations_table,
|
durations_table,
|
||||||
phantom);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return durations_table;
|
return std::make_pair(durations_table, distances_table);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace mld
|
} // namespace mld
|
||||||
@@ -534,32 +914,54 @@ std::vector<EdgeDuration> manyToManySearch(SearchEngineData<Algorithm> &engine_w
|
|||||||
// then search is performed on a reversed graph with phantom nodes with flipped roles and
|
// then search is performed on a reversed graph with phantom nodes with flipped roles and
|
||||||
// returning a transposed matrix.
|
// returning a transposed matrix.
|
||||||
template <>
|
template <>
|
||||||
std::vector<EdgeDuration> manyToManySearch(SearchEngineData<mld::Algorithm> &engine_working_data,
|
std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
|
||||||
const DataFacade<mld::Algorithm> &facade,
|
manyToManySearch(SearchEngineData<mld::Algorithm> &engine_working_data,
|
||||||
const std::vector<PhantomNode> &phantom_nodes,
|
const DataFacade<mld::Algorithm> &facade,
|
||||||
const std::vector<std::size_t> &source_indices,
|
const std::vector<PhantomNode> &phantom_nodes,
|
||||||
const std::vector<std::size_t> &target_indices)
|
const std::vector<std::size_t> &source_indices,
|
||||||
|
const std::vector<std::size_t> &target_indices,
|
||||||
|
const bool calculate_distance,
|
||||||
|
const bool calculate_duration)
|
||||||
{
|
{
|
||||||
|
(void)calculate_duration; // flag stub to use for calculating distances in matrix in mld in the
|
||||||
|
// future
|
||||||
|
|
||||||
if (source_indices.size() == 1)
|
if (source_indices.size() == 1)
|
||||||
{ // TODO: check if target_indices.size() == 1 and do a bi-directional search
|
{ // TODO: check if target_indices.size() == 1 and do a bi-directional search
|
||||||
return mld::oneToManySearch<FORWARD_DIRECTION>(
|
return mld::oneToManySearch<FORWARD_DIRECTION>(engine_working_data,
|
||||||
engine_working_data, facade, phantom_nodes, source_indices.front(), target_indices);
|
facade,
|
||||||
|
phantom_nodes,
|
||||||
|
source_indices.front(),
|
||||||
|
target_indices,
|
||||||
|
calculate_distance);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target_indices.size() == 1)
|
if (target_indices.size() == 1)
|
||||||
{
|
{
|
||||||
return mld::oneToManySearch<REVERSE_DIRECTION>(
|
return mld::oneToManySearch<REVERSE_DIRECTION>(engine_working_data,
|
||||||
engine_working_data, facade, phantom_nodes, target_indices.front(), source_indices);
|
facade,
|
||||||
|
phantom_nodes,
|
||||||
|
target_indices.front(),
|
||||||
|
source_indices,
|
||||||
|
calculate_distance);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target_indices.size() < source_indices.size())
|
if (target_indices.size() < source_indices.size())
|
||||||
{
|
{
|
||||||
return mld::manyToManySearch<REVERSE_DIRECTION>(
|
return mld::manyToManySearch<REVERSE_DIRECTION>(engine_working_data,
|
||||||
engine_working_data, facade, phantom_nodes, target_indices, source_indices);
|
facade,
|
||||||
|
phantom_nodes,
|
||||||
|
target_indices,
|
||||||
|
source_indices,
|
||||||
|
calculate_distance);
|
||||||
}
|
}
|
||||||
|
|
||||||
return mld::manyToManySearch<FORWARD_DIRECTION>(
|
return mld::manyToManySearch<FORWARD_DIRECTION>(engine_working_data,
|
||||||
engine_working_data, facade, phantom_nodes, source_indices, target_indices);
|
facade,
|
||||||
|
phantom_nodes,
|
||||||
|
source_indices,
|
||||||
|
target_indices,
|
||||||
|
calculate_distance);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace routing_algorithms
|
} // namespace routing_algorithms
|
||||||
|
|||||||
@@ -59,6 +59,24 @@ 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.
|
// assumes that heaps are already setup correctly.
|
||||||
// ATTENTION: This only works if no additional offset is supplied next to the Phantom Node
|
// ATTENTION: This only works if no additional offset is supplied next to the Phantom Node
|
||||||
// Offsets.
|
// Offsets.
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user