Merge branch 'master' into sf-windows-node
This commit is contained in:
commit
4c492c024a
@ -12,6 +12,7 @@ Checks: >
|
||||
-bugprone-unhandled-self-assignment,
|
||||
-bugprone-forward-declaration-namespace,
|
||||
-bugprone-sizeof-expression,
|
||||
-bugprone-throw-keyword-missing,
|
||||
-clang-analyzer-*,
|
||||
-clang-diagnostic-deprecated-declarations,
|
||||
-clang-diagnostic-constant-conversion,
|
||||
@ -64,6 +65,7 @@ Checks: >
|
||||
-readability-else-after-return,
|
||||
-readability-inconsistent-declaration-parameter-name,
|
||||
-readability-isolate-declaration,
|
||||
-readability-identifier-length,
|
||||
-readability-redundant-declaration,
|
||||
-readability-uppercase-literal-suffix,
|
||||
-readability-named-parameter,
|
||||
|
||||
20
.github/workflows/osrm-backend.yml
vendored
20
.github/workflows/osrm-backend.yml
vendored
@ -329,16 +329,16 @@ jobs:
|
||||
# NODE_PACKAGE_TESTS_ONLY: ON
|
||||
|
||||
|
||||
- name: node-16-conan-linux-release
|
||||
build_node_package: true
|
||||
continue-on-error: false
|
||||
node: 16
|
||||
runs-on: ubuntu-20.04
|
||||
BUILD_TYPE: Release
|
||||
CLANG_VERSION: 6.0.0
|
||||
ENABLE_GLIBC_WORKAROUND: ON
|
||||
ENABLE_CONAN: ON
|
||||
NODE_PACKAGE_TESTS_ONLY: ON
|
||||
# - name: node-16-conan-linux-release
|
||||
# build_node_package: true
|
||||
# continue-on-error: false
|
||||
# node: 16
|
||||
# runs-on: ubuntu-20.04
|
||||
# BUILD_TYPE: Release
|
||||
# CLANG_VERSION: 6.0.0
|
||||
# ENABLE_GLIBC_WORKAROUND: ON
|
||||
# ENABLE_CONAN: ON
|
||||
# NODE_PACKAGE_TESTS_ONLY: ON
|
||||
|
||||
# - name: node-16-conan-linux-debug
|
||||
# build_node_package: true
|
||||
|
||||
10
CHANGELOG.md
10
CHANGELOG.md
@ -1,12 +1,17 @@
|
||||
# Unreleased
|
||||
- Changes from 5.26.0
|
||||
- API:
|
||||
- ADDED: Add Flatbuffers support to NodeJS bindings. [#6338](https://github.com/Project-OSRM/osrm-backend/pull/6338)
|
||||
- CHANGED: Add `data_version` field to responses of all services. [#5387](https://github.com/Project-OSRM/osrm-backend/pull/5387)
|
||||
- FIXED: Use Boost.Beast to parse HTTP request. [#6294](https://github.com/Project-OSRM/osrm-backend/pull/6294)
|
||||
- FIXED: Fix inefficient osrm-routed connection handling [#6113](https://github.com/Project-OSRM/osrm-backend/pull/6113)
|
||||
- FIXED: Fix HTTP compression precedence [#6113](https://github.com/Project-OSRM/osrm-backend/pull/6113)
|
||||
- NodeJS:
|
||||
- FIXED: Support `skip_waypoints` in Node bindings [#6060](https://github.com/Project-OSRM/osrm-backend/pull/6060)
|
||||
- Misc:
|
||||
- CHANGED: Optimize RestrictionParser performance. [#6344](https://github.com/Project-OSRM/osrm-backend/pull/6344)
|
||||
- ADDED: Support floats for speed value in traffic updates CSV. [#6327](https://github.com/Project-OSRM/osrm-backend/pull/6327)
|
||||
- CHANGED: Use Lua 5.4 in Docker image. [#6346](https://github.com/Project-OSRM/osrm-backend/pull/6346)
|
||||
- CHANGED: Remove redundant nullptr check. [#6326](https://github.com/Project-OSRM/osrm-backend/pull/6326)
|
||||
- CHANGED: missing files list is included in exception message. [#5360](https://github.com/Project-OSRM/osrm-backend/pull/5360)
|
||||
- CHANGED: Do not use deprecated Callback::Call overload in Node bindings. [#6318](https://github.com/Project-OSRM/osrm-backend/pull/6318)
|
||||
@ -17,6 +22,8 @@
|
||||
- FIXED: Bug in bicycle profile that caused exceptions if there is a highway=bicycle in the data. [#6296](https://github.com/Project-OSRM/osrm-backend/pull/6296)
|
||||
- FIXED: Internal refactoring of identifier types used in data facade [#6044](https://github.com/Project-OSRM/osrm-backend/pull/6044)
|
||||
- Build:
|
||||
- CHANGED: Use apt-get to install Clang on CI. [#6345](https://github.com/Project-OSRM/osrm-backend/pull/6345)
|
||||
- CHANGED: Fix TBB in case of Conan + NodeJS build. [#6333](https://github.com/Project-OSRM/osrm-backend/pull/6333)
|
||||
- CHANGED: Migrate to modern TBB version. [#6300](https://github.com/Project-OSRM/osrm-backend/pull/6300)
|
||||
- CHANGED: Enable performance-move-const-arg clang-tidy check. [#6319](https://github.com/Project-OSRM/osrm-backend/pull/6319)
|
||||
- CHANGED: Use the latest node on CI. [#6317](https://github.com/Project-OSRM/osrm-backend/pull/6317)
|
||||
@ -47,6 +54,9 @@
|
||||
- FIXED: Completed support for no_entry and no_exit turn restrictions. [#5988](https://github.com/Project-OSRM/osrm-backend/pull/5988)
|
||||
- ADDED: Add support for non-round-trips with a single fixed endpoint. [#6050](https://github.com/Project-OSRM/osrm-backend/pull/6050)
|
||||
- FIXED: Improvements to maneuver override processing [#6125](https://github.com/Project-OSRM/osrm-backend/pull/6125)
|
||||
- ADDED: Support snapping to multiple ways at an input location. [#5953](https://github.com/Project-OSRM/osrm-backend/pull/5953)
|
||||
- FIXED: Fix snapping target locations to ways used in turn restrictions. [#6339](https://github.com/Project-OSRM/osrm-backend/pull/6339)
|
||||
- ADDED: Support OSM traffic signal directions. [#6153](https://github.com/Project-OSRM/osrm-backend/pull/6153)
|
||||
|
||||
# 5.26.0
|
||||
- Changes from 5.25.0
|
||||
|
||||
@ -469,11 +469,11 @@ if(ENABLE_CONAN)
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/cmake/conan.cmake)
|
||||
|
||||
set(CONAN_BOOST_VERSION 1.79.0)
|
||||
set(CONAN_BZIP2_VERSION 1.0.8)
|
||||
set(CONAN_EXPAT_VERSION 2.2.10)
|
||||
set(CONAN_LUA_VERSION 5.4.4)
|
||||
set(CONAN_TBB_VERSION 2021.3.0)
|
||||
set(CONAN_BOOST_VERSION "1.79.0#96e4902111a2e343a8ba0aa95391bb58")
|
||||
set(CONAN_BZIP2_VERSION "1.0.8#d1b2d5816f25865acf978501dff1f897")
|
||||
set(CONAN_EXPAT_VERSION "2.2.10#916908d4a570ad839edd25322c3268cd")
|
||||
set(CONAN_LUA_VERSION "5.4.4#3ec62efc37cd0a5d80b9e5cb35277360")
|
||||
set(CONAN_TBB_VERSION "2021.3.0#507ec17cbd51a84167e143b20d170eea")
|
||||
|
||||
set(CONAN_SYSTEM_INCLUDES ON)
|
||||
|
||||
@ -488,11 +488,11 @@ if(ENABLE_CONAN)
|
||||
|
||||
set(CONAN_ARGS
|
||||
REQUIRES
|
||||
boost/${CONAN_BOOST_VERSION}
|
||||
bzip2/${CONAN_BZIP2_VERSION}
|
||||
expat/${CONAN_EXPAT_VERSION}
|
||||
lua/${CONAN_LUA_VERSION}
|
||||
onetbb/${CONAN_TBB_VERSION}
|
||||
"boost/${CONAN_BOOST_VERSION}"
|
||||
"bzip2/${CONAN_BZIP2_VERSION}"
|
||||
"expat/${CONAN_EXPAT_VERSION}"
|
||||
"lua/${CONAN_LUA_VERSION}"
|
||||
"onetbb/${CONAN_TBB_VERSION}"
|
||||
BASIC_SETUP
|
||||
GENERATORS cmake_find_package
|
||||
KEEP_RPATHS
|
||||
@ -515,7 +515,7 @@ if(ENABLE_CONAN)
|
||||
add_dependency_includes(${CONAN_INCLUDE_DIRS_TBB})
|
||||
|
||||
set(Boost_USE_STATIC_LIBS ON)
|
||||
find_package(Boost REQUIRED EXACT ${CONAN_BOOST_VERSION} COMPONENTS ${BOOST_COMPONENTS})
|
||||
find_package(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS})
|
||||
set(Boost_DATE_TIME_LIBRARY "${Boost_date_time_LIB_TARGETS}")
|
||||
set(Boost_CHRONO_LIBRARY "${Boost_chrono_LIB_TARGETS}")
|
||||
set(Boost_PROGRAM_OPTIONS_LIBRARY "${Boost_program_options_LIB_TARGETS}")
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
## Open Source Routing Machine
|
||||
|
||||
| Linux / macOS | Windows | Code Coverage |
|
||||
| ------------- | ------- | ------------- |
|
||||
| [](https://github.com/Project-OSRM/osrm-backend/actions/workflows/osrm-backend.yml) | [](https://ci.appveyor.com/project/DennisOSRM/osrm-backend) | [](https://codecov.io/gh/Project-OSRM/osrm-backend) |
|
||||
| Linux / macOS / Windows | Code Coverage |
|
||||
| ----------------------- | ------------- |
|
||||
| [](https://github.com/Project-OSRM/osrm-backend/actions/workflows/osrm-backend.yml) | [](https://codecov.io/gh/Project-OSRM/osrm-backend) |
|
||||
|
||||
High performance routing engine written in C++14 designed to run on OpenStreetMap data.
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@ RUN mkdir -p /src && mkdir -p /opt
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get -y --no-install-recommends install ca-certificates cmake make git gcc g++ libbz2-dev libxml2-dev wget \
|
||||
libzip-dev libboost1.74-all-dev lua5.2 liblua5.2-dev -o APT::Install-Suggests=0 -o APT::Install-Recommends=0
|
||||
libzip-dev libboost1.74-all-dev lua5.4 liblua5.4-dev -o APT::Install-Suggests=0 -o APT::Install-Recommends=0
|
||||
|
||||
RUN NPROC=${BUILD_CONCURRENCY:-$(nproc)} && \
|
||||
ldconfig /usr/local/lib && \
|
||||
@ -51,7 +51,7 @@ RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends libboost-program-options1.74.0 libboost-regex1.74.0 \
|
||||
libboost-date-time1.74.0 libboost-chrono1.74.0 libboost-filesystem1.74.0 \
|
||||
libboost-iostreams1.74.0 libboost-system1.74.0 libboost-thread1.74.0 \
|
||||
expat liblua5.2-0 && \
|
||||
expat liblua5.4-0 && \
|
||||
rm -rf /var/lib/apt/lists/* && \
|
||||
# add /usr/local/lib to ldconfig to allow loading libraries from there
|
||||
ldconfig /usr/local/lib
|
||||
|
||||
134
docs/http.md
134
docs/http.md
@ -1,7 +1,7 @@
|
||||
# OSRM HTTP server
|
||||
|
||||
Built-in HTTP server is a basic HTTP/1.0 server that supports 'keep-alive' extension. Persistent connections are limited to 512 requests per
|
||||
connection and allow no more then 5 seconds between requests.
|
||||
The built-in HTTP server is a basic HTTP/1.0 server that supports a 'keep-alive' extension. Persistent connections are limited to 512 requests per
|
||||
connection and allow no more than 5 seconds between requests.
|
||||
|
||||
## General options
|
||||
|
||||
@ -25,20 +25,20 @@ GET /{service}/{version}/{profile}/{coordinates}[.{format}]?option=value&option=
|
||||
|
||||
Passing any `option=value` is optional. `polyline` follows Google's polyline format with precision 5 by default and can be generated using [this package](https://www.npmjs.com/package/polyline).
|
||||
|
||||
To pass parameters to each location some options support an array like encoding:
|
||||
To pass parameters to each location some options support an array-like encoding:
|
||||
|
||||
**Request options**
|
||||
|
||||
| Option | Values | Description |
|
||||
|----------------|--------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
|bearings |`{bearing};{bearing}[;{bearing} ...]` |Limits the search to segments with given bearing in degrees towards true north in clockwise direction. |
|
||||
|bearings |`{bearing};{bearing}[;{bearing} ...]` |Limits the search to segments with given bearing in degrees towards true north in a clockwise direction. |
|
||||
|radiuses |`{radius};{radius}[;{radius} ...]` |Limits the search to given radius in meters. |
|
||||
|generate\_hints |`true` (default), `false` |Adds a Hint to the response which can be used in subsequent requests, see `hints` parameter. |
|
||||
|hints |`{hint};{hint}[;{hint} ...]` |Hint from previous request to derive position in street network. |
|
||||
|approaches |`{approach};{approach}[;{approach} ...]` |Keep waypoints on curbside. |
|
||||
|exclude |`{class}[,{class}]` |Additive list of classes to avoid, order does not matter. |
|
||||
|exclude |`{class}[,{class}]` |Additive list of classes to avoid, the order does not matter. |
|
||||
|snapping |`default` (default), `any` |Default snapping avoids is_startpoint (see profile) edges, `any` will snap to any edge in the graph |
|
||||
|skip_waypoints |`true`, `false` (default) |Removes waypoints from the response. Waypoints are still calculated, but not serialized. Could be useful in case you are interested in some other part of response and do not want to transfer waste data. |
|
||||
|skip_waypoints |`true`, `false` (default) |Removes waypoints from the response. Waypoints are still calculated, but not serialized. Could be useful in case you are interested in some other part of the response and do not want to transfer waste data. |
|
||||
|
||||
Where the elements follow the following format:
|
||||
|
||||
@ -56,7 +56,7 @@ Where the elements follow the following format:
|
||||
|
||||
The number of elements must match exactly the number of locations (except for `generate_hints` and `exclude`). If you don't want to pass a value but instead use the default you can pass an empty `element`.
|
||||
|
||||
Example: 2nd location use the default value for `option`:
|
||||
Example: 2nd location uses the default value for `option`:
|
||||
|
||||
```
|
||||
{option}={element};;{element}
|
||||
@ -88,17 +88,17 @@ Every response object has a `code` property containing one of the strings below
|
||||
| `InvalidService` | Service name is invalid. |
|
||||
| `InvalidVersion` | Version is not found. |
|
||||
| `InvalidOptions` | Options are invalid. |
|
||||
| `InvalidQuery` | The query string is synctactically malformed. |
|
||||
| `InvalidQuery` | The query string is syntactically malformed. |
|
||||
| `InvalidValue` | The successfully parsed query parameters are invalid. |
|
||||
| `NoSegment` | One of the supplied input coordinates could not snap to street segment. |
|
||||
| `TooBig` | The request size violates one of the service specific request size restrictions. |
|
||||
| `NoSegment` | One of the supplied input coordinates could not snap to the street segment. |
|
||||
| `TooBig` | The request size violates one of the service-specific request size restrictions. |
|
||||
|
||||
- `message` is a **optional** human-readable error message. All other status types are service dependent.
|
||||
- In case of an error the HTTP status code will be `400`. Otherwise the HTTP status code will be `200` and `code` will be `Ok`.
|
||||
- `message` is a **optional** human-readable error message. All other status types are service-dependent.
|
||||
- In case of an error the HTTP status code will be `400`. Otherwise, the HTTP status code will be `200` and `code` will be `Ok`.
|
||||
|
||||
#### Data version
|
||||
|
||||
Every response object has a `data_version` propetry containing timestamp from the original OpenStreetMap file. This field is optional. It can be ommited if data_version parametr was not set on osrm-extract stage or OSM file has not `osmosis_replication_timestamp` section.
|
||||
Every response object has a `data_version` property containing a timestamp from the original OpenStreetMap file. This field is optional. It can be omitted if the data_version parameter was not set on the osrm-extract stage or the OSM file has not `osmosis_replication_timestamp` section.
|
||||
|
||||
#### Example response
|
||||
|
||||
@ -129,8 +129,8 @@ In addition to the [general options](#general-options) the following options are
|
||||
|------------|------------------------------|----------------------------------------------------|
|
||||
|number |`integer >= 1` (default `1`) |Number of nearest segments that should be returned. |
|
||||
|
||||
As `waypoints` is a single thing, returned byt that service, using it with option `skip_waypoints` set to `true` is quite useless, but still
|
||||
possible. In that case only `code` field will be returned.
|
||||
As `waypoints` is a single thing, returned by that service, using it with the option `skip_waypoints` set to `true` is quite useless, but still
|
||||
possible. In that case, only the `code` field will be returned.
|
||||
|
||||
**Response**
|
||||
|
||||
@ -147,7 +147,7 @@ curl 'http://router.project-osrm.org/nearest/v1/driving/13.388860,52.517037?numb
|
||||
|
||||
#### Example Response
|
||||
|
||||
```json
|
||||
```JSON
|
||||
{
|
||||
"waypoints" : [
|
||||
{
|
||||
@ -239,7 +239,7 @@ curl 'http://router.project-osrm.org/route/v1/driving/13.388860,52.517037;13.397
|
||||
|
||||
### Table service
|
||||
|
||||
Computes the duration of the fastest route between all pairs of supplied coordinates. Returns the durations or distances or both between the coordinate pairs. Note that the distances are not the shortest distance between two coordinates, but rather the distances of the fastest routes. Duration is in seconds and distances is in meters.
|
||||
Computes the duration of the fastest route between all pairs of supplied coordinates. Returns 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. Durations are in seconds and distances are in meters.
|
||||
|
||||
```endpoint
|
||||
GET /table/v1/{profile}/{coordinates}?{sources}=[{elem}...];&{destinations}=[{elem}...]&annotations={duration|distance|duration,distance}
|
||||
@ -417,7 +417,7 @@ All other properties might be undefined.
|
||||
### Match service
|
||||
|
||||
Map matching matches/snaps given GPS points to the road network in the most plausible way.
|
||||
Please note the request might result multiple sub-traces. Large jumps in the timestamps (> 60s) or improbable transitions lead to trace splits if a complete matching could not be found.
|
||||
Please note the request might result in multiple sub-traces. Large jumps in the timestamps (> 60s) or improbable transitions lead to trace splits if a complete matching could not be found.
|
||||
The algorithm might not be able to match all points. Outliers are removed if they can not be matched successfully.
|
||||
|
||||
```endpoint
|
||||
@ -452,11 +452,11 @@ The area to search is chosen such that the correct candidate should be considere
|
||||
|
||||
- `code` if the request was successful `Ok` otherwise see the service dependent and general status codes.
|
||||
- `tracepoints`: Array of `Waypoint` objects representing all points of the trace in order.
|
||||
If the trace point was ommited by map matching because it is an outlier, the entry will be `null`.
|
||||
If the tracepoint was omitted by map matching because it is an outlier, the entry will be `null`.
|
||||
Each `Waypoint` object has the following additional properties:
|
||||
- `matchings_index`: Index to the `Route` object in `matchings` the sub-trace was matched to.
|
||||
- `waypoint_index`: Index of the waypoint inside the matched route.
|
||||
- `alternatives_count`: Number of probable alternative matchings for this trace point. A value of zero indicate that this point was matched unambiguously. Split the trace at these points for incremental map matching.
|
||||
- `alternatives_count`: Number of probable alternative matchings for this tracepoint. A value of zero indicates that this point was matched unambiguously. Split the trace at these points for incremental map matching.
|
||||
- `matchings`: An array of `Route` objects that assemble the trace. Each `Route` object has the following additional properties:
|
||||
- `confidence`: Confidence of the matching. `float` value between 0 and 1. 1 is very confident that the matching is correct.
|
||||
|
||||
@ -471,7 +471,7 @@ All other properties might be undefined.
|
||||
### Trip service
|
||||
|
||||
The trip plugin solves the Traveling Salesman Problem using a greedy heuristic (farthest-insertion algorithm) for 10 or more waypoints and uses brute force for less than 10 waypoints.
|
||||
The returned path does not have to be the fastest path. As TSP is NP-hard it only returns an approximation.
|
||||
The returned path does not have to be the fastest one. As TSP is NP-hard it only returns an approximation.
|
||||
Note that all input coordinates have to be connected for the trip service to work.
|
||||
|
||||
```endpoint
|
||||
@ -492,12 +492,12 @@ In addition to the [general options](#general-options) the following options are
|
||||
|
||||
**Fixing Start and End Points**
|
||||
|
||||
It is possible to explicitely set the start or end coordinate of the trip.
|
||||
When source is set to `first`, the first coordinate is used as start coordinate of the trip in the output. When destination is set to `last`, the last coordinate will be used as destination of the trip in the returned output. If you specify `any`, any of the coordinates can be used as the first or last coordinate in the output.
|
||||
It is possible to explicitly set the start or end coordinate of the trip.
|
||||
When the source is set to `first`, the first coordinate is used as the start coordinate of the trip in the output. When the destination is set to `last`, the last coordinate will be used as the destination of the trip in the returned output. If you specify `any`, any of the coordinates can be used as the first or last coordinate in the output.
|
||||
|
||||
However, if `source=any&destination=any` the returned round-trip will still start at the first input coordinate by default.
|
||||
|
||||
Currently, not all combinations of `roundtrip`, `source` and `destination` are supported.
|
||||
Currently, not all combinations of `roundtrip`, `source`, and `destination` are supported.
|
||||
Right now, the following combinations are possible:
|
||||
|
||||
| roundtrip | source | destination | supported |
|
||||
@ -576,7 +576,7 @@ Vector tiles contain two layers:
|
||||
| `duration` | `float` | how long this segment takes to traverse, in seconds. This value is to calculate the total route ETA. |
|
||||
| `weight ` | `integer` | how long this segment takes to traverse, in units (may differ from `duration` when artificial biasing is applied in the Lua profiles). ACTUAL ROUTING USES THIS VALUE. |
|
||||
| `name` | `string` | the name of the road this segment belongs to |
|
||||
| `rate` | `float` | the value of `length/weight` - analagous to `speed`, but using the `weight` value rather than `duration`, rounded to the nearest integer |
|
||||
| `rate` | `float` | the value of `length/weight` - analogous to `speed`, but using the `weight` value rather than `duration`, rounded to the nearest integer |
|
||||
| `is_startpoint` | `boolean` | whether this segment can be used as a start/endpoint for routes |
|
||||
|
||||
`turns` layer:
|
||||
@ -601,13 +601,13 @@ Represents a route through (potentially multiple) waypoints.
|
||||
|
||||
- `distance`: The distance traveled by the route, in `float` meters.
|
||||
- `duration`: The estimated travel time, in `float` number of seconds.
|
||||
- `geometry`: The whole geometry of the route value depending on `overview` parameter, format depending on the `geometries` parameter. See `RouteStep`'s `geometry` property for a parameter documentation.
|
||||
- `geometry`: The whole geometry of the route value depending on `overview` parameter, format depending on the `geometries` parameter. See `RouteStep`'s `geometry` property for the parameter documentation.
|
||||
- `weight`: The calculated weight of the route.
|
||||
- `weight_name`: The name of the weight profile used during extraction phase.
|
||||
- `weight_name`: The name of the weight profile used during the extraction phase.
|
||||
|
||||
| overview | Description |
|
||||
|------------|-----------------------------|
|
||||
| simplified | Geometry is simplified according to the highest zoom level it can still be displayed on full. |
|
||||
| simplified | Geometry is simplified according to the highest zoom level it can still be displayed in full. |
|
||||
| full | Geometry is not simplified. |
|
||||
| false | Geometry is not added. |
|
||||
|
||||
@ -652,7 +652,7 @@ Represents a route between two waypoints.
|
||||
|
||||
| summary | |
|
||||
|--------------|-----------------------------------------------------------------------|
|
||||
| true | Names of the two major roads used. Can be empty if route is too short.|
|
||||
| true | Names of the two major roads used. Can be empty if the route is too short.|
|
||||
| false | empty `string` |
|
||||
|
||||
- `steps`: Depends on the `steps` parameter.
|
||||
@ -662,11 +662,11 @@ Represents a route between two waypoints.
|
||||
| true | array of `RouteStep` objects describing the turn-by-turn instructions |
|
||||
| false | empty array |
|
||||
|
||||
- `annotation`: Additional details about each coordinate along the route geometry:
|
||||
- `annotation`: Additional details about each coordinate along with the route geometry:
|
||||
|
||||
| annotations | |
|
||||
|--------------|-------------------------------------------------------------------------------|
|
||||
| true | An `Annotation` object containing node ids, durations, distances and weights. |
|
||||
| true | An `Annotation` object containing node ids, durations, distances, and weights. |
|
||||
| false | `undefined` |
|
||||
|
||||
#### Example
|
||||
@ -696,7 +696,7 @@ Annotation of the whole route leg with fine-grained information about each segme
|
||||
|
||||
**Properties**
|
||||
|
||||
- `distance`: The distance, in metres, between each pair of coordinates
|
||||
- `distance`: The distance, in meters, between each pair of coordinates
|
||||
- `duration`: The duration between each pair of coordinates, in seconds. Does not include the duration of any turns.
|
||||
- `datasources`: The index of the data source for the speed between each pair of coordinates. `0` is the default profile, other values are supplied via `--segment-speed-file` to `osrm-contract` or `osrm-customize`. String-like names are in the `metadata.datasource_names` array.
|
||||
- `nodes`: The OSM node ID for each coordinate along the route, excluding the first/last user-supplied coordinates
|
||||
@ -803,11 +803,11 @@ step.
|
||||
- `bearing_after`: The clockwise angle from true north to the
|
||||
direction of travel immediately after the maneuver. Range 0-359.
|
||||
- `type` A string indicating the type of maneuver. **new identifiers might be introduced without API change**
|
||||
Types unknown to the client should be handled like the `turn` type, the existence of correct `modifier` values is guranteed.
|
||||
Types unknown to the client should be handled like the `turn` type, the existence of correct `modifier` values is guaranteed.
|
||||
|
||||
| `type` | Description |
|
||||
|------------------|--------------------------------------------------------------|
|
||||
| `turn` | a basic turn into direction of the `modifier` |
|
||||
| `turn` | a basic turn into the direction of the `modifier` |
|
||||
| `new name` | no turn is taken/possible, but the road name changes. The road can take a turn itself, following `modifier`. |
|
||||
| `depart` | indicates the departure of the leg |
|
||||
| `arrive` | indicates the destination of the leg |
|
||||
@ -821,9 +821,9 @@ step.
|
||||
| `continue` | Turn in direction of `modifier` to stay on the same road |
|
||||
| `roundabout` | traverse roundabout, if the route leaves the roundabout there will be an additional property `exit` for exit counting. The modifier specifies the direction of entering the roundabout. |
|
||||
| `rotary` | a traffic circle. While very similar to a larger version of a roundabout, it does not necessarily follow roundabout rules for right of way. It can offer `rotary_name` and/or `rotary_pronunciation` parameters (located in the RouteStep object) in addition to the `exit` parameter (located on the StepManeuver object). |
|
||||
| `roundabout turn`| Describes a turn at a small roundabout that should be treated as normal turn. The `modifier` indicates the turn direciton. Example instruction: `At the roundabout turn left`. |
|
||||
| `roundabout turn`| Describes a turn at a small roundabout that should be treated as a normal turn. The `modifier` indicates the turn direction. Example instruction: `At the roundabout turn left`. |
|
||||
| `notification` | not an actual turn but a change in the driving conditions. For example the travel mode or classes. If the road takes a turn itself, the `modifier` describes the direction |
|
||||
| `exit roundabout`| Describes a maneuver exiting a roundabout (usually preceeded by a `roundabout` instruction) |
|
||||
| `exit roundabout`| Describes a maneuver exiting a roundabout (usually preceded by a `roundabout` instruction) |
|
||||
| `exit rotary` | Describes the maneuver exiting a rotary (large named roundabout) |
|
||||
|
||||
Please note that even though there are `new name` and `notification` instructions, the `mode` and `name` can change
|
||||
@ -833,7 +833,7 @@ step.
|
||||
|
||||
| `modifier` | Description |
|
||||
|-------------------|-------------------------------------------|
|
||||
| `uturn` | indicates reversal of direction |
|
||||
| `uturn` | indicates the reversal of direction |
|
||||
| `sharp right` | a sharp right turn |
|
||||
| `right` | a normal turn to the right |
|
||||
| `slight right` | a slight turn to the right |
|
||||
@ -851,8 +851,8 @@ step.
|
||||
| `turn` | `modifier` indicates the change in direction accomplished through the turn |
|
||||
| `depart`/`arrive` | `modifier` indicates the position of departure point and arrival point in relation to the current direction of travel |
|
||||
|
||||
- `exit` An optional `integer` indicating number of the exit to take. The property exists for the `roundabout` / `rotary` property:
|
||||
Number of the roundabout exit to take. If exit is `undefined` the destination is on the roundabout.
|
||||
- `exit` An optional `integer` indicating the number of the exit to take. The property exists for the `roundabout` / `rotary` property:
|
||||
Number of the roundabout exit to take. If an exit is `undefined` the destination is on the roundabout.
|
||||
|
||||
|
||||
New properties (potentially depending on `type`) may be introduced in the future without an API version change.
|
||||
@ -863,7 +863,7 @@ A `Lane` represents a turn lane at the corresponding turn location.
|
||||
|
||||
**Properties**
|
||||
|
||||
- `indications`: a indication (e.g. marking on the road) specifying the turn lane. A road can have multiple indications (e.g. an arrow pointing straight and left). The indications are given in an array, each containing one of the following types. Further indications might be added on without an API version change.
|
||||
- `indications`: an indication (e.g. marking on the road) specifying the turn lane. A road can have multiple indications (e.g. an arrow pointing straight and left). The indications are given in an array, each containing one of the following types. Further indications might be added on without an API version change.
|
||||
|
||||
| `value` | Description |
|
||||
|------------------------|---------------------------------------------------------------------------------------------------------------------------|
|
||||
@ -890,7 +890,7 @@ A `Lane` represents a turn lane at the corresponding turn location.
|
||||
|
||||
### Intersection object
|
||||
|
||||
An intersection gives a full representation of any cross-way the path passes bay. For every step, the very first intersection (`intersections[0]`) corresponds to the
|
||||
An intersection gives a full representation of any cross-way the path passes by. For every step, the very first intersection (`intersections[0]`) corresponds to the
|
||||
location of the StepManeuver. Further intersections are listed for every cross-way until the next turn instruction.
|
||||
|
||||
**Properties**
|
||||
@ -926,15 +926,15 @@ location of the StepManeuver. Further intersections are listed for every cross-w
|
||||
|
||||
### Waypoint object
|
||||
|
||||
Object used to describe waypoint on a route.
|
||||
The object is used to describe the waypoint on a route.
|
||||
|
||||
**Properties**
|
||||
|
||||
- `name` Name of the street the coordinate snapped to
|
||||
- `location` Array that contains the `[longitude, latitude]` pair of the snapped coordinate
|
||||
- `distance` The distance, in metres, from the input coordinate to the snapped coordinate
|
||||
- `distance` The distance, in meters, from the input coordinate to the snapped coordinate
|
||||
- `hint` Unique internal identifier of the segment (ephemeral, not constant over data updates)
|
||||
This can be used on subsequent request to significantly speed up the query and to connect multiple services.
|
||||
This can be used on subsequent requests to significantly speed up the query and to connect multiple services.
|
||||
E.g. you can use the `hint` value obtained by the `nearest` query as `hint` values for `route` inputs.
|
||||
|
||||
#### Example
|
||||
@ -953,14 +953,14 @@ Object used to describe waypoint on a route.
|
||||
|
||||
## Flatbuffers format
|
||||
|
||||
Default response format is `json`, but OSRM supports binary [`flatbuffers`](https://google.github.io/flatbuffers/) format, which
|
||||
The default response format is `json`, but OSRM supports binary [`flatbuffers`](https://google.github.io/flatbuffers/) format, which
|
||||
is much faster in serialization/deserialization, comparing to `json`.
|
||||
|
||||
The format itself is described in message descriptors, located at `include/engine/api/flatbuffers directory`. Those descriptors could
|
||||
The format itself is described in message descriptors, located at `include/engine/api/flatbuffers` directory. Those descriptors could
|
||||
be compiled to provide protocol parsers in Go/Javascript/Typescript/Java/Dart/C#/Python/Lobster/Lua/Rust/PHP/Kotlin. Precompiled
|
||||
protocol parser for C++ is supplied with OSRM.
|
||||
|
||||
`Flatbuffers` format provides exactly same data, as `json` format with a slightly different layout, which was optimized to minimize
|
||||
`Flatbuffers` format provides exactly the same data, as `json` format with a slightly different layout, which was optimized to minimize
|
||||
in-transfer size.
|
||||
|
||||
### Root object
|
||||
@ -971,7 +971,7 @@ Root object is the only object, available from a 'raw' `flatbuffers` buffer. It
|
||||
|
||||
**Properties**
|
||||
|
||||
- `error`: `bool` Marks response as erroneous. Erroneus response should include `code` field set, all the other field may not present.
|
||||
- `error`: `bool` Marks response as erroneous. An erroneous response should include the `code` fieldset, all the other fields may not be present.
|
||||
- `code`: `Error` Error description object, only present, when `error` is `true`
|
||||
- `waypoints`: `[Waypoint]` Array of `Waypoint` objects. Should present for every service call, unless `skip_waypoints` is set to `true`. Table service will put `sources` array here.
|
||||
- `routes`: `[RouteObject]` Array of `RouteObject` objects. May be empty or absent. Should present for Route/Trip/Match services call.
|
||||
@ -988,14 +988,14 @@ Contains error information.
|
||||
|
||||
### Waypoint object
|
||||
|
||||
Almost same as `json` Waypoint object. The following properties differ:
|
||||
Almost the same as `json` Waypoint object. The following properties differ:
|
||||
|
||||
- `location`: `Position` Same as `json` location field, but different format.
|
||||
- `nodes`: `Uint64Pair` Same as `json` nodes field, but different format.
|
||||
|
||||
### RouteObject object
|
||||
|
||||
Almost same as `json` Route object. The following properties differ:
|
||||
Almost the same as `json` Route object. The following properties differ:
|
||||
|
||||
- `polyline`: `string` Same as `json` geometry.polyline or geometry.polyline6 fields. One field for both formats.
|
||||
- `coordinates`: `[Position]` Same as `json` geometry.coordinates field, but different format.
|
||||
@ -1003,14 +1003,14 @@ Almost same as `json` Route object. The following properties differ:
|
||||
|
||||
### Leg object
|
||||
|
||||
Almost same as `json` Leg object. The following properties differ:
|
||||
Almost the same as `json` Leg object. The following properties differ:
|
||||
|
||||
- `annotations`: `Annotation` Same as `json` annotation field, but different format.
|
||||
- `steps`: `[Step]` Same as `step` annotation field, but different format.
|
||||
|
||||
### Step object
|
||||
|
||||
Almost same as `json` Step object. The following properties differ:
|
||||
Almost the same as `json` Step object. The following properties differ:
|
||||
|
||||
- `polyline`: `string` Same as `json` geometry.polyline or geometry.polyline6 fields. One field for both formats.
|
||||
- `coordinates`: `[Position]` Same as `json` geometry.coordinates field, but different format.
|
||||
@ -1018,7 +1018,7 @@ Almost same as `json` Step object. The following properties differ:
|
||||
|
||||
| `type` | Description |
|
||||
|------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `Turn` | a basic turn into direction of the `modifier` |
|
||||
| `Turn` | a basic turn into the direction of the `modifier` |
|
||||
| `NewName` | no turn is taken/possible, but the road name changes. The road can take a turn itself, following `modifier`. |
|
||||
| `Depart` | indicates the departure of the leg |
|
||||
| `Arrive` | indicates the destination of the leg |
|
||||
@ -1030,9 +1030,9 @@ Almost same as `json` Step object. The following properties differ:
|
||||
| `Continue` | Turn in direction of `modifier` to stay on the same road |
|
||||
| `Roundabout` | traverse roundabout, if the route leaves the roundabout there will be an additional property `exit` for exit counting. The modifier specifies the direction of entering the roundabout. |
|
||||
| `Rotary` | a traffic circle. While very similar to a larger version of a roundabout, it does not necessarily follow roundabout rules for right of way. It can offer `rotary_name` and/or `rotary_pronunciation` parameters (located in the RouteStep object) in addition to the `exit` parameter (located on the StepManeuver object). |
|
||||
| `RoundaboutTurn` | Describes a turn at a small roundabout that should be treated as normal turn. The `modifier` indicates the turn direciton. Example instruction: `At the roundabout turn left`. |
|
||||
| `RoundaboutTurn` | Describes a turn at a small roundabout that should be treated as a normal turn. The `modifier` indicates the turn direction. Example instruction: `At the roundabout turn left`. |
|
||||
| `Notification` | not an actual turn but a change in the driving conditions. For example the travel mode or classes. If the road takes a turn itself, the `modifier` describes the direction |
|
||||
| `ExitRoundabout` | Describes a maneuver exiting a roundabout (usually preceeded by a `roundabout` instruction) |
|
||||
| `ExitRoundabout` | Describes a maneuver exiting a roundabout (usually preceded by a `roundabout` instruction) |
|
||||
| `ExitRotary` | Describes the maneuver exiting a rotary (large named roundabout) |
|
||||
|
||||
- `driving_side`: `bool` Ttrue stands for the left side driving.
|
||||
@ -1040,14 +1040,14 @@ Almost same as `json` Step object. The following properties differ:
|
||||
|
||||
### Intersection object
|
||||
|
||||
Almost same as `json` Intersection object. The following properties differ:
|
||||
Almost the same as `json` Intersection object. The following properties differ:
|
||||
|
||||
- `location`: `Position` Same as `json` location property, but in different format.
|
||||
- `location`: `Position` Same as `json` location property, but in a different format.
|
||||
- `lanes`: `[Lane]` Array of `Lane` objects.
|
||||
|
||||
### Lane object
|
||||
|
||||
Almost same as `json` Lane object. The following properties differ:
|
||||
Almost the same as `json` Lane object. The following properties differ:
|
||||
|
||||
- `indications`: `Turn` Array of `Turn` enum values.
|
||||
|
||||
@ -1065,14 +1065,14 @@ Almost same as `json` Lane object. The following properties differ:
|
||||
|
||||
### StepManeuver object
|
||||
|
||||
Almost same as `json` StepManeuver object. The following properties differ:
|
||||
Almost the same as `json` StepManeuver object. The following properties differ:
|
||||
|
||||
- `location`: `Position` Same as `json` location property, but in different format.
|
||||
- `location`: `Position` Same as `json` location property, but in a different format.
|
||||
- `type`: `ManeuverType` Type of a maneuver (enum)
|
||||
|
||||
| `type` | Description |
|
||||
|------------------|--------------------------------------------------------------|
|
||||
| `Turn` | a basic turn into direction of the `modifier` |
|
||||
| `Turn` | a basic turn into the direction of the `modifier` |
|
||||
| `NewName` | no turn is taken/possible, but the road name changes. The road can take a turn itself, following `modifier`. |
|
||||
| `Depart` | indicates the departure of the leg |
|
||||
| `Arrive` | indicates the destination of the leg |
|
||||
@ -1084,16 +1084,16 @@ Almost same as `json` StepManeuver object. The following properties differ:
|
||||
| `Continue` | Turn in direction of `modifier` to stay on the same road |
|
||||
| `Roundabout` | traverse roundabout, if the route leaves the roundabout there will be an additional property `exit` for exit counting. The modifier specifies the direction of entering the roundabout. |
|
||||
| `Rotary` | a traffic circle. While very similar to a larger version of a roundabout, it does not necessarily follow roundabout rules for right of way. It can offer `rotary_name` and/or `rotary_pronunciation` parameters (located in the RouteStep object) in addition to the `exit` parameter (located on the StepManeuver object). |
|
||||
| `RoundaboutTurn` | Describes a turn at a small roundabout that should be treated as normal turn. The `modifier` indicates the turn direciton. Example instruction: `At the roundabout turn left`. |
|
||||
| `RoundaboutTurn` | Describes a turn at a small roundabout that should be treated as a normal turn. The `modifier` indicates the turn direction. Example instruction: `At the roundabout turn left`. |
|
||||
| `Notification` | not an actual turn but a change in the driving conditions. For example the travel mode or classes. If the road takes a turn itself, the `modifier` describes the direction |
|
||||
| `ExitRoundabout` | Describes a maneuver exiting a roundabout (usually preceeded by a `roundabout` instruction) |
|
||||
| `ExitRoundabout` | Describes a maneuver exiting a roundabout (usually preceded by a `roundabout` instruction) |
|
||||
| `ExitRotary` | Describes the maneuver exiting a rotary (large named roundabout) |
|
||||
|
||||
- `modifier`: `Turn` Maneuver turn (enum)
|
||||
|
||||
### Annotation object
|
||||
|
||||
Exactly same as `json` annotation object.
|
||||
Exactly the same as `json` annotation object.
|
||||
|
||||
|
||||
### Position object
|
||||
@ -1114,11 +1114,11 @@ A pair of long long integers. Used only by `Waypoint` object.
|
||||
|
||||
### Table object
|
||||
|
||||
Almost same as `json` Table object. The main difference is that 'sources' field is absent and root's object 'waypoints' field is
|
||||
Almost the same as `json` Table object. The main difference is that 'sources' field is absent and the root's object 'waypoints' field is
|
||||
used instead. All the other differences follow:
|
||||
|
||||
- `durations`: `[float]` Flat representation of a durations matrix. Element at row;col can be adressed as [row * cols + col]
|
||||
- `distances`: `[float]` Flat representation of a destinations matrix. Element at row;col can be adressed as [row * cols + col]
|
||||
- `durations`: `[float]` Flat representation of a durations matrix. Element at row;col can be addressed as [row * cols + col]
|
||||
- `distances`: `[float]` Flat representation of a destinations matrix. Element at row;col can be addressed as [row * cols + col]
|
||||
- `destinations`: `[Waypoint]` Array of `Waypoint` objects. Will be `null` if `skip_waypoints` will be set to `true`
|
||||
- `rows`: `ushort` Number of rows in durations/destinations matrices.
|
||||
- `cols`: `ushort` Number of cols in durations/destinations matrices.
|
||||
|
||||
@ -51,6 +51,7 @@ Returns the fastest route between two or more coordinates while visiting the way
|
||||
Can be `null` or an array of `[{value},{range}]` with `integer 0 .. 360,integer 0 .. 180`.
|
||||
- `options.radiuses` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)?** Limits the coordinate snapping to streets in the given radius in meters. Can be `null` (unlimited, default) or `double >= 0`.
|
||||
- `options.hints` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)?** Hints for the coordinate snapping. Array of base64 encoded strings.
|
||||
- `options.exclude` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)?** List of classes to avoid, order does not matter.
|
||||
- `options.generate_hints` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** Whether or not adds a Hint to the response which can be used in subsequent requests. (optional, default `true`)
|
||||
- `options.alternatives` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** Search for alternative routes. (optional, default `false`)
|
||||
- `options.alternatives` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** Search for up to this many alternative routes.
|
||||
@ -63,7 +64,9 @@ Returns the fastest route between two or more coordinates while visiting the way
|
||||
- `options.approaches` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)?** Keep waypoints on curb side. Can be `null` (unrestricted, default) or `curb`.
|
||||
`null`/`true`/`false`
|
||||
- `options.waypoints` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)?** Indices to coordinates to treat as waypoints. If not supplied, all coordinates are waypoints. Must include first and last coordinate index.
|
||||
- `options.format` **[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** Which output format to use, either `json`, or [`flatbuffers`](https://github.com/Project-OSRM/osrm-backend/tree/master/include/engine/api/flatbuffers).
|
||||
- `options.snapping` **[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** Which edges can be snapped to, either `default`, or `any`. `default` only snaps to edges marked by the profile as `is_startpoint`, `any` will allow snapping to any edge in the routing graph.
|
||||
- `options.skip_waypoints` **[Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** Removes waypoints from the response. Waypoints are still calculated, but not serialized. Could be useful in case you are interested in some other part of response and do not want to transfer waste data. (optional, default `false`)
|
||||
- `callback` **[Function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function)**
|
||||
|
||||
**Examples**
|
||||
@ -97,6 +100,7 @@ Note: `coordinates` in the general options only supports a single `{longitude},{
|
||||
- `options.number` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** Number of nearest segments that should be returned.
|
||||
Must be an integer greater than or equal to `1`. (optional, default `1`)
|
||||
- `options.approaches` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)?** Keep waypoints on curb side. Can be `null` (unrestricted, default) or `curb`.
|
||||
- `options.format` **[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** Which output format to use, either `json`, or [`flatbuffers`](https://github.com/Project-OSRM/osrm-backend/tree/master/include/engine/api/flatbuffers).
|
||||
- `options.snapping` **[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** Which edges can be snapped to, either `default`, or `any`. `default` only snaps to edges marked by the profile as `is_startpoint`, `any` will allow snapping to any edge in the routing graph.
|
||||
- `callback` **[Function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function)**
|
||||
|
||||
@ -332,12 +336,15 @@ specific behaviours.
|
||||
|
||||
- `plugin_config` **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)?** Object literal containing parameters for the trip query.
|
||||
- `plugin_config.format` **[String](https://developer.mozilla.org/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.
|
||||
Valid options are `object` (default if `options.format` is
|
||||
`json`), which returns a standard Javascript object, as described above, and `buffer`(default if
|
||||
`options.format` is `flatbuffers`), which will return a NodeJS
|
||||
**[Buffer](https://nodejs.org/api/buffer.html)** object, containing a JSON string or Flatbuffers
|
||||
object. 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. Also note that `options.format` set to `flatbuffers`
|
||||
cannot be used with `plugin_config.format` set to `object`. `json_buffer` is deprecated alias for
|
||||
`buffer`.
|
||||
|
||||
**Examples**
|
||||
|
||||
@ -349,7 +356,7 @@ var options = {
|
||||
[13.374481201171875, 52.506191342034576]
|
||||
]
|
||||
};
|
||||
osrm.route(options, { format: "json_buffer" }, function(err, response) {
|
||||
osrm.route(options, { format: "buffer" }, function(err, response) {
|
||||
if (err) throw err;
|
||||
console.log(response.toString("utf-8"));
|
||||
});
|
||||
|
||||
@ -28,7 +28,7 @@ Feature: Bicycle - Route around alleys
|
||||
|
||||
When I route I should get
|
||||
| from | to | a:nodes | weight | # |
|
||||
| a | f | 1:2:3:6 | 200.4 | Avoids d,e,f |
|
||||
| a | e | 1:2:5 | 176.4 | Take the alley b,e if neccessary |
|
||||
| d | f | 4:1:2:3:6 | 252.6 | Avoids the alley d,e,f |
|
||||
| a | f | 1:2:3:6 | 196.2 | Avoids d,e,f |
|
||||
| a | e | 1:2:5 | 172.2 | Take the alley b,e if neccessary |
|
||||
| d | f | 4:1:2:3:6 | 248.4 | Avoids the alley d,e,f |
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ Feature: Car - Destination only, no passing through
|
||||
Given the node map
|
||||
"""
|
||||
a e
|
||||
b c d
|
||||
b1 c 2d
|
||||
|
||||
x y
|
||||
"""
|
||||
@ -23,19 +23,19 @@ Feature: Car - Destination only, no passing through
|
||||
When I route I should get
|
||||
| from | to | route |
|
||||
| a | b | ab,ab |
|
||||
| a | c | ab,bcd |
|
||||
| a | d | ab,bcd,bcd |
|
||||
| a | c | ab,bcd,bcd |
|
||||
| a | 2 | ab,bcd,bcd |
|
||||
| a | e | axye,axye |
|
||||
| e | d | de,de |
|
||||
| e | c | de,bcd |
|
||||
| e | b | de,bcd,bcd |
|
||||
| e | c | de,bcd,bcd |
|
||||
| e | 1 | de,bcd,bcd |
|
||||
| e | a | axye,axye |
|
||||
|
||||
Scenario: Car - Destination only street
|
||||
Given the node map
|
||||
"""
|
||||
a e
|
||||
b c d
|
||||
b1 c 2d
|
||||
|
||||
x y
|
||||
"""
|
||||
@ -51,12 +51,12 @@ Feature: Car - Destination only, no passing through
|
||||
When I route I should get
|
||||
| from | to | route |
|
||||
| a | b | ab,ab |
|
||||
| a | c | ab,bc |
|
||||
| a | d | ab,cd |
|
||||
| a | c | ab,bc,bc |
|
||||
| a | 2 | ab,bc,cd |
|
||||
| a | e | axye,axye |
|
||||
| e | d | de,de |
|
||||
| e | c | de,cd |
|
||||
| e | b | de,bc |
|
||||
| e | c | de,cd,cd |
|
||||
| e | 1 | de,cd,bc |
|
||||
| e | a | axye,axye |
|
||||
|
||||
Scenario: Car - Routing inside a destination only area
|
||||
@ -117,6 +117,7 @@ Feature: Car - Destination only, no passing through
|
||||
+ \
|
||||
+ |
|
||||
d |
|
||||
1 |
|
||||
\___e
|
||||
"""
|
||||
|
||||
@ -129,7 +130,7 @@ Feature: Car - Destination only, no passing through
|
||||
When I route I should get
|
||||
| from | to | route |
|
||||
| e | a | acbe,acbe |
|
||||
| d | a | de,acbe,acbe |
|
||||
| 1 | a | de,acbe,acbe |
|
||||
| c | d | cd,cd |
|
||||
|
||||
Scenario: Car - Routing through a parking lot tagged access=destination,service
|
||||
|
||||
@ -1031,3 +1031,61 @@ Feature: Car - Multiple Via Turn restrictions
|
||||
| from | to | route | locations |
|
||||
| a | f | ab,bc,cd,de,ef,ef | a,b,c,d,e,f |
|
||||
|
||||
|
||||
@restriction-way
|
||||
Scenario: Snap source/target to via restriction way
|
||||
Given the node map
|
||||
"""
|
||||
a-1-b-2-c-3-d
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes |
|
||||
| ab |
|
||||
| bc |
|
||||
| cd |
|
||||
|
||||
And the relations
|
||||
| type | way:from | way:via | way:to | restriction |
|
||||
| restriction | ab | bc | cd | no_straight_on |
|
||||
|
||||
When I route I should get
|
||||
| from | to | route |
|
||||
| 1 | 2 | ab,bc,bc |
|
||||
| 2 | 3 | bc,cd,cd |
|
||||
|
||||
|
||||
@restriction-way
|
||||
Scenario: Car - Snap source/target to multi-via restriction way
|
||||
# Example: https://www.openstreetmap.org/relation/11787041
|
||||
Given the node map
|
||||
"""
|
||||
|--g---f---e
|
||||
a | 1
|
||||
|--b---c---d
|
||||
|
||||
"""
|
||||
|
||||
And the nodes
|
||||
| node | highway |
|
||||
| b | traffic_signals |
|
||||
|
||||
And the ways
|
||||
| nodes | oneway | name |
|
||||
| ab | yes | enter |
|
||||
| bc | yes | enter |
|
||||
| cd | yes | right |
|
||||
| de | yes | up |
|
||||
| ef | yes | left |
|
||||
| fc | yes | down |
|
||||
| fg | yes | exit |
|
||||
| ga | yes | exit |
|
||||
|
||||
And the relations
|
||||
| type | way:from | way:via | way:to | restriction |
|
||||
| restriction | bc | cd,de,ef | fg | no_u_turn |
|
||||
|
||||
When I route I should get
|
||||
| from | to | route | locations |
|
||||
| a | 1 | enter,right,up,up | a,c,d,_ |
|
||||
| 1 | a | up,left,exit,exit | _,e,f,a |
|
||||
|
||||
@ -411,7 +411,7 @@ Feature: Car - Turn restrictions
|
||||
y
|
||||
i j f b x a e g h
|
||||
|
||||
c d
|
||||
c1 d
|
||||
"""
|
||||
|
||||
And the ways
|
||||
@ -438,7 +438,7 @@ Feature: Car - Turn restrictions
|
||||
When I route I should get
|
||||
| from | to | route |
|
||||
| e | f | ae,xa,bx,fb,fb |
|
||||
| c | f | dc,da,ae,ge,hg,hg,ge,ae,xa,bx,fb,fb |
|
||||
| 1 | f | dc,da,ae,ge,hg,hg,ge,ae,xa,bx,fb,fb |
|
||||
| d | f | da,ae,ge,hg,hg,ge,ae,xa,bx,fb,fb |
|
||||
|
||||
@except
|
||||
|
||||
@ -39,7 +39,113 @@ Feature: Car - Handle traffic lights
|
||||
| k | n | 20.7s | turn with traffic light |
|
||||
|
||||
|
||||
Scenario: Tarrif Signal Geometry
|
||||
Scenario: Car - Traffic signal direction
|
||||
Given the node map
|
||||
"""
|
||||
a-1-b-2-c
|
||||
|
||||
d-3-e-4-f
|
||||
|
||||
g-5-h-6-i
|
||||
|
||||
j-7-k-8-l
|
||||
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | highway |
|
||||
| abc | primary |
|
||||
| def | primary |
|
||||
| ghi | primary |
|
||||
| jkl | primary |
|
||||
|
||||
And the nodes
|
||||
| node | highway | traffic_signals:direction |
|
||||
| e | traffic_signals | |
|
||||
| h | traffic_signals | forward |
|
||||
| k | traffic_signals | backward |
|
||||
|
||||
When I route I should get
|
||||
| from | to | time | # |
|
||||
| 1 | 2 | 11.1s | no turn with no traffic light |
|
||||
| 2 | 1 | 11.1s | no turn with no traffic light |
|
||||
| 3 | 4 | 13.1s | no turn with traffic light |
|
||||
| 4 | 3 | 13.1s | no turn with traffic light |
|
||||
| 5 | 6 | 13.1s | no turn with traffic light |
|
||||
| 6 | 5 | 11.1s | no turn with no traffic light |
|
||||
| 7 | 8 | 11.1s | no turn with no traffic light |
|
||||
| 8 | 7 | 13.1s | no turn with traffic light |
|
||||
|
||||
|
||||
Scenario: Car - Encounters a traffic light
|
||||
Given the node map
|
||||
"""
|
||||
a f k
|
||||
| | |
|
||||
b-c-d h-g-i l-m-n
|
||||
| | |
|
||||
e j o
|
||||
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | highway |
|
||||
| bcd | primary |
|
||||
| ace | primary |
|
||||
| hgi | primary |
|
||||
| fgj | primary |
|
||||
| lmn | primary |
|
||||
| kmo | primary |
|
||||
|
||||
And the nodes
|
||||
| node | highway | traffic_signals:direction |
|
||||
| g | traffic_signals | forward |
|
||||
| m | traffic_signals | backward |
|
||||
|
||||
|
||||
When I route I should get
|
||||
| from | to | time | # |
|
||||
| a | d | 21.9s | no turn with no traffic light |
|
||||
| a | e | 22.2s | no turn with traffic light |
|
||||
| a | b | 18.7s | turn with no traffic light |
|
||||
| e | b | 21.9s | no turn with no traffic light |
|
||||
| e | a | 22.2s | no turn with traffic light |
|
||||
| e | d | 18.7s | turn with no traffic light |
|
||||
| d | e | 21.9s | no turn with no traffic light |
|
||||
| d | b | 11s | no turn with traffic light |
|
||||
| d | a | 18.7s | turn with no traffic light |
|
||||
| b | a | 21.9s | no turn with no traffic light |
|
||||
| b | d | 11s | no turn with traffic light |
|
||||
| b | e | 18.7s | turn with no traffic light |
|
||||
|
||||
| f | i | 23.9s | no turn with no traffic light |
|
||||
| f | j | 24.2s | no turn with traffic light |
|
||||
| f | h | 20.7s | turn with no traffic light |
|
||||
| j | h | 21.9s | no turn with no traffic light |
|
||||
| j | f | 22.2s | no turn with traffic light |
|
||||
| j | i | 18.7s | turn with no traffic light |
|
||||
| i | j | 21.9s | no turn with no traffic light |
|
||||
| i | h | 11s | no turn with traffic light |
|
||||
| i | f | 18.7s | turn with no traffic light |
|
||||
| h | f | 23.9s | no turn with no traffic light |
|
||||
| h | i | 13s | no turn with traffic light |
|
||||
| h | j | 20.7s | turn with no traffic light |
|
||||
|
||||
| k | n | 21.9s | no turn with no traffic light |
|
||||
| k | o | 22.2s | no turn with traffic light |
|
||||
| k | l | 18.7s | turn with no traffic light |
|
||||
| o | l | 23.9s | no turn with no traffic light |
|
||||
| o | k | 24.2s | no turn with traffic light |
|
||||
| o | n | 20.7s | turn with no traffic light |
|
||||
| n | o | 23.9s | no turn with no traffic light |
|
||||
| n | l | 13s | no turn with traffic light |
|
||||
| n | k | 20.7s | turn with no traffic light |
|
||||
| l | k | 21.9s | no turn with no traffic light |
|
||||
| l | n | 11s | no turn with traffic light |
|
||||
| l | o | 18.7s | turn with no traffic light |
|
||||
|
||||
|
||||
Scenario: Traffic Signal Geometry
|
||||
Given the query options
|
||||
| overview | full |
|
||||
| geometries | polyline |
|
||||
@ -61,6 +167,53 @@ Feature: Car - Handle traffic lights
|
||||
| from | to | route | geometry |
|
||||
| a | c | abc,abc | _ibE_ibE?gJ?eJ |
|
||||
|
||||
|
||||
Scenario: Traffic Signal Geometry - forward signal
|
||||
Given the query options
|
||||
| overview | full |
|
||||
| geometries | polyline |
|
||||
|
||||
Given the node map
|
||||
"""
|
||||
a - b - c
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | highway |
|
||||
| abc | primary |
|
||||
|
||||
And the nodes
|
||||
| node | highway | traffic_signals:direction |
|
||||
| b | traffic_signals | forward |
|
||||
|
||||
When I route I should get
|
||||
| from | to | route | geometry |
|
||||
| a | c | abc,abc | _ibE_ibE?gJ?eJ |
|
||||
|
||||
|
||||
Scenario: Traffic Signal Geometry - reverse signal
|
||||
Given the query options
|
||||
| overview | full |
|
||||
| geometries | polyline |
|
||||
|
||||
Given the node map
|
||||
"""
|
||||
a - b - c
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | highway |
|
||||
| abc | primary |
|
||||
|
||||
And the nodes
|
||||
| node | highway | traffic_signals:direction |
|
||||
| b | traffic_signals | reverse |
|
||||
|
||||
When I route I should get
|
||||
| from | to | route | geometry |
|
||||
| a | c | abc,abc | _ibE_ibE?gJ?eJ |
|
||||
|
||||
|
||||
@traffic
|
||||
Scenario: Traffic update on the edge with a traffic signal
|
||||
Given the node map
|
||||
@ -91,3 +244,67 @@ Feature: Car - Handle traffic lights
|
||||
| from | to | route | speed | weights | time | distances | a:datasources | a:nodes | a:speed | a:duration | a:weight |
|
||||
| a | c | abc,abc | 60 km/h | 24.2,0 | 24.2s | 400m,0m | 1:0 | 1:2:3 | 18:18 | 11.1:11.1 | 11.1:11.1 |
|
||||
| c | a | abc,abc | 60 km/h | 24.2,0 | 24.2s | 400m,0m | 0:1 | 3:2:1 | 18:18 | 11.1:11.1 | 11.1:11.1 |
|
||||
|
||||
|
||||
@traffic
|
||||
Scenario: Traffic update on the edge with a traffic signal - forward
|
||||
Given the node map
|
||||
"""
|
||||
a - b - c
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | highway |
|
||||
| abc | primary |
|
||||
|
||||
|
||||
And the nodes
|
||||
| node | highway | traffic_signals:direction |
|
||||
| b | traffic_signals | forward |
|
||||
|
||||
And the contract extra arguments "--segment-speed-file {speeds_file}"
|
||||
And the customize extra arguments "--segment-speed-file {speeds_file}"
|
||||
And the speed file
|
||||
"""
|
||||
1,2,65
|
||||
2,1,65
|
||||
"""
|
||||
And the query options
|
||||
| annotations | datasources,nodes,speed,duration,weight |
|
||||
|
||||
When I route I should get
|
||||
| from | to | route | speed | weights | time | distances | a:datasources | a:nodes | a:speed | a:duration | a:weight |
|
||||
| a | c | abc,abc | 60 km/h | 24.2,0 | 24.2s | 400m,0m | 1:0 | 1:2:3 | 18:18 | 11.1:11.1 | 11.1:11.1 |
|
||||
| c | a | abc,abc | 65 km/h | 22.2,0 | 22.2s | 400m,0m | 0:1 | 3:2:1 | 18:18 | 11.1:11.1 | 11.1:11.1 |
|
||||
|
||||
|
||||
@traffic
|
||||
Scenario: Traffic update on the edge with a traffic signal - backward
|
||||
Given the node map
|
||||
"""
|
||||
a - b - c
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | highway |
|
||||
| abc | primary |
|
||||
|
||||
|
||||
And the nodes
|
||||
| node | highway | traffic_signals:direction |
|
||||
| b | traffic_signals | backward |
|
||||
|
||||
And the contract extra arguments "--segment-speed-file {speeds_file}"
|
||||
And the customize extra arguments "--segment-speed-file {speeds_file}"
|
||||
And the speed file
|
||||
"""
|
||||
1,2,65
|
||||
2,1,65
|
||||
"""
|
||||
And the query options
|
||||
| annotations | datasources,nodes,speed,duration,weight |
|
||||
|
||||
When I route I should get
|
||||
| from | to | route | speed | weights | time | distances | a:datasources | a:nodes | a:speed | a:duration | a:weight |
|
||||
| a | c | abc,abc | 65 km/h | 22.2,0 | 22.2s | 400m,0m | 1:0 | 1:2:3 | 18:18 | 11.1:11.1 | 11.1:11.1 |
|
||||
| c | a | abc,abc | 60 km/h | 24.2,0 | 24.2s | 400m,0m | 0:1 | 3:2:1 | 18:18 | 11.1:11.1 | 11.1:11.1 |
|
||||
|
||||
@ -332,10 +332,11 @@ Feature: Merge Segregated Roads
|
||||
|
|
||||
.b.
|
||||
c h
|
||||
1 |
|
||||
| 4
|
||||
| |
|
||||
| |
|
||||
1 2
|
||||
| |
|
||||
2 |
|
||||
| 3
|
||||
d g
|
||||
'e'
|
||||
|
|
||||
@ -356,11 +357,11 @@ Feature: Merge Segregated Roads
|
||||
When I route I should get
|
||||
| waypoints | turns | route | intersections |
|
||||
| a,f | depart,arrive | road,road | true:180,false:0 true:180,false:0 true:180;true:0 |
|
||||
| c,f | depart,arrive | bridge,road | true:180,false:0 true:180;true:0 |
|
||||
| 1,f | depart,arrive | bridge,road | true:180,false:0 true:180;true:0 |
|
||||
| 2,f | depart,arrive | bridge,road | true:180,false:0 true:180;true:0 |
|
||||
| f,a | depart,arrive | road,road | true:0,true:0 false:180,true:0 false:180;true:180 |
|
||||
| g,a | depart,arrive | bridge,road | true:0,true:0 false:180;true:180 |
|
||||
| 2,a | depart,arrive | bridge,road | true:0,true:0 false:180;true:180 |
|
||||
| 3,a | depart,arrive | bridge,road | true:0,true:0 false:180;true:180 |
|
||||
| 4,a | depart,arrive | bridge,road | true:0,true:0 false:180;true:180 |
|
||||
|
||||
@negative
|
||||
Scenario: Traffic Circle
|
||||
|
||||
@ -67,10 +67,10 @@ Feature: Compass bearing
|
||||
Scenario: Bearing in a roundabout
|
||||
Given the node map
|
||||
"""
|
||||
k d c j
|
||||
k d 1c j
|
||||
e b
|
||||
f a
|
||||
l g h i
|
||||
l g2 h i
|
||||
"""
|
||||
|
||||
And the ways
|
||||
@ -94,8 +94,8 @@ Feature: Compass bearing
|
||||
|
||||
When I route I should get
|
||||
| from | to | route | bearing |
|
||||
| c | b | cd,de,ef,fg,gh,ha,ab,ab | 0->270,270->225,225->180,180->135,135->90,90->45,45->0,0->0 |
|
||||
| g | f | gh,ha,ab,bc,cd,de,ef,ef | 0->90,90->45,45->0,0->315,315->270,270->225,225->180,180->0 |
|
||||
| 1 | b | cd,de,ef,fg,gh,ha,ab,ab | 0->270,270->225,225->180,180->135,135->90,90->45,45->0,0->0 |
|
||||
| 2 | f | gh,ha,ab,bc,cd,de,ef,ef | 0->90,90->45,45->0,0->315,315->270,270->225,225->180,180->0 |
|
||||
|
||||
Scenario: Bearing should stay constant when zig-zagging
|
||||
Given the node map
|
||||
|
||||
@ -589,12 +589,12 @@ Feature: Basic Distance Matrix
|
||||
|
||||
When I request a travel distance matrix I should get
|
||||
| | a | b | c | d | e | f |
|
||||
| a | 0 | 100 | 300 | 650 | 1934.5 | 1534.6 |
|
||||
| b | 760.6 | 0 | 200 | 550.1 | 1834.6 | 1434.6 |
|
||||
| c | 560.6 | 660.5 | 0 | 350 | 1634.6 | 1234.6 |
|
||||
| d | 1484.6 | 1584.5| 1784.5 | 0 | 1284.5 | 884.6 |
|
||||
| e | 200 | 300 | 500 | 710.6 | 0 | 1595.2 |
|
||||
| f | 600 | 699.9 | 899.9 | 1110.5 | 399.9 | 0 |
|
||||
| a | 0 | 100 | 300 | 650 | 660.5 | 1534.6 |
|
||||
| b | 760.6 | 0 | 200 | 550.1 | 560.6 | 1434.6 |
|
||||
| c | 560.6 | 660.5 | 0 | 350 | 360.5 | 1234.6 |
|
||||
| d | 1484.6 | 1584.5| 1645.1 | 0 | 1284.5 | 884.6 |
|
||||
| e | 200 | 300 | 360.5 | 710.6 | 0 | 1595.2 |
|
||||
| f | 600 | 699.9 | 760.5 | 884.6 | 399.9 | 0 |
|
||||
|
||||
|
||||
Scenario: Testbot - Filling in noroutes with estimates (defaults to input coordinate location)
|
||||
@ -727,4 +727,3 @@ Feature: Basic Distance Matrix
|
||||
| 1 | 0 | 1000.1 | 1400.1 |
|
||||
| 2 | 1000.1 | 0 | 400 |
|
||||
| 3 | 1400.1 | 400 | 0 |
|
||||
|
||||
|
||||
@ -491,18 +491,18 @@ Feature: Basic Duration Matrix
|
||||
|
||||
When I route I should get
|
||||
| from | to | route | distance | time | weight |
|
||||
| a | c | ac,ac | 200m | 5s | 5 |
|
||||
| a | c | ac,ac | 200m | 1s | 1 |
|
||||
|
||||
When I request a travel time matrix I should get
|
||||
| | a | b | c | d |
|
||||
| a | 0 | 1 | 5 | 10 |
|
||||
| a | 0 | 1 | 1 | 6 |
|
||||
|
||||
When I request a travel time matrix I should get
|
||||
| | a |
|
||||
| a | 0 |
|
||||
| b | 1 |
|
||||
| c | 15 |
|
||||
| d | 10 |
|
||||
| c | 1 |
|
||||
| d | 6 |
|
||||
|
||||
Scenario: Testbot - OneToMany vs ManyToOne
|
||||
Given the node map
|
||||
|
||||
629
features/testbot/snap_intersection.feature
Normal file
629
features/testbot/snap_intersection.feature
Normal file
@ -0,0 +1,629 @@
|
||||
Feature: Snapping at intersections
|
||||
|
||||
Background:
|
||||
# Use turnbot so that we can validate when we are
|
||||
# snapping to one of many potential candidate ways
|
||||
Given the profile "turnbot"
|
||||
|
||||
# https://github.com/Project-OSRM/osrm-backend/issues/4465
|
||||
Scenario: Snapping source to intersection with one-way roads
|
||||
Given the node map
|
||||
"""
|
||||
a e c
|
||||
\ | /
|
||||
d
|
||||
|
||||
1
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | oneway |
|
||||
| da | yes |
|
||||
| dc | yes |
|
||||
| de | yes |
|
||||
|
||||
|
||||
When I route I should get
|
||||
| from | to | route | time |
|
||||
| 1 | e | de,de | 20s |
|
||||
| 1 | a | da,da | 28.3s |
|
||||
| 1 | c | dc,dc | 28.3s |
|
||||
|
||||
When I request a travel time matrix I should get
|
||||
| | a | c | e |
|
||||
| 1 | 28.3 | 28.3 | 20 |
|
||||
|
||||
|
||||
Scenario: Snapping destination to intersection with one-way roads
|
||||
Given the node map
|
||||
"""
|
||||
a e c
|
||||
\ | /
|
||||
d
|
||||
|
||||
1
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | oneway |
|
||||
| da | -1 |
|
||||
| dc | -1 |
|
||||
| de | -1 |
|
||||
|
||||
|
||||
When I route I should get
|
||||
| from | to | route | time |
|
||||
| e | 1 | de,de | 20s |
|
||||
| a | 1 | da,da | 28.3s |
|
||||
| c | 1 | dc,dc | 28.3s |
|
||||
|
||||
When I request a travel time matrix I should get
|
||||
| | 1 |
|
||||
| a | 28.3 |
|
||||
| c | 28.3 |
|
||||
| e | 20 |
|
||||
|
||||
|
||||
Scenario: Snapping to intersection with bi-directional roads
|
||||
Given the node map
|
||||
"""
|
||||
a e
|
||||
| /
|
||||
d---c
|
||||
|
||||
1
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes |
|
||||
| ad |
|
||||
| ed |
|
||||
| dc |
|
||||
|
||||
When I route I should get
|
||||
| from | to | route | time | weight |
|
||||
| 1 | c | dc,dc | 20s | 20 |
|
||||
| 1 | a | ad,ad | 20s | 20 |
|
||||
| 1 | e | ed,ed | 28.3s | 28.3 |
|
||||
| c | 1 | dc,dc | 20s | 20 |
|
||||
| a | 1 | ad,ad | 20s | 20 |
|
||||
| e | 1 | ed,ed | 28.3s | 28.3 |
|
||||
|
||||
When I request a travel time matrix I should get
|
||||
| | a | c | e |
|
||||
| 1 | 20 | 20 | 28.3 |
|
||||
|
||||
When I request a travel time matrix I should get
|
||||
| | 1 |
|
||||
| a | 20 |
|
||||
| c | 20 |
|
||||
| e | 28.3 |
|
||||
|
||||
|
||||
Scenario: Snapping at compressible node
|
||||
Given the node map
|
||||
"""
|
||||
a---b---c
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes |
|
||||
| abc |
|
||||
|
||||
When I route I should get
|
||||
| from | to | route | time | weight |
|
||||
| b | c | abc,abc | 20s | 20 |
|
||||
| b | a | abc,abc | 20s | 20 |
|
||||
| a | b | abc,abc | 20s | 20 |
|
||||
| c | b | abc,abc | 20s | 20 |
|
||||
|
||||
|
||||
Scenario: Snapping at compressible node with traffic lights
|
||||
Given the node map
|
||||
"""
|
||||
a---b---c
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes |
|
||||
| abc |
|
||||
|
||||
# Turnbot will use the turn penalty instead of traffic penalty.
|
||||
# We do this to induce a penalty between two edges of the same
|
||||
# segment.
|
||||
And the nodes
|
||||
| node | highway |
|
||||
| b | traffic_signals |
|
||||
|
||||
# Snaps to first edge in forward direction
|
||||
When I route I should get
|
||||
| from | to | route | time | weight |
|
||||
| b | c | abc,abc | 40s | 40 |
|
||||
| b | a | abc,abc | 20s | 20 |
|
||||
| a | b | abc,abc | 20s | 20 |
|
||||
| c | b | abc,abc | 40s | 40 |
|
||||
|
||||
|
||||
Scenario: Snapping at compressible node traffic lights, one-way
|
||||
Given the node map
|
||||
"""
|
||||
a-->b-->c
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | oneway |
|
||||
| abc | yes |
|
||||
|
||||
# Turnbot will use the turn penalty instead of traffic penalty.
|
||||
# We do this to induce a penalty between two edges of the same
|
||||
# segment.
|
||||
And the nodes
|
||||
| node | highway |
|
||||
| b | traffic_signals |
|
||||
|
||||
|
||||
# Snaps to first edge in forward direction
|
||||
When I route I should get
|
||||
| from | to | route | time | weight |
|
||||
| b | c | abc,abc | 40s | 40 |
|
||||
| a | b | abc,abc | 20s | 20 |
|
||||
|
||||
When I route I should get
|
||||
| from | to | code |
|
||||
| b | a | NoRoute |
|
||||
| c | b | NoRoute |
|
||||
|
||||
|
||||
Scenario: Snapping at compressible node traffic lights, reverse one-way
|
||||
Given the node map
|
||||
"""
|
||||
a<--b<--c
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | oneway |
|
||||
| abc | -1 |
|
||||
|
||||
# Turnbot will use the turn penalty instead of traffic penalty.
|
||||
# We do this to induce a penalty between two edges of the same
|
||||
# segment.
|
||||
And the nodes
|
||||
| node | highway |
|
||||
| b | traffic_signals |
|
||||
|
||||
|
||||
# Snaps to first edge in forward direction - as this is one-way,
|
||||
# the forward direction has changed.
|
||||
When I route I should get
|
||||
| from | to | route | time | weight |
|
||||
| b | a | abc,abc | 40s | 40 |
|
||||
| c | b | abc,abc | 20s | 20 |
|
||||
|
||||
When I route I should get
|
||||
| from | to | code |
|
||||
| b | c | NoRoute |
|
||||
| a | b | NoRoute |
|
||||
|
||||
|
||||
Scenario: Snapping at traffic lights, reverse disabled
|
||||
Given the node map
|
||||
"""
|
||||
a-->b-->c
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes |
|
||||
| abc |
|
||||
|
||||
And the contract extra arguments "--segment-speed-file {speeds_file}"
|
||||
And the customize extra arguments "--segment-speed-file {speeds_file}"
|
||||
And the speed file
|
||||
"""
|
||||
2,1,0
|
||||
3,2,0
|
||||
"""
|
||||
|
||||
# Turnbot will use the turn penalty instead of traffic penalty.
|
||||
# We do this to induce a penalty between two edges of the same
|
||||
# segment.
|
||||
And the nodes
|
||||
| node | highway |
|
||||
| b | traffic_signals |
|
||||
|
||||
# Snaps to first edge in forward direction.
|
||||
When I route I should get
|
||||
| from | to | route | time | weight |
|
||||
| b | c | abc,abc | 40s | 40 |
|
||||
| a | b | abc,abc | 20s | 20 |
|
||||
|
||||
When I route I should get
|
||||
| from | to | code |
|
||||
| b | a | NoRoute |
|
||||
| c | b | NoRoute |
|
||||
|
||||
|
||||
Scenario: Snapping at traffic lights, forward disabled
|
||||
Given the node map
|
||||
"""
|
||||
a<--b<--c
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes |
|
||||
| abc |
|
||||
|
||||
And the contract extra arguments "--segment-speed-file {speeds_file}"
|
||||
And the customize extra arguments "--segment-speed-file {speeds_file}"
|
||||
And the speed file
|
||||
"""
|
||||
1,2,0
|
||||
2,3,0
|
||||
"""
|
||||
|
||||
# Turnbot will use the turn penalty instead of traffic penalty.
|
||||
# We do this to induce a penalty between two edges of the same
|
||||
# segment.
|
||||
And the nodes
|
||||
| node | highway |
|
||||
| b | traffic_signals |
|
||||
|
||||
# Forward direction is disabled, still snaps to first edge in forward direction
|
||||
When I route I should get
|
||||
| from | to | route | time | weight |
|
||||
| b | a | abc,abc | 20s | 20 |
|
||||
| c | b | abc,abc | 40s | 40 |
|
||||
|
||||
When I route I should get
|
||||
| from | to | code |
|
||||
| b | c | NoRoute |
|
||||
| a | b | NoRoute |
|
||||
|
||||
|
||||
Scenario: Snap to target node with next section of segment blocked
|
||||
Given the node map
|
||||
"""
|
||||
a-->b---c---d<--e
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes |
|
||||
| abc |
|
||||
| cde |
|
||||
|
||||
And the contract extra arguments "--segment-speed-file {speeds_file}"
|
||||
And the customize extra arguments "--segment-speed-file {speeds_file}"
|
||||
And the speed file
|
||||
"""
|
||||
2,1,0
|
||||
4,5,0
|
||||
"""
|
||||
|
||||
When I route I should get
|
||||
| from | to | route | time | weight |
|
||||
| a | d | abc,cde,cde | 60s | 60 |
|
||||
| e | b | cde,abc,abc | 60s | 60 |
|
||||
|
||||
|
||||
When I route I should get
|
||||
| from | to | code |
|
||||
| a | e | NoRoute |
|
||||
| e | a | NoRoute |
|
||||
|
||||
|
||||
Scenario: Snapping to source node with previous section of segment blocked
|
||||
Given the node map
|
||||
"""
|
||||
a<--b---c---d-->e
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes |
|
||||
| abc |
|
||||
| cde |
|
||||
|
||||
And the contract extra arguments "--segment-speed-file {speeds_file}"
|
||||
And the customize extra arguments "--segment-speed-file {speeds_file}"
|
||||
And the speed file
|
||||
"""
|
||||
1,2,0
|
||||
5,4,0
|
||||
"""
|
||||
|
||||
When I route I should get
|
||||
| from | to | code |
|
||||
| a | e | NoRoute |
|
||||
| b | e | NoRoute |
|
||||
| e | a | NoRoute |
|
||||
| d | a | NoRoute |
|
||||
|
||||
|
||||
Scenario: Only snaps to one of many equidistant nearest locations
|
||||
Given the node map
|
||||
"""
|
||||
b-------c
|
||||
| |
|
||||
| |
|
||||
a 1 d
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes |
|
||||
| ab |
|
||||
| bc |
|
||||
| cd |
|
||||
|
||||
When I route I should get
|
||||
| from | to | route | time | weight |
|
||||
| 1 | b | ab,ab | 30s | 30 |
|
||||
| 1 | c | ab,bc,bc | 80s +-1 | 80 +-1 |
|
||||
|
||||
|
||||
Scenario: Snaps to alternative big SCC candidate if nearest candidates are not strongly connected
|
||||
Given the node map
|
||||
"""
|
||||
1
|
||||
g---h---i
|
||||
a-----b-----c
|
||||
|
|
||||
f-----e-----d
|
||||
j---k---l
|
||||
2
|
||||
"""
|
||||
|
||||
Given the extract extra arguments "--small-component-size=4"
|
||||
|
||||
And the ways
|
||||
| nodes |
|
||||
| abc |
|
||||
| cd |
|
||||
| fed |
|
||||
| ghi |
|
||||
| jkl |
|
||||
|
||||
# As forward direction is disabled...
|
||||
When I route I should get
|
||||
| from | to | route | time | weight | locations |
|
||||
| 1 | 2 | abc,cd,fed,fed | 100s +-1 | 100 +-1 | b,c,d,e |
|
||||
|
||||
|
||||
Scenario: Can use big or small SCC nearest candidates if at same location
|
||||
Given the node map
|
||||
"""
|
||||
1
|
||||
a-----b-----c
|
||||
| |
|
||||
g |
|
||||
|
|
||||
f-----e-----d
|
||||
|
||||
"""
|
||||
|
||||
Given the extract extra arguments "--small-component-size=4"
|
||||
|
||||
And the ways
|
||||
| nodes | oneway |
|
||||
| ab | no |
|
||||
| bc | no |
|
||||
| cd | no |
|
||||
| fed | no |
|
||||
| bg | yes | # small SCC
|
||||
|
||||
And the relations
|
||||
| type | way:from | way:to | node:via | restriction |
|
||||
| restriction | ab | bg | b | no_right_turn |
|
||||
| restriction | bc | bg | b | no_left_turn |
|
||||
|
||||
When I route I should get
|
||||
| from | to | route | time | weight | locations |
|
||||
| 1 | g | bg,bg | 20s | 20 | b,g |
|
||||
| 1 | e | bc,cd,fed,fed | 120s +-1 | 120 +-1 | b,c,d,e |
|
||||
|
||||
|
||||
Scenario: Using small SCC candidates when at same location as big SCC alternatives is not supported
|
||||
Given the node map
|
||||
"""
|
||||
1
|
||||
g---h---i
|
||||
a-----b-----c
|
||||
| |
|
||||
| |
|
||||
m |
|
||||
f-----e-----d
|
||||
j---k---l
|
||||
2
|
||||
|
||||
"""
|
||||
|
||||
Given the extract extra arguments "--small-component-size=4"
|
||||
|
||||
And the ways
|
||||
| nodes | oneway |
|
||||
| ab | no |
|
||||
| bc | no |
|
||||
| cd | no |
|
||||
| fed | no |
|
||||
| ghi | no |
|
||||
| jkl | no |
|
||||
| bm | yes | # small SCC
|
||||
|
||||
And the relations
|
||||
| type | way:from | way:to | node:via | restriction |
|
||||
| restriction | ab | bm | b | no_right_turn |
|
||||
| restriction | bc | bm | b | no_left_turn |
|
||||
|
||||
When I route I should get
|
||||
| from | to | route | time | weight | locations |
|
||||
| 1 | 2 | bc,cd,fed,fed | 120s +-1 | 120 +-1 | b,c,d,e |
|
||||
| 1 | m | bc,cd,fed,fed | 120s +-1 | 120 +-1 | b,c,d,e |
|
||||
|
||||
|
||||
Scenario: Shortest via path with continuation, simple loop
|
||||
Given the node map
|
||||
"""
|
||||
a---b
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes |
|
||||
| ab |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | time | weight |
|
||||
| a,b,a | ab,ab,ab,ab | 60s | 60 |
|
||||
|
||||
|
||||
Scenario: Shortest via path with uturns, simple loop
|
||||
Given the node map
|
||||
"""
|
||||
a---b
|
||||
"""
|
||||
|
||||
Given the query options
|
||||
| continue_straight | false |
|
||||
|
||||
And the ways
|
||||
| nodes |
|
||||
| ab |
|
||||
|
||||
# Does not pay the cost of the turn
|
||||
When I route I should get
|
||||
| waypoints | route | time | weight |
|
||||
| a,b,a | ab,ab,ab,ab | 40s | 40 |
|
||||
|
||||
|
||||
Scenario: Shortest path with multiple endpoint snapping candidates
|
||||
Given the node map
|
||||
"""
|
||||
b
|
||||
|
||||
c
|
||||
|
||||
a d f
|
||||
|
||||
e
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | oneway |
|
||||
| ab | no |
|
||||
| ac | no |
|
||||
| ad | no |
|
||||
| ae | no |
|
||||
| bf | no |
|
||||
| cf | yes |
|
||||
| df | yes |
|
||||
| ef | no |
|
||||
|
||||
|
||||
When I route I should get
|
||||
| from | to | route | time | weight |
|
||||
| a | f | ad,df,df | 40s | 40 |
|
||||
| f | a | ef,ae,ae | 66.6s | 66.6 |
|
||||
|
||||
When I request a travel time matrix I should get
|
||||
| | a | f |
|
||||
| a | 0 | 40 |
|
||||
| f | 66.6 | 0 |
|
||||
|
||||
|
||||
Scenario: Shortest via path with continuation, multiple waypoint snapping candidates
|
||||
Given the node map
|
||||
"""
|
||||
b g
|
||||
|
||||
c h
|
||||
|
||||
a d f i
|
||||
k
|
||||
e j
|
||||
"""
|
||||
|
||||
And the ways
|
||||
| nodes | oneway |
|
||||
| ab | no |
|
||||
| ac | no |
|
||||
| ad | no |
|
||||
| ae | no |
|
||||
| bf | no |
|
||||
| cf | yes |
|
||||
| df | yes |
|
||||
| ef | no |
|
||||
| fg | no |
|
||||
| fh | -1 |
|
||||
| fi | -1 |
|
||||
| fj | no |
|
||||
| gk | no |
|
||||
| hk | no |
|
||||
| ik | no |
|
||||
| kj | no |
|
||||
|
||||
And the relations
|
||||
| type | way:from | way:to | node:via | restriction |
|
||||
| restriction | df | fg | f | only_left_turn |
|
||||
| restriction | fi | bf | f | only_right_turn |
|
||||
|
||||
# Longer routes can take different paths from sub-routes
|
||||
When I route I should get
|
||||
| waypoints | route | time | weight |
|
||||
| a,f | ad,df,df | 40s | 40 |
|
||||
| f,k | fj,kj,kj | 65.6s | 65.6 |
|
||||
| a,f,k | ac,cf,cf,fj,kj,kj | 132.8s | 132.8 |
|
||||
| k,f | ik,fi,fi | 54.3s | 54.3 |
|
||||
| f,a | ef,ae,ae | 66.6s | 66.6 |
|
||||
| k,f,a | kj,fj,fj,ef,ae,ae | 141.4s | 141.4 |
|
||||
|
||||
When I request a travel time matrix I should get
|
||||
| | a | f | k |
|
||||
| a | 0 | 40 | 132.8 |
|
||||
| f | 66.6 | 0 | 65.6 |
|
||||
| k | 141.4 | 54.3 | 0 |
|
||||
|
||||
|
||||
Scenario: Shortest via path with uturns, multiple waypoint snapping candidates
|
||||
Given the node map
|
||||
"""
|
||||
b g
|
||||
|
||||
c h
|
||||
|
||||
a d f i
|
||||
k
|
||||
e j
|
||||
"""
|
||||
|
||||
Given the query options
|
||||
| continue_straight | false |
|
||||
|
||||
And the ways
|
||||
| nodes | oneway |
|
||||
| ab | no |
|
||||
| ac | no |
|
||||
| ad | no |
|
||||
| ae | no |
|
||||
| bf | no |
|
||||
| cf | yes |
|
||||
| df | yes |
|
||||
| ef | no |
|
||||
| fg | no |
|
||||
| fh | -1 |
|
||||
| fi | -1 |
|
||||
| fj | no |
|
||||
| gk | no |
|
||||
| hk | no |
|
||||
| ik | no |
|
||||
| kj | no |
|
||||
|
||||
And the relations
|
||||
| type | way:from | way:to | node:via | restriction |
|
||||
| restriction | df | fg | f | only_left_turn |
|
||||
| restriction | fi | bf | f | only_right_turn |
|
||||
|
||||
# Longer routes use same path as sub-routes
|
||||
When I route I should get
|
||||
| waypoints | route | time | weight |
|
||||
| a,f | ad,df,df | 40s | 40 |
|
||||
| f,k | fj,kj,kj | 65.6s | 65.6 |
|
||||
| a,f,k | ad,df,df,fj,kj,kj | 105.6s | 105.6 |
|
||||
| k,f | ik,fi,fi | 54.3s | 54.3 |
|
||||
| f,a | ef,ae,ae | 66.6s | 66.6 |
|
||||
| k,f,a | ik,fi,fi,ef,ae,ae | 120.9s | 120.9 |
|
||||
@ -47,7 +47,7 @@ Feature: Traffic - turn penalties applied to turn onto which a phantom node snap
|
||||
| 1 | e | ab,be,be | 36 km/h | 30s +-1 |
|
||||
| b | f | bc,cf,cf | 36 km/h | 40s +-1 |
|
||||
| 2 | f | bc,cf,cf | 36 km/h | 30s +-1 |
|
||||
| c | g | cd,dg,dg | 144 km/h | 10s +-1 |
|
||||
| c | g | cd,dg,dg | 72 km/h | 20s +-1 |
|
||||
| 3 | g | cd,dg,dg | 54 km/h | 20s +-1 |
|
||||
|
||||
Scenario: Weighting based on turn penalty file with weights
|
||||
@ -65,5 +65,5 @@ Feature: Traffic - turn penalties applied to turn onto which a phantom node snap
|
||||
| 1 | e | ab,be,be | 36 km/h | 30s +-1 | 6.8,20,0 |
|
||||
| b | f | bc,cf,cf | 36 km/h | 40s +-1 | 20,20,0 |
|
||||
| 2 | f | bc,cf,cf | 36 km/h | 30s +-1 | 10.1,20,0 |
|
||||
| c | g | cd,dg,dg | 144 km/h | 10s +-1 | 120.8,20,0 |
|
||||
| c | g | cd,dg,dg | 72 km/h | 20s +-1 | 120.8,20,0 |
|
||||
| 3 | g | cd,dg,dg | 54 km/h | 20s +-1 | 110.9,20,0 |
|
||||
|
||||
@ -281,7 +281,7 @@ Feature: Weight tests
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | distance | weights | times |
|
||||
| a,c | , | 40m +-.1 | 5.12,0 | 290s,0s |
|
||||
| a,c | , | 40m +-.1 | 2.22,0 | 200s,0s |
|
||||
| a,e | ,, | 60m +-.1 | 5.12,1.11,0 | 290s,100s,0s |
|
||||
| e,a | ,, | 60m +-.1 | 2.21,2.22,0 | 10s,200s,0s |
|
||||
| e,d | ,, | 40m +-.1 | 4.01,1.11,0 | 190s,100s,0s |
|
||||
@ -329,7 +329,7 @@ Feature: Weight tests
|
||||
| ce |
|
||||
And the speed file
|
||||
"""
|
||||
1,2,36,42
|
||||
1,2,36.999,42
|
||||
2,1,36,42
|
||||
"""
|
||||
And the turn penalty file
|
||||
@ -341,8 +341,8 @@ Feature: Weight tests
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | distance | weights | times |
|
||||
| a,d | , | 60m | 20.5,0 | 24s,0s |
|
||||
| a,e | ,, | 60m | 27.2,10,0 | 38.5s,11s,0s |
|
||||
| a,d | , | 60m | 20.5,0 | 23.9s,0s |
|
||||
| a,e | ,, | 60m | 27.2,10,0 | 38.4s,11s,0s |
|
||||
| d,e | ,, | 40m | 10,10,0 | 11s,11s,0s |
|
||||
|
||||
@traffic @speed
|
||||
|
||||
@ -9,9 +9,12 @@
|
||||
#include "engine/hint.hpp"
|
||||
#include "util/coordinate_calculation.hpp"
|
||||
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/range/adaptor/transformed.hpp>
|
||||
#include <boost/range/algorithm/transform.hpp>
|
||||
|
||||
#include <boost/range/adaptor/filtered.hpp>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
@ -22,6 +25,8 @@ namespace engine
|
||||
namespace api
|
||||
{
|
||||
|
||||
static const constexpr char *INTERSECTION_DELIMITER = " / ";
|
||||
|
||||
class BaseAPI
|
||||
{
|
||||
public:
|
||||
@ -30,92 +35,129 @@ class BaseAPI
|
||||
{
|
||||
}
|
||||
|
||||
util::json::Array MakeWaypoints(const std::vector<PhantomNodes> &segment_end_coordinates) const
|
||||
util::json::Array
|
||||
MakeWaypoints(const std::vector<PhantomNodeCandidates> &waypoint_candidates) const
|
||||
{
|
||||
BOOST_ASSERT(parameters.coordinates.size() > 0);
|
||||
BOOST_ASSERT(parameters.coordinates.size() == segment_end_coordinates.size() + 1);
|
||||
BOOST_ASSERT(parameters.coordinates.size() == waypoint_candidates.size());
|
||||
|
||||
util::json::Array waypoints;
|
||||
waypoints.values.resize(parameters.coordinates.size());
|
||||
waypoints.values[0] = MakeWaypoint(segment_end_coordinates.front().source_phantom);
|
||||
|
||||
auto out_iter = std::next(waypoints.values.begin());
|
||||
boost::range::transform(
|
||||
segment_end_coordinates, out_iter, [this](const PhantomNodes &phantom_pair) {
|
||||
return MakeWaypoint(phantom_pair.target_phantom);
|
||||
});
|
||||
waypoint_candidates,
|
||||
waypoints.values.begin(),
|
||||
[this](const PhantomNodeCandidates &candidates) { return MakeWaypoint(candidates); });
|
||||
return waypoints;
|
||||
}
|
||||
|
||||
// FIXME: gcc 4.9 does not like MakeWaypoints to be protected
|
||||
// protected:
|
||||
util::json::Object MakeWaypoint(const PhantomNode &phantom) const
|
||||
{
|
||||
if (parameters.generate_hints)
|
||||
util::json::Object MakeWaypoint(const PhantomNodeCandidates &candidates) const
|
||||
{
|
||||
// TODO: check forward/reverse
|
||||
const auto toName = [this](const auto &phantom) {
|
||||
return facade.GetNameForID(facade.GetNameIndex(phantom.forward_segment_id.id))
|
||||
.to_string();
|
||||
};
|
||||
const auto noEmpty = [](const auto &name) { return !name.empty(); };
|
||||
|
||||
// At an intersection we may have multiple phantom node candidates.
|
||||
// Combine them to represent the waypoint name.
|
||||
std::string waypoint_name = boost::algorithm::join(
|
||||
candidates | boost::adaptors::transformed(toName) | boost::adaptors::filtered(noEmpty),
|
||||
INTERSECTION_DELIMITER);
|
||||
|
||||
const auto &snapped_location = candidatesSnappedLocation(candidates);
|
||||
const auto &input_location = candidatesInputLocation(candidates);
|
||||
if (parameters.generate_hints)
|
||||
{
|
||||
std::vector<SegmentHint> seg_hints(candidates.size());
|
||||
std::transform(candidates.begin(),
|
||||
candidates.end(),
|
||||
seg_hints.begin(),
|
||||
[this](const auto &phantom) {
|
||||
return SegmentHint{phantom, facade.GetCheckSum()};
|
||||
});
|
||||
|
||||
return json::makeWaypoint(
|
||||
phantom.location,
|
||||
util::coordinate_calculation::greatCircleDistance(phantom.location,
|
||||
phantom.input_location),
|
||||
facade.GetNameForID(facade.GetNameIndex(phantom.forward_segment_id.id)).to_string(),
|
||||
Hint{phantom, facade.GetCheckSum()});
|
||||
snapped_location,
|
||||
util::coordinate_calculation::greatCircleDistance(snapped_location, input_location),
|
||||
waypoint_name,
|
||||
{std::move(seg_hints)});
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: check forward/reverse
|
||||
return json::makeWaypoint(
|
||||
phantom.location,
|
||||
util::coordinate_calculation::greatCircleDistance(phantom.location,
|
||||
phantom.input_location),
|
||||
facade.GetNameForID(facade.GetNameIndex(phantom.forward_segment_id.id))
|
||||
.to_string());
|
||||
snapped_location,
|
||||
util::coordinate_calculation::greatCircleDistance(snapped_location, input_location),
|
||||
waypoint_name);
|
||||
}
|
||||
}
|
||||
|
||||
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<fbresult::Waypoint>>>
|
||||
MakeWaypoints(flatbuffers::FlatBufferBuilder *builder,
|
||||
const std::vector<PhantomNodes> &segment_end_coordinates) const
|
||||
const std::vector<PhantomNodeCandidates> &waypoint_candidates) const
|
||||
{
|
||||
BOOST_ASSERT(parameters.coordinates.size() > 0);
|
||||
BOOST_ASSERT(parameters.coordinates.size() == segment_end_coordinates.size() + 1);
|
||||
BOOST_ASSERT(parameters.coordinates.size() == waypoint_candidates.size());
|
||||
|
||||
std::vector<flatbuffers::Offset<fbresult::Waypoint>> waypoints;
|
||||
waypoints.resize(parameters.coordinates.size());
|
||||
waypoints[0] =
|
||||
MakeWaypoint(builder, segment_end_coordinates.front().source_phantom)->Finish();
|
||||
|
||||
std::transform(segment_end_coordinates.begin(),
|
||||
segment_end_coordinates.end(),
|
||||
std::next(waypoints.begin()),
|
||||
[this, builder](const PhantomNodes &phantom_pair) {
|
||||
return MakeWaypoint(builder, phantom_pair.target_phantom)->Finish();
|
||||
std::transform(waypoint_candidates.begin(),
|
||||
waypoint_candidates.end(),
|
||||
waypoints.begin(),
|
||||
[this, builder](const PhantomNodeCandidates &candidates) {
|
||||
return MakeWaypoint(builder, candidates)->Finish();
|
||||
});
|
||||
return builder->CreateVector(waypoints);
|
||||
}
|
||||
|
||||
// FIXME: gcc 4.9 does not like MakeWaypoints to be protected
|
||||
// protected:
|
||||
std::unique_ptr<fbresult::WaypointBuilder> MakeWaypoint(flatbuffers::FlatBufferBuilder *builder,
|
||||
const PhantomNode &phantom) const
|
||||
std::unique_ptr<fbresult::WaypointBuilder>
|
||||
MakeWaypoint(flatbuffers::FlatBufferBuilder *builder,
|
||||
const PhantomNodeCandidates &candidates) const
|
||||
{
|
||||
|
||||
const auto &snapped_location = candidatesSnappedLocation(candidates);
|
||||
const auto &input_location = candidatesInputLocation(candidates);
|
||||
auto location =
|
||||
fbresult::Position(static_cast<double>(util::toFloating(phantom.location.lon)),
|
||||
static_cast<double>(util::toFloating(phantom.location.lat)));
|
||||
auto name_string = builder->CreateString(
|
||||
facade.GetNameForID(facade.GetNameIndex(phantom.forward_segment_id.id)).to_string());
|
||||
fbresult::Position(static_cast<double>(util::toFloating(snapped_location.lon)),
|
||||
static_cast<double>(util::toFloating(snapped_location.lat)));
|
||||
|
||||
const auto toName = [this](const auto &phantom) {
|
||||
return facade.GetNameForID(facade.GetNameIndex(phantom.forward_segment_id.id))
|
||||
.to_string();
|
||||
};
|
||||
const auto noEmpty = [](const auto &name) { return !name.empty(); };
|
||||
|
||||
// At an intersection we may have multiple phantom node candidates.
|
||||
// Combine them to represent the waypoint name.
|
||||
std::string waypoint_name = boost::algorithm::join(
|
||||
candidates | boost::adaptors::transformed(toName) | boost::adaptors::filtered(noEmpty),
|
||||
INTERSECTION_DELIMITER);
|
||||
auto name_string = builder->CreateString(waypoint_name);
|
||||
|
||||
flatbuffers::Offset<flatbuffers::String> hint_string;
|
||||
if (parameters.generate_hints)
|
||||
{
|
||||
hint_string = builder->CreateString(Hint{phantom, facade.GetCheckSum()}.ToBase64());
|
||||
std::vector<SegmentHint> seg_hints(candidates.size());
|
||||
std::transform(candidates.begin(),
|
||||
candidates.end(),
|
||||
seg_hints.begin(),
|
||||
[this](const auto &phantom) {
|
||||
return SegmentHint{phantom, facade.GetCheckSum()};
|
||||
});
|
||||
Hint hint{std::move(seg_hints)};
|
||||
hint_string = builder->CreateString(hint.ToBase64());
|
||||
}
|
||||
|
||||
auto waypoint = std::make_unique<fbresult::WaypointBuilder>(*builder);
|
||||
waypoint->add_location(&location);
|
||||
waypoint->add_distance(util::coordinate_calculation::greatCircleDistance(
|
||||
phantom.location, phantom.input_location));
|
||||
waypoint->add_distance(
|
||||
util::coordinate_calculation::greatCircleDistance(snapped_location, input_location));
|
||||
waypoint->add_name(name_string);
|
||||
if (parameters.generate_hints)
|
||||
{
|
||||
|
||||
@ -51,14 +51,14 @@ namespace api
|
||||
* Holds member attributes:
|
||||
* - coordinates: for specifying location(s) to services
|
||||
* - hints: hint for the service to derive the position(s) in the road network more efficiently,
|
||||
* optional per coordinate
|
||||
* optional per coordinate. Multiple hints can be provided for a coordinate.
|
||||
* - radiuses: limits the search for segments in the road network to given radius(es) in meter,
|
||||
* optional per coordinate
|
||||
* - bearings: limits the search for segments in the road network to given bearing(s) in degree
|
||||
* towards true north in clockwise direction, optional per coordinate
|
||||
* - approaches: force the phantom node to start towards the node with the road country side.
|
||||
*
|
||||
* \see OSRM, Coordinate, Hint, Bearing, RouteParame, RouteParameters, TableParameters,
|
||||
* \see OSRM, Coordinate, Hint, Bearing, RouteParameters, TableParameters,
|
||||
* NearestParameters, TripParameters, MatchParameters and TileParameters
|
||||
*/
|
||||
struct BaseParameters
|
||||
|
||||
@ -76,7 +76,7 @@ class MatchAPI final : public RouteAPI
|
||||
routes.values.reserve(number_of_routes);
|
||||
for (auto index : util::irange<std::size_t>(0UL, sub_matchings.size()))
|
||||
{
|
||||
auto route = MakeRoute(sub_routes[index].segment_end_coordinates,
|
||||
auto route = MakeRoute(sub_routes[index].leg_endpoints,
|
||||
sub_routes[index].unpacked_path_segments,
|
||||
sub_routes[index].source_traversed_in_reverse,
|
||||
sub_routes[index].target_traversed_in_reverse);
|
||||
@ -146,7 +146,7 @@ class MatchAPI final : public RouteAPI
|
||||
}
|
||||
const auto &phantom =
|
||||
sub_matchings[matching_index.sub_matching_index].nodes[matching_index.point_index];
|
||||
auto waypoint = BaseAPI::MakeWaypoint(&fb_result, phantom);
|
||||
auto waypoint = BaseAPI::MakeWaypoint(&fb_result, {phantom});
|
||||
waypoint->add_matchings_index(matching_index.sub_matching_index);
|
||||
waypoint->add_alternatives_count(sub_matchings[matching_index.sub_matching_index]
|
||||
.alternatives_count[matching_index.point_index]);
|
||||
@ -200,7 +200,7 @@ class MatchAPI final : public RouteAPI
|
||||
}
|
||||
const auto &phantom =
|
||||
sub_matchings[matching_index.sub_matching_index].nodes[matching_index.point_index];
|
||||
auto waypoint = BaseAPI::MakeWaypoint(phantom);
|
||||
auto waypoint = BaseAPI::MakeWaypoint({phantom});
|
||||
waypoint.values["matchings_index"] = matching_index.sub_matching_index;
|
||||
waypoint.values["waypoint_index"] = matching_index.point_index;
|
||||
waypoint.values["alternatives_count"] =
|
||||
|
||||
@ -71,7 +71,7 @@ class NearestAPI final : public BaseAPI
|
||||
auto node_values = MakeNodes(phantom_node);
|
||||
fbresult::Uint64Pair nodes{node_values.first, node_values.second};
|
||||
|
||||
auto waypoint = MakeWaypoint(&fb_result, phantom_node);
|
||||
auto waypoint = MakeWaypoint(&fb_result, {phantom_node});
|
||||
waypoint->add_nodes(&nodes);
|
||||
return waypoint->Finish();
|
||||
});
|
||||
@ -100,7 +100,7 @@ class NearestAPI final : public BaseAPI
|
||||
waypoints.values.begin(),
|
||||
[this](const PhantomNodeWithDistance &phantom_with_distance) {
|
||||
auto &phantom_node = phantom_with_distance.phantom_node;
|
||||
auto waypoint = MakeWaypoint(phantom_node);
|
||||
auto waypoint = MakeWaypoint({phantom_node});
|
||||
|
||||
util::json::Array nodes;
|
||||
|
||||
|
||||
@ -48,8 +48,8 @@ class RouteAPI : public BaseAPI
|
||||
|
||||
void
|
||||
MakeResponse(const InternalManyRoutesResult &raw_routes,
|
||||
const std::vector<PhantomNodes>
|
||||
&all_start_end_points, // all used coordinates, ignoring waypoints= parameter
|
||||
const std::vector<PhantomNodeCandidates>
|
||||
&waypoint_candidates, // all used coordinates, ignoring waypoints= parameter
|
||||
osrm::engine::api::ResultT &response) const
|
||||
{
|
||||
BOOST_ASSERT(!raw_routes.routes.empty());
|
||||
@ -57,19 +57,19 @@ class RouteAPI : public BaseAPI
|
||||
if (response.is<flatbuffers::FlatBufferBuilder>())
|
||||
{
|
||||
auto &fb_result = response.get<flatbuffers::FlatBufferBuilder>();
|
||||
MakeResponse(raw_routes, all_start_end_points, fb_result);
|
||||
MakeResponse(raw_routes, waypoint_candidates, fb_result);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto &json_result = response.get<util::json::Object>();
|
||||
MakeResponse(raw_routes, all_start_end_points, json_result);
|
||||
MakeResponse(raw_routes, waypoint_candidates, json_result);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MakeResponse(const InternalManyRoutesResult &raw_routes,
|
||||
const std::vector<PhantomNodes>
|
||||
&all_start_end_points, // all used coordinates, ignoring waypoints= parameter
|
||||
const std::vector<PhantomNodeCandidates>
|
||||
&waypoint_candidates, // all used coordinates, ignoring waypoints= parameter
|
||||
flatbuffers::FlatBufferBuilder &fb_result) const
|
||||
{
|
||||
|
||||
@ -81,8 +81,8 @@ class RouteAPI : public BaseAPI
|
||||
}
|
||||
|
||||
auto response =
|
||||
MakeFBResponse(raw_routes, fb_result, [this, &all_start_end_points, &fb_result]() {
|
||||
return BaseAPI::MakeWaypoints(&fb_result, all_start_end_points);
|
||||
MakeFBResponse(raw_routes, fb_result, [this, &waypoint_candidates, &fb_result]() {
|
||||
return BaseAPI::MakeWaypoints(&fb_result, waypoint_candidates);
|
||||
});
|
||||
|
||||
if (!data_timestamp.empty())
|
||||
@ -94,8 +94,8 @@ class RouteAPI : public BaseAPI
|
||||
|
||||
void
|
||||
MakeResponse(const InternalManyRoutesResult &raw_routes,
|
||||
const std::vector<PhantomNodes>
|
||||
&all_start_end_points, // all used coordinates, ignoring waypoints= parameter
|
||||
const std::vector<PhantomNodeCandidates>
|
||||
&waypoint_candidates, // all used coordinates, ignoring waypoints= parameter
|
||||
util::json::Object &response) const
|
||||
{
|
||||
util::json::Array jsRoutes;
|
||||
@ -105,7 +105,7 @@ class RouteAPI : public BaseAPI
|
||||
if (!route.is_valid())
|
||||
continue;
|
||||
|
||||
jsRoutes.values.push_back(MakeRoute(route.segment_end_coordinates,
|
||||
jsRoutes.values.push_back(MakeRoute(route.leg_endpoints,
|
||||
route.unpacked_path_segments,
|
||||
route.source_traversed_in_reverse,
|
||||
route.target_traversed_in_reverse));
|
||||
@ -113,7 +113,7 @@ class RouteAPI : public BaseAPI
|
||||
|
||||
if (!parameters.skip_waypoints)
|
||||
{
|
||||
response.values["waypoints"] = BaseAPI::MakeWaypoints(all_start_end_points);
|
||||
response.values["waypoints"] = BaseAPI::MakeWaypoints(waypoint_candidates);
|
||||
}
|
||||
response.values["routes"] = std::move(jsRoutes);
|
||||
response.values["code"] = "Ok";
|
||||
@ -139,7 +139,7 @@ class RouteAPI : public BaseAPI
|
||||
continue;
|
||||
|
||||
routes.push_back(MakeRoute(fb_result,
|
||||
raw_route.segment_end_coordinates,
|
||||
raw_route.leg_endpoints,
|
||||
raw_route.unpacked_path_segments,
|
||||
raw_route.source_traversed_in_reverse,
|
||||
raw_route.target_traversed_in_reverse));
|
||||
@ -329,12 +329,12 @@ class RouteAPI : public BaseAPI
|
||||
|
||||
flatbuffers::Offset<fbresult::RouteObject>
|
||||
MakeRoute(flatbuffers::FlatBufferBuilder &fb_result,
|
||||
const std::vector<PhantomNodes> &segment_end_coordinates,
|
||||
const std::vector<PhantomEndpoints> &leg_endpoints,
|
||||
const std::vector<std::vector<PathData>> &unpacked_path_segments,
|
||||
const std::vector<bool> &source_traversed_in_reverse,
|
||||
const std::vector<bool> &target_traversed_in_reverse) const
|
||||
{
|
||||
auto legs_info = MakeLegs(segment_end_coordinates,
|
||||
auto legs_info = MakeLegs(leg_endpoints,
|
||||
unpacked_path_segments,
|
||||
source_traversed_in_reverse,
|
||||
target_traversed_in_reverse);
|
||||
@ -706,12 +706,12 @@ class RouteAPI : public BaseAPI
|
||||
return fb_result.CreateVector(intersections);
|
||||
}
|
||||
|
||||
util::json::Object MakeRoute(const std::vector<PhantomNodes> &segment_end_coordinates,
|
||||
util::json::Object MakeRoute(const std::vector<PhantomEndpoints> &leg_endpoints,
|
||||
const std::vector<std::vector<PathData>> &unpacked_path_segments,
|
||||
const std::vector<bool> &source_traversed_in_reverse,
|
||||
const std::vector<bool> &target_traversed_in_reverse) const
|
||||
{
|
||||
auto legs_info = MakeLegs(segment_end_coordinates,
|
||||
auto legs_info = MakeLegs(leg_endpoints,
|
||||
unpacked_path_segments,
|
||||
source_traversed_in_reverse,
|
||||
target_traversed_in_reverse);
|
||||
@ -869,7 +869,7 @@ class RouteAPI : public BaseAPI
|
||||
const RouteParameters ¶meters;
|
||||
|
||||
std::pair<std::vector<guidance::RouteLeg>, std::vector<guidance::LegGeometry>>
|
||||
MakeLegs(const std::vector<PhantomNodes> &segment_end_coordinates,
|
||||
MakeLegs(const std::vector<PhantomEndpoints> &leg_endpoints,
|
||||
const std::vector<std::vector<PathData>> &unpacked_path_segments,
|
||||
const std::vector<bool> &source_traversed_in_reverse,
|
||||
const std::vector<bool> &target_traversed_in_reverse) const
|
||||
@ -878,13 +878,13 @@ class RouteAPI : public BaseAPI
|
||||
std::make_pair(std::vector<guidance::RouteLeg>(), std::vector<guidance::LegGeometry>());
|
||||
auto &legs = result.first;
|
||||
auto &leg_geometries = result.second;
|
||||
auto number_of_legs = segment_end_coordinates.size();
|
||||
auto number_of_legs = leg_endpoints.size();
|
||||
legs.reserve(number_of_legs);
|
||||
leg_geometries.reserve(number_of_legs);
|
||||
|
||||
for (auto idx : util::irange<std::size_t>(0UL, number_of_legs))
|
||||
{
|
||||
const auto &phantoms = segment_end_coordinates[idx];
|
||||
const auto &phantoms = leg_endpoints[idx];
|
||||
const auto &path_data = unpacked_path_segments[idx];
|
||||
|
||||
const bool reversed_source = source_traversed_in_reverse[idx];
|
||||
|
||||
@ -32,6 +32,8 @@ namespace api
|
||||
class TableAPI final : public BaseAPI
|
||||
{
|
||||
public:
|
||||
virtual ~TableAPI() = default;
|
||||
|
||||
struct TableCellRef
|
||||
{
|
||||
TableCellRef(const std::size_t &row, const std::size_t &column) : row{row}, column{column}
|
||||
@ -48,25 +50,25 @@ class TableAPI final : public BaseAPI
|
||||
|
||||
virtual void
|
||||
MakeResponse(const std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>> &tables,
|
||||
const std::vector<PhantomNode> &phantoms,
|
||||
const std::vector<PhantomNodeCandidates> &candidates,
|
||||
const std::vector<TableCellRef> &fallback_speed_cells,
|
||||
osrm::engine::api::ResultT &response) const
|
||||
{
|
||||
if (response.is<flatbuffers::FlatBufferBuilder>())
|
||||
{
|
||||
auto &fb_result = response.get<flatbuffers::FlatBufferBuilder>();
|
||||
MakeResponse(tables, phantoms, fallback_speed_cells, fb_result);
|
||||
MakeResponse(tables, candidates, fallback_speed_cells, fb_result);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto &json_result = response.get<util::json::Object>();
|
||||
MakeResponse(tables, phantoms, fallback_speed_cells, json_result);
|
||||
MakeResponse(tables, candidates, fallback_speed_cells, json_result);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void
|
||||
MakeResponse(const std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>> &tables,
|
||||
const std::vector<PhantomNode> &phantoms,
|
||||
const std::vector<PhantomNodeCandidates> &candidates,
|
||||
const std::vector<TableCellRef> &fallback_speed_cells,
|
||||
flatbuffers::FlatBufferBuilder &fb_result) const
|
||||
{
|
||||
@ -86,15 +88,15 @@ class TableAPI final : public BaseAPI
|
||||
{
|
||||
if (!parameters.skip_waypoints)
|
||||
{
|
||||
sources = MakeWaypoints(fb_result, phantoms);
|
||||
sources = MakeWaypoints(fb_result, candidates);
|
||||
}
|
||||
number_of_sources = phantoms.size();
|
||||
number_of_sources = candidates.size();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!parameters.skip_waypoints)
|
||||
{
|
||||
sources = MakeWaypoints(fb_result, phantoms, parameters.sources);
|
||||
sources = MakeWaypoints(fb_result, candidates, parameters.sources);
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,15 +106,15 @@ class TableAPI final : public BaseAPI
|
||||
{
|
||||
if (!parameters.skip_waypoints)
|
||||
{
|
||||
destinations = MakeWaypoints(fb_result, phantoms);
|
||||
destinations = MakeWaypoints(fb_result, candidates);
|
||||
}
|
||||
number_of_destinations = phantoms.size();
|
||||
number_of_destinations = candidates.size();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!parameters.skip_waypoints)
|
||||
{
|
||||
destinations = MakeWaypoints(fb_result, phantoms, parameters.destinations);
|
||||
destinations = MakeWaypoints(fb_result, candidates, parameters.destinations);
|
||||
}
|
||||
}
|
||||
|
||||
@ -168,7 +170,7 @@ class TableAPI final : public BaseAPI
|
||||
|
||||
virtual void
|
||||
MakeResponse(const std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>> &tables,
|
||||
const std::vector<PhantomNode> &phantoms,
|
||||
const std::vector<PhantomNodeCandidates> &candidates,
|
||||
const std::vector<TableCellRef> &fallback_speed_cells,
|
||||
util::json::Object &response) const
|
||||
{
|
||||
@ -180,15 +182,15 @@ class TableAPI final : public BaseAPI
|
||||
{
|
||||
if (!parameters.skip_waypoints)
|
||||
{
|
||||
response.values["sources"] = MakeWaypoints(phantoms);
|
||||
response.values["sources"] = MakeWaypoints(candidates);
|
||||
}
|
||||
number_of_sources = phantoms.size();
|
||||
number_of_sources = candidates.size();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!parameters.skip_waypoints)
|
||||
{
|
||||
response.values["sources"] = MakeWaypoints(phantoms, parameters.sources);
|
||||
response.values["sources"] = MakeWaypoints(candidates, parameters.sources);
|
||||
}
|
||||
}
|
||||
|
||||
@ -196,15 +198,16 @@ class TableAPI final : public BaseAPI
|
||||
{
|
||||
if (!parameters.skip_waypoints)
|
||||
{
|
||||
response.values["destinations"] = MakeWaypoints(phantoms);
|
||||
response.values["destinations"] = MakeWaypoints(candidates);
|
||||
}
|
||||
number_of_destinations = phantoms.size();
|
||||
number_of_destinations = candidates.size();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!parameters.skip_waypoints)
|
||||
{
|
||||
response.values["destinations"] = MakeWaypoints(phantoms, parameters.destinations);
|
||||
response.values["destinations"] =
|
||||
MakeWaypoints(candidates, parameters.destinations);
|
||||
}
|
||||
}
|
||||
|
||||
@ -236,31 +239,33 @@ class TableAPI final : public BaseAPI
|
||||
protected:
|
||||
virtual flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<fbresult::Waypoint>>>
|
||||
MakeWaypoints(flatbuffers::FlatBufferBuilder &builder,
|
||||
const std::vector<PhantomNode> &phantoms) const
|
||||
const std::vector<PhantomNodeCandidates> &candidates) const
|
||||
{
|
||||
std::vector<flatbuffers::Offset<fbresult::Waypoint>> waypoints;
|
||||
waypoints.reserve(phantoms.size());
|
||||
BOOST_ASSERT(phantoms.size() == parameters.coordinates.size());
|
||||
waypoints.reserve(candidates.size());
|
||||
BOOST_ASSERT(candidates.size() == parameters.coordinates.size());
|
||||
|
||||
boost::range::transform(
|
||||
phantoms, std::back_inserter(waypoints), [this, &builder](const PhantomNode &phantom) {
|
||||
return BaseAPI::MakeWaypoint(&builder, phantom)->Finish();
|
||||
boost::range::transform(candidates,
|
||||
std::back_inserter(waypoints),
|
||||
[this, &builder](const PhantomNodeCandidates &candidates) {
|
||||
return BaseAPI::MakeWaypoint(&builder, candidates)->Finish();
|
||||
});
|
||||
return builder.CreateVector(waypoints);
|
||||
}
|
||||
|
||||
virtual flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<fbresult::Waypoint>>>
|
||||
MakeWaypoints(flatbuffers::FlatBufferBuilder &builder,
|
||||
const std::vector<PhantomNode> &phantoms,
|
||||
const std::vector<PhantomNodeCandidates> &candidates,
|
||||
const std::vector<std::size_t> &indices) const
|
||||
{
|
||||
std::vector<flatbuffers::Offset<fbresult::Waypoint>> waypoints;
|
||||
waypoints.reserve(indices.size());
|
||||
boost::range::transform(indices,
|
||||
boost::range::transform(
|
||||
indices,
|
||||
std::back_inserter(waypoints),
|
||||
[this, &builder, phantoms](const std::size_t idx) {
|
||||
BOOST_ASSERT(idx < phantoms.size());
|
||||
return BaseAPI::MakeWaypoint(&builder, phantoms[idx])->Finish();
|
||||
[this, &builder, &candidates](const std::size_t idx) {
|
||||
BOOST_ASSERT(idx < candidates.size());
|
||||
return BaseAPI::MakeWaypoint(&builder, candidates[idx])->Finish();
|
||||
});
|
||||
return builder.CreateVector(waypoints);
|
||||
}
|
||||
@ -313,29 +318,31 @@ class TableAPI final : public BaseAPI
|
||||
return builder.CreateVector(fb_table);
|
||||
}
|
||||
|
||||
virtual util::json::Array MakeWaypoints(const std::vector<PhantomNode> &phantoms) const
|
||||
virtual util::json::Array
|
||||
MakeWaypoints(const std::vector<PhantomNodeCandidates> &candidates) const
|
||||
{
|
||||
util::json::Array json_waypoints;
|
||||
json_waypoints.values.reserve(phantoms.size());
|
||||
BOOST_ASSERT(phantoms.size() == parameters.coordinates.size());
|
||||
json_waypoints.values.reserve(candidates.size());
|
||||
BOOST_ASSERT(candidates.size() == parameters.coordinates.size());
|
||||
|
||||
boost::range::transform(
|
||||
phantoms,
|
||||
boost::range::transform(candidates,
|
||||
std::back_inserter(json_waypoints.values),
|
||||
[this](const PhantomNode &phantom) { return BaseAPI::MakeWaypoint(phantom); });
|
||||
[this](const PhantomNodeCandidates &candidates) {
|
||||
return BaseAPI::MakeWaypoint(candidates);
|
||||
});
|
||||
return json_waypoints;
|
||||
}
|
||||
|
||||
virtual util::json::Array MakeWaypoints(const std::vector<PhantomNode> &phantoms,
|
||||
virtual util::json::Array MakeWaypoints(const std::vector<PhantomNodeCandidates> &candidates,
|
||||
const std::vector<std::size_t> &indices) const
|
||||
{
|
||||
util::json::Array json_waypoints;
|
||||
json_waypoints.values.reserve(indices.size());
|
||||
boost::range::transform(indices,
|
||||
std::back_inserter(json_waypoints.values),
|
||||
[this, phantoms](const std::size_t idx) {
|
||||
BOOST_ASSERT(idx < phantoms.size());
|
||||
return BaseAPI::MakeWaypoint(phantoms[idx]);
|
||||
[this, &candidates](const std::size_t idx) {
|
||||
BOOST_ASSERT(idx < candidates.size());
|
||||
return BaseAPI::MakeWaypoint(candidates[idx]);
|
||||
});
|
||||
return json_waypoints;
|
||||
}
|
||||
|
||||
@ -26,7 +26,7 @@ class TripAPI final : public RouteAPI
|
||||
}
|
||||
void MakeResponse(const std::vector<std::vector<NodeID>> &sub_trips,
|
||||
const std::vector<InternalRouteResult> &sub_routes,
|
||||
const std::vector<PhantomNode> &phantoms,
|
||||
const std::vector<PhantomNodeCandidates> &candidates,
|
||||
osrm::engine::api::ResultT &response) const
|
||||
{
|
||||
BOOST_ASSERT(sub_trips.size() == sub_routes.size());
|
||||
@ -34,17 +34,17 @@ class TripAPI final : public RouteAPI
|
||||
if (response.is<flatbuffers::FlatBufferBuilder>())
|
||||
{
|
||||
auto &fb_result = response.get<flatbuffers::FlatBufferBuilder>();
|
||||
MakeResponse(sub_trips, sub_routes, phantoms, fb_result);
|
||||
MakeResponse(sub_trips, sub_routes, candidates, fb_result);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto &json_result = response.get<util::json::Object>();
|
||||
MakeResponse(sub_trips, sub_routes, phantoms, json_result);
|
||||
MakeResponse(sub_trips, sub_routes, candidates, json_result);
|
||||
}
|
||||
}
|
||||
void MakeResponse(const std::vector<std::vector<NodeID>> &sub_trips,
|
||||
const std::vector<InternalRouteResult> &sub_routes,
|
||||
const std::vector<PhantomNode> &phantoms,
|
||||
const std::vector<PhantomNodeCandidates> &candidates,
|
||||
flatbuffers::FlatBufferBuilder &fb_result) const
|
||||
{
|
||||
auto data_timestamp = facade.GetTimestamp();
|
||||
@ -55,8 +55,8 @@ class TripAPI final : public RouteAPI
|
||||
}
|
||||
|
||||
auto response =
|
||||
MakeFBResponse(sub_routes, fb_result, [this, &fb_result, &sub_trips, &phantoms]() {
|
||||
return MakeWaypoints(fb_result, sub_trips, phantoms);
|
||||
MakeFBResponse(sub_routes, fb_result, [this, &fb_result, &sub_trips, &candidates]() {
|
||||
return MakeWaypoints(fb_result, sub_trips, candidates);
|
||||
});
|
||||
|
||||
if (!data_timestamp.empty())
|
||||
@ -67,7 +67,7 @@ class TripAPI final : public RouteAPI
|
||||
}
|
||||
void MakeResponse(const std::vector<std::vector<NodeID>> &sub_trips,
|
||||
const std::vector<InternalRouteResult> &sub_routes,
|
||||
const std::vector<PhantomNode> &phantoms,
|
||||
const std::vector<PhantomNodeCandidates> &candidates,
|
||||
util::json::Object &response) const
|
||||
{
|
||||
auto number_of_routes = sub_trips.size();
|
||||
@ -75,7 +75,7 @@ class TripAPI final : public RouteAPI
|
||||
routes.values.reserve(number_of_routes);
|
||||
for (auto index : util::irange<std::size_t>(0UL, sub_trips.size()))
|
||||
{
|
||||
auto route = MakeRoute(sub_routes[index].segment_end_coordinates,
|
||||
auto route = MakeRoute(sub_routes[index].leg_endpoints,
|
||||
sub_routes[index].unpacked_path_segments,
|
||||
sub_routes[index].source_traversed_in_reverse,
|
||||
sub_routes[index].target_traversed_in_reverse);
|
||||
@ -83,7 +83,7 @@ class TripAPI final : public RouteAPI
|
||||
}
|
||||
if (!parameters.skip_waypoints)
|
||||
{
|
||||
response.values["waypoints"] = MakeWaypoints(sub_trips, phantoms);
|
||||
response.values["waypoints"] = MakeWaypoints(sub_trips, candidates);
|
||||
}
|
||||
response.values["trips"] = std::move(routes);
|
||||
response.values["code"] = "Ok";
|
||||
@ -120,7 +120,7 @@ class TripAPI final : public RouteAPI
|
||||
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<fbresult::Waypoint>>>
|
||||
MakeWaypoints(flatbuffers::FlatBufferBuilder &fb_result,
|
||||
const std::vector<std::vector<NodeID>> &sub_trips,
|
||||
const std::vector<PhantomNode> &phantoms) const
|
||||
const std::vector<PhantomNodeCandidates> &candidates) const
|
||||
{
|
||||
std::vector<flatbuffers::Offset<fbresult::Waypoint>> waypoints;
|
||||
waypoints.reserve(parameters.coordinates.size());
|
||||
@ -132,7 +132,7 @@ class TripAPI final : public RouteAPI
|
||||
auto trip_index = input_idx_to_trip_idx[input_index];
|
||||
BOOST_ASSERT(!trip_index.NotUsed());
|
||||
|
||||
auto waypoint = BaseAPI::MakeWaypoint(&fb_result, phantoms[input_index]);
|
||||
auto waypoint = BaseAPI::MakeWaypoint(&fb_result, candidates[input_index]);
|
||||
waypoint->add_waypoint_index(trip_index.point_index);
|
||||
waypoint->add_trips_index(trip_index.sub_trip_index);
|
||||
waypoints.push_back(waypoint->Finish());
|
||||
@ -142,7 +142,7 @@ class TripAPI final : public RouteAPI
|
||||
}
|
||||
|
||||
util::json::Array MakeWaypoints(const std::vector<std::vector<NodeID>> &sub_trips,
|
||||
const std::vector<PhantomNode> &phantoms) const
|
||||
const std::vector<PhantomNodeCandidates> &candidates) const
|
||||
{
|
||||
util::json::Array waypoints;
|
||||
waypoints.values.reserve(parameters.coordinates.size());
|
||||
@ -154,7 +154,7 @@ class TripAPI final : public RouteAPI
|
||||
auto trip_index = input_idx_to_trip_idx[input_index];
|
||||
BOOST_ASSERT(!trip_index.NotUsed());
|
||||
|
||||
auto waypoint = BaseAPI::MakeWaypoint(phantoms[input_index]);
|
||||
auto waypoint = BaseAPI::MakeWaypoint(candidates[input_index]);
|
||||
waypoint.values["trips_index"] = trip_index.sub_trip_index;
|
||||
waypoint.values["waypoint_index"] = trip_index.point_index;
|
||||
waypoints.values.push_back(std::move(waypoint));
|
||||
|
||||
@ -31,6 +31,8 @@ template <> class AlgorithmDataFacade<CH>
|
||||
using EdgeData = contractor::QueryEdge::EdgeData;
|
||||
using EdgeRange = util::filtered_range<EdgeID, util::vector_view<bool>>;
|
||||
|
||||
virtual ~AlgorithmDataFacade() = default;
|
||||
|
||||
// search graph access
|
||||
virtual unsigned GetNumberOfNodes() const = 0;
|
||||
|
||||
@ -66,6 +68,8 @@ template <> class AlgorithmDataFacade<MLD>
|
||||
using EdgeData = customizer::EdgeBasedGraphEdgeData;
|
||||
using EdgeRange = util::range<EdgeID>;
|
||||
|
||||
virtual ~AlgorithmDataFacade() = default;
|
||||
|
||||
// search graph access
|
||||
virtual unsigned GetNumberOfNodes() const = 0;
|
||||
|
||||
|
||||
@ -323,127 +323,41 @@ class ContiguousInternalMemoryDataFacadeBase : public BaseDataFacade
|
||||
|
||||
std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodesInRange(const util::Coordinate input_coordinate,
|
||||
const float max_distance,
|
||||
const Approach approach,
|
||||
const bool use_all_edges) const override final
|
||||
{
|
||||
BOOST_ASSERT(m_geospatial_query.get());
|
||||
|
||||
return m_geospatial_query->NearestPhantomNodesInRange(
|
||||
input_coordinate, max_distance, approach, use_all_edges);
|
||||
}
|
||||
|
||||
std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodesInRange(const util::Coordinate input_coordinate,
|
||||
const float max_distance,
|
||||
const int bearing,
|
||||
const int bearing_range,
|
||||
const Approach approach,
|
||||
const bool use_all_edges) const override final
|
||||
{
|
||||
BOOST_ASSERT(m_geospatial_query.get());
|
||||
|
||||
return m_geospatial_query->NearestPhantomNodesInRange(
|
||||
input_coordinate, max_distance, bearing, bearing_range, approach, use_all_edges);
|
||||
}
|
||||
|
||||
std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodes(const util::Coordinate input_coordinate,
|
||||
const unsigned max_results,
|
||||
const Approach approach) const override final
|
||||
{
|
||||
BOOST_ASSERT(m_geospatial_query.get());
|
||||
|
||||
return m_geospatial_query->NearestPhantomNodes(input_coordinate, max_results, approach);
|
||||
}
|
||||
|
||||
std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodes(const util::Coordinate input_coordinate,
|
||||
const unsigned max_results,
|
||||
const double max_distance,
|
||||
const boost::optional<Bearing> bearing,
|
||||
const Approach approach,
|
||||
const bool use_all_edges) const override final
|
||||
{
|
||||
BOOST_ASSERT(m_geospatial_query.get());
|
||||
|
||||
return m_geospatial_query->NearestPhantomNodes(
|
||||
input_coordinate, approach, boost::none, max_distance, bearing, use_all_edges);
|
||||
}
|
||||
|
||||
std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodes(const util::Coordinate input_coordinate,
|
||||
const size_t max_results,
|
||||
const boost::optional<double> max_distance,
|
||||
const boost::optional<Bearing> bearing,
|
||||
const Approach approach) const override final
|
||||
{
|
||||
BOOST_ASSERT(m_geospatial_query.get());
|
||||
|
||||
return m_geospatial_query->NearestPhantomNodes(
|
||||
input_coordinate, max_results, max_distance, approach);
|
||||
input_coordinate, approach, max_results, max_distance, bearing, boost::none);
|
||||
}
|
||||
|
||||
std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodes(const util::Coordinate input_coordinate,
|
||||
const unsigned max_results,
|
||||
const int bearing,
|
||||
const int bearing_range,
|
||||
const Approach approach) const override final
|
||||
{
|
||||
BOOST_ASSERT(m_geospatial_query.get());
|
||||
|
||||
return m_geospatial_query->NearestPhantomNodes(
|
||||
input_coordinate, max_results, bearing, bearing_range, approach);
|
||||
}
|
||||
|
||||
std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodes(const util::Coordinate input_coordinate,
|
||||
const unsigned max_results,
|
||||
const double max_distance,
|
||||
const int bearing,
|
||||
const int bearing_range,
|
||||
const Approach approach) const override final
|
||||
{
|
||||
BOOST_ASSERT(m_geospatial_query.get());
|
||||
|
||||
return m_geospatial_query->NearestPhantomNodes(
|
||||
input_coordinate, max_results, max_distance, bearing, bearing_range, approach);
|
||||
}
|
||||
|
||||
std::pair<PhantomNode, PhantomNode>
|
||||
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
|
||||
PhantomCandidateAlternatives
|
||||
NearestCandidatesWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
|
||||
const boost::optional<double> max_distance,
|
||||
const boost::optional<Bearing> bearing,
|
||||
const Approach approach,
|
||||
const bool use_all_edges) const override final
|
||||
{
|
||||
BOOST_ASSERT(m_geospatial_query.get());
|
||||
|
||||
return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent(
|
||||
input_coordinate, approach, use_all_edges);
|
||||
}
|
||||
|
||||
std::pair<PhantomNode, PhantomNode>
|
||||
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
|
||||
const double max_distance,
|
||||
const Approach approach,
|
||||
const bool use_all_edges) const override final
|
||||
{
|
||||
BOOST_ASSERT(m_geospatial_query.get());
|
||||
|
||||
return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent(
|
||||
input_coordinate, max_distance, approach, use_all_edges);
|
||||
}
|
||||
|
||||
std::pair<PhantomNode, PhantomNode>
|
||||
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
|
||||
const double max_distance,
|
||||
const int bearing,
|
||||
const int bearing_range,
|
||||
const Approach approach,
|
||||
const bool use_all_edges) const override final
|
||||
{
|
||||
BOOST_ASSERT(m_geospatial_query.get());
|
||||
|
||||
return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent(
|
||||
input_coordinate, max_distance, bearing, bearing_range, approach, use_all_edges);
|
||||
}
|
||||
|
||||
std::pair<PhantomNode, PhantomNode>
|
||||
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
|
||||
const int bearing,
|
||||
const int bearing_range,
|
||||
const Approach approach,
|
||||
const bool use_all_edges) const override final
|
||||
{
|
||||
BOOST_ASSERT(m_geospatial_query.get());
|
||||
|
||||
return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent(
|
||||
input_coordinate, bearing, bearing_range, approach, use_all_edges);
|
||||
return m_geospatial_query->NearestCandidatesWithAlternativeFromBigComponent(
|
||||
input_coordinate, approach, max_distance, bearing, use_all_edges);
|
||||
}
|
||||
|
||||
std::uint32_t GetCheckSum() const override final { return m_check_sum; }
|
||||
|
||||
@ -35,6 +35,7 @@
|
||||
#include <boost/range/any_range.hpp>
|
||||
#include <cstddef>
|
||||
|
||||
#include <engine/bearing.hpp>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@ -130,62 +131,24 @@ class BaseDataFacade
|
||||
|
||||
virtual std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodesInRange(const util::Coordinate input_coordinate,
|
||||
const float max_distance,
|
||||
const int bearing,
|
||||
const int bearing_range,
|
||||
const Approach approach,
|
||||
const bool use_all_edges) const = 0;
|
||||
virtual std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodesInRange(const util::Coordinate input_coordinate,
|
||||
const float max_distance,
|
||||
const double max_distance,
|
||||
const boost::optional<Bearing> bearing,
|
||||
const Approach approach,
|
||||
const bool use_all_edges) const = 0;
|
||||
|
||||
virtual std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodes(const util::Coordinate input_coordinate,
|
||||
const unsigned max_results,
|
||||
const double max_distance,
|
||||
const int bearing,
|
||||
const int bearing_range,
|
||||
const Approach approach) const = 0;
|
||||
virtual std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodes(const util::Coordinate input_coordinate,
|
||||
const unsigned max_results,
|
||||
const int bearing,
|
||||
const int bearing_range,
|
||||
const Approach approach) const = 0;
|
||||
virtual std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodes(const util::Coordinate input_coordinate,
|
||||
const unsigned max_results,
|
||||
const Approach approach) const = 0;
|
||||
virtual std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodes(const util::Coordinate input_coordinate,
|
||||
const unsigned max_results,
|
||||
const double max_distance,
|
||||
const size_t max_results,
|
||||
const boost::optional<double> max_distance,
|
||||
const boost::optional<Bearing> bearing,
|
||||
const Approach approach) const = 0;
|
||||
|
||||
virtual std::pair<PhantomNode, PhantomNode>
|
||||
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
|
||||
virtual PhantomCandidateAlternatives
|
||||
NearestCandidatesWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
|
||||
const boost::optional<double> max_distance,
|
||||
const boost::optional<Bearing> bearing,
|
||||
const Approach approach,
|
||||
const bool use_all_edges) const = 0;
|
||||
virtual std::pair<PhantomNode, PhantomNode>
|
||||
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
|
||||
const double max_distance,
|
||||
const Approach approach,
|
||||
const bool use_all_edges) const = 0;
|
||||
virtual std::pair<PhantomNode, PhantomNode>
|
||||
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
|
||||
const double max_distance,
|
||||
const int bearing,
|
||||
const int bearing_range,
|
||||
const Approach approach,
|
||||
const bool use_all_edges) const = 0;
|
||||
virtual std::pair<PhantomNode, PhantomNode>
|
||||
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
|
||||
const int bearing,
|
||||
const int bearing_range,
|
||||
const Approach approach,
|
||||
const bool use_all_edges = false) const = 0;
|
||||
|
||||
virtual bool HasLaneData(const EdgeID edge_based_edge_id) const = 0;
|
||||
virtual util::guidance::LaneTupleIdPair GetLaneData(const EdgeID edge_based_edge_id) const = 0;
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
#define GEOSPATIAL_QUERY_HPP
|
||||
|
||||
#include "engine/approach.hpp"
|
||||
#include "engine/bearing.hpp"
|
||||
#include "engine/phantom_node.hpp"
|
||||
#include "util/bearing.hpp"
|
||||
#include "util/coordinate_calculation.hpp"
|
||||
@ -22,10 +23,10 @@ namespace osrm
|
||||
namespace engine
|
||||
{
|
||||
|
||||
inline std::pair<bool, bool> boolPairAnd(const std::pair<bool, bool> &A,
|
||||
const std::pair<bool, bool> &B)
|
||||
inline std::pair<bool, bool> operator&&(const std::pair<bool, bool> &a,
|
||||
const std::pair<bool, bool> &b)
|
||||
{
|
||||
return std::make_pair(A.first && B.first, A.second && B.second);
|
||||
return {a.first && b.first, a.second && b.second};
|
||||
}
|
||||
|
||||
// Implements complex queries on top of an RTree and builds PhantomNodes from it.
|
||||
@ -48,390 +49,241 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
|
||||
return rtree.SearchInBox(bbox);
|
||||
}
|
||||
|
||||
// Returns nearest PhantomNodes in the given bearing range within max_distance.
|
||||
// Returns max_results nearest PhantomNodes that are valid within the provided parameters.
|
||||
// Does not filter by small/big component!
|
||||
std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodesInRange(const util::Coordinate input_coordinate,
|
||||
const double max_distance,
|
||||
NearestPhantomNodes(const util::Coordinate input_coordinate,
|
||||
const Approach approach,
|
||||
const bool use_all_edges) const
|
||||
const boost::optional<size_t> max_results,
|
||||
const boost::optional<double> max_distance,
|
||||
const boost::optional<Bearing> bearing_with_range,
|
||||
const boost::optional<bool> use_all_edges) const
|
||||
{
|
||||
auto results = rtree.Nearest(
|
||||
input_coordinate,
|
||||
[this, approach, &input_coordinate, use_all_edges](const CandidateSegment &segment) {
|
||||
return boolPairAnd(
|
||||
boolPairAnd(HasValidEdge(segment, use_all_edges), CheckSegmentExclude(segment)),
|
||||
CheckApproach(input_coordinate, segment, approach));
|
||||
},
|
||||
[this, max_distance, input_coordinate](const std::size_t,
|
||||
[this, approach, &input_coordinate, &bearing_with_range, &use_all_edges](
|
||||
const CandidateSegment &segment) {
|
||||
return CheckSegmentDistance(input_coordinate, segment, max_distance);
|
||||
auto valid = CheckSegmentExclude(segment) &&
|
||||
CheckApproach(input_coordinate, segment, approach) &&
|
||||
(use_all_edges ? HasValidEdge(segment, *use_all_edges)
|
||||
: HasValidEdge(segment)) &&
|
||||
(bearing_with_range ? CheckSegmentBearing(segment, *bearing_with_range)
|
||||
: std::make_pair(true, true));
|
||||
return valid;
|
||||
},
|
||||
[this, &max_distance, &max_results, input_coordinate](const std::size_t num_results,
|
||||
const CandidateSegment &segment) {
|
||||
return (max_results && num_results >= *max_results) ||
|
||||
(max_distance &&
|
||||
CheckSegmentDistance(input_coordinate, segment, *max_distance));
|
||||
});
|
||||
|
||||
return MakePhantomNodes(input_coordinate, results);
|
||||
}
|
||||
|
||||
// Returns nearest PhantomNodes in the given bearing range within max_distance.
|
||||
// Does not filter by small/big component!
|
||||
std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodesInRange(const util::Coordinate input_coordinate,
|
||||
const double max_distance,
|
||||
const int bearing,
|
||||
const int bearing_range,
|
||||
// Returns a list of phantom node candidates from the nearest location that are valid
|
||||
// within the provided parameters. If there is tie between equidistant locations,
|
||||
// we only pick candidates from one location.
|
||||
// If candidates do not include a node from a big component, an alternative list of candidates
|
||||
// from the nearest location which has nodes from a big component is returned.
|
||||
PhantomCandidateAlternatives NearestCandidatesWithAlternativeFromBigComponent(
|
||||
const util::Coordinate input_coordinate,
|
||||
const Approach approach,
|
||||
const bool use_all_edges) const
|
||||
const boost::optional<double> max_distance,
|
||||
const boost::optional<Bearing> bearing_with_range,
|
||||
const boost::optional<bool> use_all_edges) const
|
||||
{
|
||||
auto results = rtree.Nearest(
|
||||
input_coordinate,
|
||||
[this, approach, &input_coordinate, bearing, bearing_range, use_all_edges](
|
||||
const CandidateSegment &segment) {
|
||||
auto use_direction =
|
||||
boolPairAnd(CheckSegmentBearing(segment, bearing, bearing_range),
|
||||
boolPairAnd(HasValidEdge(segment, use_all_edges),
|
||||
CheckSegmentExclude(segment)));
|
||||
use_direction =
|
||||
boolPairAnd(use_direction, CheckApproach(input_coordinate, segment, approach));
|
||||
return use_direction;
|
||||
},
|
||||
[this, max_distance, input_coordinate](const std::size_t,
|
||||
const CandidateSegment &segment) {
|
||||
return CheckSegmentDistance(input_coordinate, segment, max_distance);
|
||||
});
|
||||
|
||||
return MakePhantomNodes(input_coordinate, results);
|
||||
}
|
||||
|
||||
// Returns max_results nearest PhantomNodes in the given bearing range.
|
||||
// Does not filter by small/big component!
|
||||
std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodes(const util::Coordinate input_coordinate,
|
||||
const unsigned max_results,
|
||||
const int bearing,
|
||||
const int bearing_range,
|
||||
const Approach approach) const
|
||||
{
|
||||
auto results = rtree.Nearest(
|
||||
input_coordinate,
|
||||
[this, approach, &input_coordinate, bearing, bearing_range](
|
||||
const CandidateSegment &segment) {
|
||||
auto use_direction =
|
||||
boolPairAnd(CheckSegmentBearing(segment, bearing, bearing_range),
|
||||
boolPairAnd(HasValidEdge(segment), CheckSegmentExclude(segment)));
|
||||
return boolPairAnd(use_direction,
|
||||
CheckApproach(input_coordinate, segment, approach));
|
||||
},
|
||||
[max_results](const std::size_t num_results, const CandidateSegment &) {
|
||||
return num_results >= max_results;
|
||||
});
|
||||
|
||||
return MakePhantomNodes(input_coordinate, results);
|
||||
}
|
||||
|
||||
// Returns max_results nearest PhantomNodes in the given bearing range within the maximum
|
||||
// distance.
|
||||
// Does not filter by small/big component!
|
||||
std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodes(const util::Coordinate input_coordinate,
|
||||
const unsigned max_results,
|
||||
const double max_distance,
|
||||
const int bearing,
|
||||
const int bearing_range,
|
||||
const Approach approach) const
|
||||
{
|
||||
auto results = rtree.Nearest(
|
||||
input_coordinate,
|
||||
[this, approach, &input_coordinate, bearing, bearing_range](
|
||||
const CandidateSegment &segment) {
|
||||
auto use_direction =
|
||||
boolPairAnd(CheckSegmentBearing(segment, bearing, bearing_range),
|
||||
boolPairAnd(HasValidEdge(segment), CheckSegmentExclude(segment)));
|
||||
return boolPairAnd(use_direction,
|
||||
CheckApproach(input_coordinate, segment, approach));
|
||||
},
|
||||
[this, max_distance, max_results, input_coordinate](const std::size_t num_results,
|
||||
const CandidateSegment &segment) {
|
||||
return num_results >= max_results ||
|
||||
CheckSegmentDistance(input_coordinate, segment, max_distance);
|
||||
});
|
||||
|
||||
return MakePhantomNodes(input_coordinate, results);
|
||||
}
|
||||
|
||||
// Returns max_results nearest PhantomNodes.
|
||||
// Does not filter by small/big component!
|
||||
std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodes(const util::Coordinate input_coordinate,
|
||||
const unsigned max_results,
|
||||
const Approach approach) const
|
||||
{
|
||||
auto results = rtree.Nearest(
|
||||
input_coordinate,
|
||||
[this, approach, &input_coordinate](const CandidateSegment &segment) {
|
||||
return boolPairAnd(boolPairAnd(HasValidEdge(segment), CheckSegmentExclude(segment)),
|
||||
CheckApproach(input_coordinate, segment, approach));
|
||||
},
|
||||
[max_results](const std::size_t num_results, const CandidateSegment &) {
|
||||
return num_results >= max_results;
|
||||
});
|
||||
|
||||
return MakePhantomNodes(input_coordinate, results);
|
||||
}
|
||||
|
||||
// Returns max_results nearest PhantomNodes in the given max distance.
|
||||
// Does not filter by small/big component!
|
||||
std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodes(const util::Coordinate input_coordinate,
|
||||
const unsigned max_results,
|
||||
const double max_distance,
|
||||
const Approach approach) const
|
||||
{
|
||||
auto results = rtree.Nearest(
|
||||
input_coordinate,
|
||||
[this, approach, &input_coordinate](const CandidateSegment &segment) {
|
||||
return boolPairAnd(boolPairAnd(HasValidEdge(segment), CheckSegmentExclude(segment)),
|
||||
CheckApproach(input_coordinate, segment, approach));
|
||||
},
|
||||
[this, max_distance, max_results, input_coordinate](const std::size_t num_results,
|
||||
const CandidateSegment &segment) {
|
||||
return num_results >= max_results ||
|
||||
CheckSegmentDistance(input_coordinate, segment, max_distance);
|
||||
});
|
||||
|
||||
return MakePhantomNodes(input_coordinate, results);
|
||||
}
|
||||
|
||||
// Returns the nearest phantom node. If this phantom node is not from a big component
|
||||
// a second phantom node is return that is the nearest coordinate in a big component.
|
||||
std::pair<PhantomNode, PhantomNode>
|
||||
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
|
||||
const double max_distance,
|
||||
const Approach approach,
|
||||
const bool use_all_edges) const
|
||||
{
|
||||
bool has_small_component = false;
|
||||
bool has_nearest = false;
|
||||
bool has_big_component = false;
|
||||
Coordinate big_component_coord;
|
||||
double big_component_distance = std::numeric_limits<double>::max();
|
||||
Coordinate nearest_coord;
|
||||
auto results = rtree.Nearest(
|
||||
input_coordinate,
|
||||
[this,
|
||||
approach,
|
||||
&input_coordinate,
|
||||
&has_nearest,
|
||||
&has_big_component,
|
||||
&has_small_component,
|
||||
&use_all_edges](const CandidateSegment &segment) {
|
||||
auto use_segment =
|
||||
(!has_small_component || (!has_big_component && !IsTinyComponent(segment)));
|
||||
auto use_directions = std::make_pair(use_segment, use_segment);
|
||||
const auto valid_edges = HasValidEdge(segment, use_all_edges);
|
||||
const auto admissible_segments = CheckSegmentExclude(segment);
|
||||
use_directions = boolPairAnd(use_directions, admissible_segments);
|
||||
use_directions = boolPairAnd(use_directions, valid_edges);
|
||||
use_directions =
|
||||
boolPairAnd(use_directions, CheckApproach(input_coordinate, segment, approach));
|
||||
&nearest_coord,
|
||||
&big_component_coord,
|
||||
&big_component_distance,
|
||||
&use_all_edges,
|
||||
&bearing_with_range](const CandidateSegment &segment) {
|
||||
auto is_big_component = !IsTinyComponent(segment);
|
||||
auto not_nearest =
|
||||
has_nearest && segment.fixed_projected_coordinate != nearest_coord;
|
||||
auto not_big =
|
||||
has_big_component && segment.fixed_projected_coordinate != big_component_coord;
|
||||
|
||||
if (use_directions.first || use_directions.second)
|
||||
/**
|
||||
*
|
||||
* Two reasons why we don't want this candidate:
|
||||
* 1. A non-big component candidate that is not at the nearest location
|
||||
* 2. A big component candidate that is not at the big location.
|
||||
*
|
||||
* It's possible that 1. could end up having the same location as the nearest big
|
||||
* component node if we have yet to see one. However, we don't know this and it
|
||||
* could lead to buffering large numbers of candidates before finding the big
|
||||
* component location.
|
||||
* By filtering out 1. nodes, this does mean that the alternative list of
|
||||
* candidates will not have non-big component candidates. Given the alternative
|
||||
* list of big component candidates is meant as a backup choice, this seems
|
||||
* reasonable.
|
||||
*/
|
||||
if ((!is_big_component && not_nearest) || (is_big_component && not_big))
|
||||
{
|
||||
has_big_component = has_big_component || !IsTinyComponent(segment);
|
||||
has_small_component = has_small_component || IsTinyComponent(segment);
|
||||
return std::make_pair(false, false);
|
||||
}
|
||||
auto use_candidate =
|
||||
CheckSegmentExclude(segment) &&
|
||||
CheckApproach(input_coordinate, segment, approach) &&
|
||||
(use_all_edges ? HasValidEdge(segment, *use_all_edges)
|
||||
: HasValidEdge(segment)) &&
|
||||
(bearing_with_range ? CheckSegmentBearing(segment, *bearing_with_range)
|
||||
: std::make_pair(true, true));
|
||||
|
||||
if (use_candidate.first || use_candidate.second)
|
||||
{
|
||||
if (!has_nearest)
|
||||
{
|
||||
has_nearest = true;
|
||||
nearest_coord = segment.fixed_projected_coordinate;
|
||||
}
|
||||
if (is_big_component && !has_big_component)
|
||||
{
|
||||
has_big_component = true;
|
||||
big_component_coord = segment.fixed_projected_coordinate;
|
||||
big_component_distance = GetSegmentDistance(input_coordinate, segment);
|
||||
}
|
||||
}
|
||||
|
||||
return use_directions;
|
||||
return use_candidate;
|
||||
},
|
||||
[this, &has_big_component, max_distance, input_coordinate](
|
||||
const std::size_t num_results, const CandidateSegment &segment) {
|
||||
return (num_results > 0 && has_big_component) ||
|
||||
CheckSegmentDistance(input_coordinate, segment, max_distance);
|
||||
[this, &has_big_component, &max_distance, input_coordinate, &big_component_distance](
|
||||
const std::size_t /*num_results*/, const CandidateSegment &segment) {
|
||||
auto distance = GetSegmentDistance(input_coordinate, segment);
|
||||
auto further_than_big_component = distance > big_component_distance;
|
||||
auto no_more_candidates = has_big_component && further_than_big_component;
|
||||
auto too_far_away = max_distance && distance > *max_distance;
|
||||
|
||||
// Time to terminate the search when:
|
||||
// 1. We've found a node from a big component and the next candidate is further away
|
||||
// than that node.
|
||||
// 2. We're further away from the input then our max allowed distance.
|
||||
return no_more_candidates || too_far_away;
|
||||
});
|
||||
|
||||
if (results.size() == 0)
|
||||
{
|
||||
return std::make_pair(PhantomNode{}, PhantomNode{});
|
||||
}
|
||||
|
||||
BOOST_ASSERT(results.size() == 1 || results.size() == 2);
|
||||
return std::make_pair(MakePhantomNode(input_coordinate, results.front()).phantom_node,
|
||||
MakePhantomNode(input_coordinate, results.back()).phantom_node);
|
||||
}
|
||||
|
||||
// Returns the nearest phantom node. If this phantom node is not from a big component
|
||||
// a second phantom node is return that is the nearest coordinate in a big component.
|
||||
std::pair<PhantomNode, PhantomNode>
|
||||
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
|
||||
const Approach approach,
|
||||
const bool use_all_edges) const
|
||||
{
|
||||
bool has_small_component = false;
|
||||
bool has_big_component = false;
|
||||
auto results = rtree.Nearest(
|
||||
input_coordinate,
|
||||
[this,
|
||||
approach,
|
||||
&input_coordinate,
|
||||
&has_big_component,
|
||||
&has_small_component,
|
||||
&use_all_edges](const CandidateSegment &segment) {
|
||||
auto use_segment =
|
||||
(!has_small_component || (!has_big_component && !IsTinyComponent(segment)));
|
||||
auto use_directions = std::make_pair(use_segment, use_segment);
|
||||
|
||||
const auto valid_edges = HasValidEdge(segment, use_all_edges);
|
||||
const auto admissible_segments = CheckSegmentExclude(segment);
|
||||
use_directions = boolPairAnd(use_directions, admissible_segments);
|
||||
use_directions = boolPairAnd(use_directions, valid_edges);
|
||||
use_directions =
|
||||
boolPairAnd(use_directions, CheckApproach(input_coordinate, segment, approach));
|
||||
|
||||
if (use_directions.first || use_directions.second)
|
||||
{
|
||||
has_big_component = has_big_component || !IsTinyComponent(segment);
|
||||
has_small_component = has_small_component || IsTinyComponent(segment);
|
||||
}
|
||||
|
||||
return use_directions;
|
||||
},
|
||||
[&has_big_component](const std::size_t num_results, const CandidateSegment &) {
|
||||
return num_results > 0 && has_big_component;
|
||||
});
|
||||
|
||||
if (results.size() == 0)
|
||||
{
|
||||
return std::make_pair(PhantomNode{}, PhantomNode{});
|
||||
}
|
||||
|
||||
BOOST_ASSERT(results.size() == 1 || results.size() == 2);
|
||||
return std::make_pair(MakePhantomNode(input_coordinate, results.front()).phantom_node,
|
||||
MakePhantomNode(input_coordinate, results.back()).phantom_node);
|
||||
}
|
||||
|
||||
// Returns the nearest phantom node. If this phantom node is not from a big component
|
||||
// a second phantom node is return that is the nearest coordinate in a big component.
|
||||
std::pair<PhantomNode, PhantomNode>
|
||||
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
|
||||
const int bearing,
|
||||
const int bearing_range,
|
||||
const Approach approach,
|
||||
const bool use_all_edges) const
|
||||
{
|
||||
bool has_small_component = false;
|
||||
bool has_big_component = false;
|
||||
auto results = rtree.Nearest(
|
||||
input_coordinate,
|
||||
[this,
|
||||
approach,
|
||||
&input_coordinate,
|
||||
bearing,
|
||||
bearing_range,
|
||||
&has_big_component,
|
||||
&has_small_component,
|
||||
&use_all_edges](const CandidateSegment &segment) {
|
||||
auto use_segment =
|
||||
(!has_small_component || (!has_big_component && !IsTinyComponent(segment)));
|
||||
auto use_directions = std::make_pair(use_segment, use_segment);
|
||||
const auto admissible_segments = CheckSegmentExclude(segment);
|
||||
|
||||
if (use_segment)
|
||||
{
|
||||
use_directions =
|
||||
boolPairAnd(CheckSegmentBearing(segment, bearing, bearing_range),
|
||||
HasValidEdge(segment, use_all_edges));
|
||||
use_directions = boolPairAnd(use_directions, admissible_segments);
|
||||
use_directions = boolPairAnd(
|
||||
use_directions, CheckApproach(input_coordinate, segment, approach));
|
||||
|
||||
if (use_directions.first || use_directions.second)
|
||||
{
|
||||
has_big_component = has_big_component || !IsTinyComponent(segment);
|
||||
has_small_component = has_small_component || IsTinyComponent(segment);
|
||||
}
|
||||
}
|
||||
|
||||
return use_directions;
|
||||
},
|
||||
[&has_big_component](const std::size_t num_results, const CandidateSegment &) {
|
||||
return num_results > 0 && has_big_component;
|
||||
});
|
||||
|
||||
if (results.size() == 0)
|
||||
{
|
||||
return std::make_pair(PhantomNode{}, PhantomNode{});
|
||||
}
|
||||
|
||||
BOOST_ASSERT(results.size() > 0);
|
||||
return std::make_pair(MakePhantomNode(input_coordinate, results.front()).phantom_node,
|
||||
MakePhantomNode(input_coordinate, results.back()).phantom_node);
|
||||
}
|
||||
|
||||
// Returns the nearest phantom node. If this phantom node is not from a big component
|
||||
// a second phantom node is return that is the nearest coordinate in a big component.
|
||||
std::pair<PhantomNode, PhantomNode>
|
||||
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
|
||||
const double max_distance,
|
||||
const int bearing,
|
||||
const int bearing_range,
|
||||
const Approach approach,
|
||||
const bool use_all_edges) const
|
||||
{
|
||||
bool has_small_component = false;
|
||||
bool has_big_component = false;
|
||||
auto results = rtree.Nearest(
|
||||
input_coordinate,
|
||||
[this,
|
||||
approach,
|
||||
&input_coordinate,
|
||||
bearing,
|
||||
bearing_range,
|
||||
&has_big_component,
|
||||
&has_small_component,
|
||||
&use_all_edges](const CandidateSegment &segment) {
|
||||
auto use_segment =
|
||||
(!has_small_component || (!has_big_component && !IsTinyComponent(segment)));
|
||||
auto use_directions = std::make_pair(use_segment, use_segment);
|
||||
const auto admissible_segments = CheckSegmentExclude(segment);
|
||||
|
||||
if (use_segment)
|
||||
{
|
||||
use_directions =
|
||||
boolPairAnd(CheckSegmentBearing(segment, bearing, bearing_range),
|
||||
HasValidEdge(segment, use_all_edges));
|
||||
use_directions = boolPairAnd(use_directions, admissible_segments);
|
||||
use_directions = boolPairAnd(
|
||||
use_directions, CheckApproach(input_coordinate, segment, approach));
|
||||
|
||||
if (use_directions.first || use_directions.second)
|
||||
{
|
||||
has_big_component = has_big_component || !IsTinyComponent(segment);
|
||||
has_small_component = has_small_component || IsTinyComponent(segment);
|
||||
}
|
||||
}
|
||||
|
||||
return use_directions;
|
||||
},
|
||||
[this, &has_big_component, max_distance, input_coordinate](
|
||||
const std::size_t num_results, const CandidateSegment &segment) {
|
||||
return (num_results > 0 && has_big_component) ||
|
||||
CheckSegmentDistance(input_coordinate, segment, max_distance);
|
||||
});
|
||||
|
||||
if (results.size() == 0)
|
||||
{
|
||||
return std::make_pair(PhantomNode{}, PhantomNode{});
|
||||
}
|
||||
|
||||
BOOST_ASSERT(results.size() > 0);
|
||||
return std::make_pair(MakePhantomNode(input_coordinate, results.front()).phantom_node,
|
||||
MakePhantomNode(input_coordinate, results.back()).phantom_node);
|
||||
return MakeAlternativeBigCandidates(input_coordinate, nearest_coord, results);
|
||||
}
|
||||
|
||||
private:
|
||||
PhantomCandidateAlternatives
|
||||
MakeAlternativeBigCandidates(const util::Coordinate input_coordinate,
|
||||
const Coordinate nearest_coord,
|
||||
const std::vector<CandidateSegment> &results) const
|
||||
{
|
||||
if (results.size() == 0)
|
||||
{
|
||||
return std::make_pair(PhantomNodeCandidates{}, PhantomNodeCandidates{});
|
||||
}
|
||||
|
||||
PhantomNodeCandidates nearest_phantoms;
|
||||
PhantomNodeCandidates big_component_phantoms;
|
||||
|
||||
const auto add_to_candidates = [this, &input_coordinate](PhantomNodeCandidates &candidates,
|
||||
const EdgeData data) {
|
||||
auto candidate_it =
|
||||
std::find_if(candidates.begin(), candidates.end(), [&](const PhantomNode &node) {
|
||||
return data.forward_segment_id.id == node.forward_segment_id.id &&
|
||||
data.reverse_segment_id.id == node.reverse_segment_id.id;
|
||||
});
|
||||
if (candidate_it == candidates.end())
|
||||
{
|
||||
// First candidate from this segment
|
||||
candidates.push_back(MakePhantomNode(input_coordinate, data).phantom_node);
|
||||
}
|
||||
else
|
||||
{
|
||||
/**
|
||||
* Second candidate from this segment (there can be at most two).
|
||||
* We're snapping at the connection between two edges e1,e2 of the segment.
|
||||
*
|
||||
* | e1 | e2 |
|
||||
* | --- f1 --> | --- f2 --> |
|
||||
* | <-- r1 --- | <-- r2 --- |
|
||||
*
|
||||
* Most of the routing algorithms only support one candidate from each segment.
|
||||
* Therefore, we have to choose between e1 and e2.
|
||||
*
|
||||
* It makes sense to pick one edge over another if that edge offers more
|
||||
* opportunities to act as a source or target for a route.
|
||||
*
|
||||
* For consistency, we use the following logic:
|
||||
* "Pick e1 unless it makes sense to choose e2"
|
||||
*
|
||||
* Representing edge enabled as a truth table:
|
||||
* f1 | r1 | f2 | r2 | selected
|
||||
* ____________________________
|
||||
* t | t | t | t | e1
|
||||
* t | t | t | f | e1
|
||||
* t | t | f | t | e1
|
||||
* t | f | t | t | e2
|
||||
* t | f | t | f | e1
|
||||
* t | f | f | t | e1
|
||||
* f | t | t | t | e2
|
||||
* f | t | t | f | e1
|
||||
* f | t | f | t | e1
|
||||
*
|
||||
* The other rows in truth table don't appear as we discard an edge if both
|
||||
* forward and reverse are disabled.
|
||||
*
|
||||
**/
|
||||
if (candidate_it->fwd_segment_position < data.fwd_segment_position)
|
||||
{
|
||||
if (data.forward_segment_id.enabled && data.reverse_segment_id.enabled &&
|
||||
!(candidate_it->forward_segment_id.enabled &&
|
||||
candidate_it->reverse_segment_id.enabled))
|
||||
{
|
||||
*candidate_it = MakePhantomNode(input_coordinate, data).phantom_node;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!candidate_it->forward_segment_id.enabled ||
|
||||
!candidate_it->reverse_segment_id.enabled ||
|
||||
(data.forward_segment_id.enabled && data.reverse_segment_id.enabled))
|
||||
{
|
||||
*candidate_it = MakePhantomNode(input_coordinate, data).phantom_node;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::for_each(results.begin(), results.end(), [&](const CandidateSegment &segment) {
|
||||
if (segment.fixed_projected_coordinate == nearest_coord)
|
||||
{
|
||||
add_to_candidates(nearest_phantoms, segment.data);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Can only be from a big component for the alternative candidates
|
||||
add_to_candidates(big_component_phantoms, segment.data);
|
||||
}
|
||||
});
|
||||
return std::make_pair(std::move(nearest_phantoms), std::move(big_component_phantoms));
|
||||
}
|
||||
|
||||
std::vector<PhantomNodeWithDistance>
|
||||
MakePhantomNodes(const util::Coordinate input_coordinate,
|
||||
const std::vector<EdgeData> &results) const
|
||||
const std::vector<CandidateSegment> &results) const
|
||||
{
|
||||
std::vector<PhantomNodeWithDistance> distance_and_phantoms(results.size());
|
||||
std::transform(results.begin(),
|
||||
results.end(),
|
||||
distance_and_phantoms.begin(),
|
||||
[this, &input_coordinate](const EdgeData &data) {
|
||||
return MakePhantomNode(input_coordinate, data);
|
||||
[this, &input_coordinate](const CandidateSegment &segment) {
|
||||
return MakePhantomNode(input_coordinate, segment.data);
|
||||
});
|
||||
return distance_and_phantoms;
|
||||
}
|
||||
@ -580,9 +432,8 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
|
||||
return transformed;
|
||||
}
|
||||
|
||||
bool CheckSegmentDistance(const Coordinate input_coordinate,
|
||||
const CandidateSegment &segment,
|
||||
const double max_distance) const
|
||||
double GetSegmentDistance(const Coordinate input_coordinate,
|
||||
const CandidateSegment &segment) const
|
||||
{
|
||||
BOOST_ASSERT(segment.data.forward_segment_id.id != SPECIAL_SEGMENTID ||
|
||||
!segment.data.forward_segment_id.enabled);
|
||||
@ -593,7 +444,14 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
|
||||
util::web_mercator::toWGS84(segment.fixed_projected_coordinate);
|
||||
|
||||
return util::coordinate_calculation::greatCircleDistance(input_coordinate,
|
||||
wsg84_coordinate) > max_distance;
|
||||
wsg84_coordinate);
|
||||
}
|
||||
|
||||
bool CheckSegmentDistance(const Coordinate input_coordinate,
|
||||
const CandidateSegment &segment,
|
||||
const double max_distance) const
|
||||
{
|
||||
return GetSegmentDistance(input_coordinate, segment) > max_distance;
|
||||
}
|
||||
|
||||
std::pair<bool, bool> CheckSegmentExclude(const CandidateSegment &segment) const
|
||||
@ -616,8 +474,7 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
|
||||
}
|
||||
|
||||
std::pair<bool, bool> CheckSegmentBearing(const CandidateSegment &segment,
|
||||
const int filter_bearing,
|
||||
const int filter_bearing_range) const
|
||||
const Bearing filter_bearing) const
|
||||
{
|
||||
BOOST_ASSERT(segment.data.forward_segment_id.id != SPECIAL_SEGMENTID ||
|
||||
!segment.data.forward_segment_id.enabled);
|
||||
@ -633,11 +490,11 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
|
||||
|
||||
const bool forward_bearing_valid =
|
||||
util::bearing::CheckInBounds(
|
||||
std::round(forward_edge_bearing), filter_bearing, filter_bearing_range) &&
|
||||
std::round(forward_edge_bearing), filter_bearing.bearing, filter_bearing.range) &&
|
||||
segment.data.forward_segment_id.enabled;
|
||||
const bool backward_bearing_valid =
|
||||
util::bearing::CheckInBounds(
|
||||
std::round(backward_edge_bearing), filter_bearing, filter_bearing_range) &&
|
||||
std::round(backward_edge_bearing), filter_bearing.bearing, filter_bearing.range) &&
|
||||
segment.data.reverse_segment_id.enabled;
|
||||
return std::make_pair(forward_bearing_valid, backward_bearing_valid);
|
||||
}
|
||||
@ -645,7 +502,7 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
|
||||
/**
|
||||
* Checks to see if the edge weights are valid. We might have an edge,
|
||||
* but a traffic update might set the speed to 0 (weight == INVALID_SEGMENT_WEIGHT).
|
||||
* which means that this edge is not currently traversible. If this is the case,
|
||||
* which means that this edge is not currently traversable. If this is the case,
|
||||
* then we shouldn't snap to this edge.
|
||||
*/
|
||||
std::pair<bool, bool> HasValidEdge(const CandidateSegment &segment,
|
||||
@ -682,7 +539,7 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
|
||||
bool IsTinyComponent(const CandidateSegment &segment) const
|
||||
{
|
||||
const auto &data = segment.data;
|
||||
BOOST_ASSERT(data.forward_segment_id.enabled);
|
||||
BOOST_ASSERT(data.forward_segment_id.enabled || data.reverse_segment_id.enabled);
|
||||
BOOST_ASSERT(data.forward_segment_id.id != SPECIAL_NODEID);
|
||||
return datafacade.GetComponentID(data.forward_segment_id.id).is_tiny;
|
||||
}
|
||||
|
||||
@ -47,8 +47,9 @@ namespace datafacade
|
||||
class BaseDataFacade;
|
||||
}
|
||||
|
||||
// Is returned as a temporary identifier for snapped coodinates
|
||||
struct Hint
|
||||
// SegmentHint represents an individual segment position that could be used
|
||||
// as the waypoint for a given input location
|
||||
struct SegmentHint
|
||||
{
|
||||
PhantomNode phantom;
|
||||
std::uint32_t data_checksum;
|
||||
@ -57,16 +58,31 @@ struct Hint
|
||||
const datafacade::BaseDataFacade &facade) const;
|
||||
|
||||
std::string ToBase64() const;
|
||||
static Hint FromBase64(const std::string &base64Hint);
|
||||
static SegmentHint FromBase64(const std::string &base64Hint);
|
||||
|
||||
friend bool operator==(const Hint &, const Hint &);
|
||||
friend std::ostream &operator<<(std::ostream &, const Hint &);
|
||||
friend bool operator==(const SegmentHint &, const SegmentHint &);
|
||||
friend bool operator!=(const SegmentHint &, const SegmentHint &);
|
||||
friend std::ostream &operator<<(std::ostream &, const SegmentHint &);
|
||||
};
|
||||
|
||||
static_assert(sizeof(Hint) == 80 + 4, "Hint is bigger than expected");
|
||||
constexpr std::size_t ENCODED_HINT_SIZE = 112;
|
||||
static_assert(ENCODED_HINT_SIZE / 4 * 3 >= sizeof(Hint),
|
||||
"ENCODED_HINT_SIZE does not match size of Hint");
|
||||
// Hint represents the suggested segment positions that could be used
|
||||
// as the waypoint for a given input location
|
||||
struct Hint
|
||||
{
|
||||
std::vector<SegmentHint> segment_hints;
|
||||
|
||||
bool IsValid(const util::Coordinate new_input_coordinates,
|
||||
const datafacade::BaseDataFacade &facade) const;
|
||||
|
||||
std::string ToBase64() const;
|
||||
static Hint FromBase64(const std::string &base64Hint);
|
||||
};
|
||||
|
||||
static_assert(sizeof(SegmentHint) == 80 + 4, "Hint is bigger than expected");
|
||||
constexpr std::size_t ENCODED_SEGMENT_HINT_SIZE = 112;
|
||||
static_assert(ENCODED_SEGMENT_HINT_SIZE / 4 * 3 >= sizeof(SegmentHint),
|
||||
"ENCODED_SEGMENT_HINT_SIZE does not match size of SegmentHint");
|
||||
|
||||
} // namespace engine
|
||||
} // namespace osrm
|
||||
|
||||
|
||||
@ -50,7 +50,7 @@ struct PathData
|
||||
struct InternalRouteResult
|
||||
{
|
||||
std::vector<std::vector<PathData>> unpacked_path_segments;
|
||||
std::vector<PhantomNodes> segment_end_coordinates;
|
||||
std::vector<PhantomEndpoints> leg_endpoints;
|
||||
std::vector<bool> source_traversed_in_reverse;
|
||||
std::vector<bool> target_traversed_in_reverse;
|
||||
EdgeWeight shortest_path_weight = INVALID_EDGE_WEIGHT;
|
||||
@ -96,7 +96,7 @@ inline InternalRouteResult CollapseInternalRouteResult(const InternalRouteResult
|
||||
if (leggy_result.unpacked_path_segments.size() == 1)
|
||||
return leggy_result;
|
||||
|
||||
BOOST_ASSERT(leggy_result.segment_end_coordinates.size() > 1);
|
||||
BOOST_ASSERT(leggy_result.leg_endpoints.size() > 1);
|
||||
|
||||
InternalRouteResult collapsed;
|
||||
collapsed.shortest_path_weight = leggy_result.shortest_path_weight;
|
||||
@ -107,7 +107,7 @@ inline InternalRouteResult CollapseInternalRouteResult(const InternalRouteResult
|
||||
// start another leg vector
|
||||
collapsed.unpacked_path_segments.push_back(leggy_result.unpacked_path_segments[i]);
|
||||
// save new phantom node pair
|
||||
collapsed.segment_end_coordinates.push_back(leggy_result.segment_end_coordinates[i]);
|
||||
collapsed.leg_endpoints.push_back(leggy_result.leg_endpoints[i]);
|
||||
// save data about phantom nodes
|
||||
collapsed.source_traversed_in_reverse.push_back(
|
||||
leggy_result.source_traversed_in_reverse[i]);
|
||||
@ -119,9 +119,9 @@ inline InternalRouteResult CollapseInternalRouteResult(const InternalRouteResult
|
||||
{
|
||||
BOOST_ASSERT(!collapsed.unpacked_path_segments.empty());
|
||||
auto &last_segment = collapsed.unpacked_path_segments.back();
|
||||
BOOST_ASSERT(!collapsed.segment_end_coordinates.empty());
|
||||
collapsed.segment_end_coordinates.back().target_phantom =
|
||||
leggy_result.segment_end_coordinates[i].target_phantom;
|
||||
BOOST_ASSERT(!collapsed.leg_endpoints.empty());
|
||||
collapsed.leg_endpoints.back().target_phantom =
|
||||
leggy_result.leg_endpoints[i].target_phantom;
|
||||
collapsed.target_traversed_in_reverse.back() =
|
||||
leggy_result.target_traversed_in_reverse[i];
|
||||
// copy path segments into current leg
|
||||
@ -138,19 +138,18 @@ inline InternalRouteResult CollapseInternalRouteResult(const InternalRouteResult
|
||||
last_segment[old_size].weight_until_turn +=
|
||||
|
||||
leggy_result.source_traversed_in_reverse[i]
|
||||
? leggy_result.segment_end_coordinates[i].source_phantom.reverse_weight
|
||||
: leggy_result.segment_end_coordinates[i].source_phantom.forward_weight;
|
||||
? leggy_result.leg_endpoints[i].source_phantom.reverse_weight
|
||||
: leggy_result.leg_endpoints[i].source_phantom.forward_weight;
|
||||
|
||||
last_segment[old_size].duration_until_turn +=
|
||||
leggy_result.source_traversed_in_reverse[i]
|
||||
? leggy_result.segment_end_coordinates[i].source_phantom.reverse_duration
|
||||
: leggy_result.segment_end_coordinates[i].source_phantom.forward_duration;
|
||||
? leggy_result.leg_endpoints[i].source_phantom.reverse_duration
|
||||
: leggy_result.leg_endpoints[i].source_phantom.forward_duration;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_ASSERT(collapsed.segment_end_coordinates.size() ==
|
||||
collapsed.unpacked_path_segments.size());
|
||||
BOOST_ASSERT(collapsed.leg_endpoints.size() == collapsed.unpacked_path_segments.size());
|
||||
return collapsed;
|
||||
}
|
||||
} // namespace engine
|
||||
|
||||
@ -28,6 +28,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#ifndef OSRM_ENGINE_PHANTOM_NODES_H
|
||||
#define OSRM_ENGINE_PHANTOM_NODES_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "extractor/travel_mode.hpp"
|
||||
|
||||
#include "util/bearing.hpp"
|
||||
@ -223,7 +225,8 @@ struct PhantomNode
|
||||
|
||||
static_assert(sizeof(PhantomNode) == 80, "PhantomNode has more padding then expected");
|
||||
|
||||
using PhantomNodePair = std::pair<PhantomNode, PhantomNode>;
|
||||
using PhantomNodeCandidates = std::vector<PhantomNode>;
|
||||
using PhantomCandidateAlternatives = std::pair<PhantomNodeCandidates, PhantomNodeCandidates>;
|
||||
|
||||
struct PhantomNodeWithDistance
|
||||
{
|
||||
@ -231,11 +234,44 @@ struct PhantomNodeWithDistance
|
||||
double distance;
|
||||
};
|
||||
|
||||
struct PhantomNodes
|
||||
struct PhantomEndpointCandidates
|
||||
{
|
||||
const PhantomNodeCandidates &source_phantoms;
|
||||
const PhantomNodeCandidates &target_phantoms;
|
||||
};
|
||||
|
||||
struct PhantomCandidatesToTarget
|
||||
{
|
||||
const PhantomNodeCandidates &source_phantoms;
|
||||
const PhantomNode &target_phantom;
|
||||
};
|
||||
|
||||
inline util::Coordinate candidatesSnappedLocation(const PhantomNodeCandidates &candidates)
|
||||
{
|
||||
BOOST_ASSERT(!candidates.empty());
|
||||
return candidates.front().location;
|
||||
}
|
||||
|
||||
inline util::Coordinate candidatesInputLocation(const PhantomNodeCandidates &candidates)
|
||||
{
|
||||
BOOST_ASSERT(!candidates.empty());
|
||||
return candidates.front().input_location;
|
||||
}
|
||||
|
||||
inline bool candidatesHaveComponent(const PhantomNodeCandidates &candidates, uint32_t component_id)
|
||||
{
|
||||
return std::any_of(
|
||||
candidates.begin(), candidates.end(), [component_id](const PhantomNode &node) {
|
||||
return node.component.id == component_id;
|
||||
});
|
||||
}
|
||||
|
||||
struct PhantomEndpoints
|
||||
{
|
||||
PhantomNode source_phantom;
|
||||
PhantomNode target_phantom;
|
||||
};
|
||||
|
||||
} // namespace engine
|
||||
} // namespace osrm
|
||||
|
||||
|
||||
@ -99,64 +99,67 @@ class BasePlugin
|
||||
return Status::Error;
|
||||
}
|
||||
|
||||
// Decides whether to use the phantom node from a big or small component if both are found.
|
||||
// Returns true if all phantom nodes are in the same component after snapping.
|
||||
std::vector<PhantomNode>
|
||||
SnapPhantomNodes(const std::vector<PhantomNodePair> &phantom_node_pair_list) const
|
||||
// Decides whether to use the phantom candidates from big or small components if both are found.
|
||||
std::vector<PhantomNodeCandidates>
|
||||
SnapPhantomNodes(std::vector<PhantomCandidateAlternatives> alternatives_list) const
|
||||
{
|
||||
const auto check_component_id_is_tiny =
|
||||
[](const std::pair<PhantomNode, PhantomNode> &phantom_pair) {
|
||||
return phantom_pair.first.component.is_tiny;
|
||||
};
|
||||
|
||||
// are all phantoms from a tiny cc?
|
||||
const auto check_all_in_same_component =
|
||||
[](const std::vector<std::pair<PhantomNode, PhantomNode>> &nodes) {
|
||||
const auto component_id = nodes.front().first.component.id;
|
||||
|
||||
return std::all_of(std::begin(nodes),
|
||||
std::end(nodes),
|
||||
[component_id](const PhantomNodePair &phantom_pair) {
|
||||
return component_id == phantom_pair.first.component.id;
|
||||
const auto all_in_same_tiny_component =
|
||||
[](const std::vector<PhantomCandidateAlternatives> &alts_list) {
|
||||
return std::any_of(
|
||||
alts_list.front().first.begin(),
|
||||
alts_list.front().first.end(),
|
||||
// For each of the first possible phantoms, check if all other
|
||||
// positions in the list have a phantom from the same small component.
|
||||
[&](const PhantomNode &phantom) {
|
||||
if (!phantom.component.is_tiny)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const auto component_id = phantom.component.id;
|
||||
return std::all_of(
|
||||
std::next(alts_list.begin()),
|
||||
std::end(alts_list),
|
||||
[component_id](const PhantomCandidateAlternatives &alternatives) {
|
||||
return candidatesHaveComponent(alternatives.first, component_id);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const auto fallback_to_big_component =
|
||||
[](const std::pair<PhantomNode, PhantomNode> &phantom_pair) {
|
||||
if (phantom_pair.first.component.is_tiny && phantom_pair.second.IsValid() &&
|
||||
!phantom_pair.second.component.is_tiny)
|
||||
{
|
||||
return phantom_pair.second;
|
||||
}
|
||||
return phantom_pair.first;
|
||||
// Move the alternative into the final list
|
||||
const auto fallback_to_big_component = [](PhantomCandidateAlternatives &alternatives) {
|
||||
auto no_big_alternative = alternatives.second.empty();
|
||||
return no_big_alternative ? std::move(alternatives.first)
|
||||
: std::move(alternatives.second);
|
||||
};
|
||||
|
||||
const auto use_closed_phantom =
|
||||
[](const std::pair<PhantomNode, PhantomNode> &phantom_pair) {
|
||||
return phantom_pair.first;
|
||||
// Move the alternative into the final list
|
||||
const auto use_closed_phantom = [](PhantomCandidateAlternatives &alternatives) {
|
||||
return std::move(alternatives.first);
|
||||
};
|
||||
|
||||
const bool every_phantom_is_in_tiny_cc = std::all_of(std::begin(phantom_node_pair_list),
|
||||
std::end(phantom_node_pair_list),
|
||||
check_component_id_is_tiny);
|
||||
auto all_in_same_component = check_all_in_same_component(phantom_node_pair_list);
|
||||
|
||||
std::vector<PhantomNode> snapped_phantoms;
|
||||
snapped_phantoms.reserve(phantom_node_pair_list.size());
|
||||
const auto no_alternatives =
|
||||
std::all_of(alternatives_list.begin(),
|
||||
alternatives_list.end(),
|
||||
[](const PhantomCandidateAlternatives &alternatives) {
|
||||
return alternatives.second.empty();
|
||||
});
|
||||
|
||||
std::vector<PhantomNodeCandidates> snapped_phantoms;
|
||||
snapped_phantoms.reserve(alternatives_list.size());
|
||||
// The only case we don't snap to the big component if all phantoms are in the same small
|
||||
// component
|
||||
if (every_phantom_is_in_tiny_cc && all_in_same_component)
|
||||
if (no_alternatives || all_in_same_tiny_component(alternatives_list))
|
||||
{
|
||||
std::transform(phantom_node_pair_list.begin(),
|
||||
phantom_node_pair_list.end(),
|
||||
std::transform(alternatives_list.begin(),
|
||||
alternatives_list.end(),
|
||||
std::back_inserter(snapped_phantoms),
|
||||
use_closed_phantom);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::transform(phantom_node_pair_list.begin(),
|
||||
phantom_node_pair_list.end(),
|
||||
std::transform(alternatives_list.begin(),
|
||||
alternatives_list.end(),
|
||||
std::back_inserter(snapped_phantoms),
|
||||
fallback_to_big_component);
|
||||
}
|
||||
@ -181,35 +184,26 @@ class BasePlugin
|
||||
|
||||
for (const auto i : util::irange<std::size_t>(0UL, parameters.coordinates.size()))
|
||||
{
|
||||
Approach approach = engine::Approach::UNRESTRICTED;
|
||||
if (use_approaches && parameters.approaches[i])
|
||||
approach = parameters.approaches[i].get();
|
||||
|
||||
if (use_hints && parameters.hints[i] &&
|
||||
if (use_hints && parameters.hints[i] && !parameters.hints[i]->segment_hints.empty() &&
|
||||
parameters.hints[i]->IsValid(parameters.coordinates[i], facade))
|
||||
{
|
||||
for (const auto &seg_hint : parameters.hints[i]->segment_hints)
|
||||
{
|
||||
phantom_nodes[i].push_back(PhantomNodeWithDistance{
|
||||
parameters.hints[i]->phantom,
|
||||
seg_hint.phantom,
|
||||
util::coordinate_calculation::greatCircleDistance(
|
||||
parameters.coordinates[i], parameters.hints[i]->phantom.location),
|
||||
});
|
||||
parameters.coordinates[i], seg_hint.phantom.location)});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (use_bearings && parameters.bearings[i])
|
||||
{
|
||||
phantom_nodes[i] =
|
||||
facade.NearestPhantomNodesInRange(parameters.coordinates[i],
|
||||
radiuses[i],
|
||||
parameters.bearings[i]->bearing,
|
||||
parameters.bearings[i]->range,
|
||||
approach,
|
||||
use_all_edges);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
phantom_nodes[i] = facade.NearestPhantomNodesInRange(
|
||||
parameters.coordinates[i], radiuses[i], approach, use_all_edges);
|
||||
}
|
||||
parameters.coordinates[i],
|
||||
radiuses[i],
|
||||
use_bearings ? parameters.bearings[i] : boost::none,
|
||||
use_approaches && parameters.approaches[i] ? parameters.approaches[i].get()
|
||||
: engine::Approach::UNRESTRICTED,
|
||||
use_all_edges);
|
||||
}
|
||||
|
||||
return phantom_nodes;
|
||||
@ -218,7 +212,7 @@ class BasePlugin
|
||||
std::vector<std::vector<PhantomNodeWithDistance>>
|
||||
GetPhantomNodes(const datafacade::BaseDataFacade &facade,
|
||||
const api::BaseParameters ¶meters,
|
||||
unsigned number_of_results) const
|
||||
size_t number_of_results) const
|
||||
{
|
||||
std::vector<std::vector<PhantomNodeWithDistance>> phantom_nodes(
|
||||
parameters.coordinates.size());
|
||||
@ -231,56 +225,26 @@ class BasePlugin
|
||||
BOOST_ASSERT(parameters.IsValid());
|
||||
for (const auto i : util::irange<std::size_t>(0UL, parameters.coordinates.size()))
|
||||
{
|
||||
Approach approach = engine::Approach::UNRESTRICTED;
|
||||
if (use_approaches && parameters.approaches[i])
|
||||
approach = parameters.approaches[i].get();
|
||||
|
||||
if (use_hints && parameters.hints[i] &&
|
||||
if (use_hints && parameters.hints[i] && !parameters.hints[i]->segment_hints.empty() &&
|
||||
parameters.hints[i]->IsValid(parameters.coordinates[i], facade))
|
||||
{
|
||||
for (const auto &seg_hint : parameters.hints[i]->segment_hints)
|
||||
{
|
||||
phantom_nodes[i].push_back(PhantomNodeWithDistance{
|
||||
parameters.hints[i]->phantom,
|
||||
seg_hint.phantom,
|
||||
util::coordinate_calculation::greatCircleDistance(
|
||||
parameters.coordinates[i], parameters.hints[i]->phantom.location),
|
||||
});
|
||||
parameters.coordinates[i], seg_hint.phantom.location)});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (use_bearings && parameters.bearings[i])
|
||||
{
|
||||
if (use_radiuses && parameters.radiuses[i])
|
||||
{
|
||||
phantom_nodes[i] = facade.NearestPhantomNodes(parameters.coordinates[i],
|
||||
number_of_results,
|
||||
*parameters.radiuses[i],
|
||||
parameters.bearings[i]->bearing,
|
||||
parameters.bearings[i]->range,
|
||||
approach);
|
||||
}
|
||||
else
|
||||
{
|
||||
phantom_nodes[i] = facade.NearestPhantomNodes(parameters.coordinates[i],
|
||||
number_of_results,
|
||||
parameters.bearings[i]->bearing,
|
||||
parameters.bearings[i]->range,
|
||||
approach);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (use_radiuses && parameters.radiuses[i])
|
||||
{
|
||||
phantom_nodes[i] = facade.NearestPhantomNodes(parameters.coordinates[i],
|
||||
number_of_results,
|
||||
*parameters.radiuses[i],
|
||||
approach);
|
||||
}
|
||||
else
|
||||
{
|
||||
phantom_nodes[i] = facade.NearestPhantomNodes(
|
||||
parameters.coordinates[i], number_of_results, approach);
|
||||
}
|
||||
}
|
||||
parameters.coordinates[i],
|
||||
number_of_results,
|
||||
use_radiuses ? parameters.radiuses[i] : boost::none,
|
||||
use_bearings ? parameters.bearings[i] : boost::none,
|
||||
use_approaches && parameters.approaches[i] ? parameters.approaches[i].get()
|
||||
: engine::Approach::UNRESTRICTED);
|
||||
|
||||
// we didn't find a fitting node, return error
|
||||
if (phantom_nodes[i].empty())
|
||||
@ -291,10 +255,11 @@ class BasePlugin
|
||||
return phantom_nodes;
|
||||
}
|
||||
|
||||
std::vector<PhantomNodePair> GetPhantomNodes(const datafacade::BaseDataFacade &facade,
|
||||
std::vector<PhantomCandidateAlternatives>
|
||||
GetPhantomNodes(const datafacade::BaseDataFacade &facade,
|
||||
const api::BaseParameters ¶meters) const
|
||||
{
|
||||
std::vector<PhantomNodePair> phantom_node_pairs(parameters.coordinates.size());
|
||||
std::vector<PhantomCandidateAlternatives> alternatives(parameters.coordinates.size());
|
||||
|
||||
const bool use_hints = !parameters.hints.empty();
|
||||
const bool use_bearings = !parameters.bearings.empty();
|
||||
@ -305,87 +270,57 @@ class BasePlugin
|
||||
BOOST_ASSERT(parameters.IsValid());
|
||||
for (const auto i : util::irange<std::size_t>(0UL, parameters.coordinates.size()))
|
||||
{
|
||||
Approach approach = engine::Approach::UNRESTRICTED;
|
||||
if (use_approaches && parameters.approaches[i])
|
||||
approach = parameters.approaches[i].get();
|
||||
|
||||
if (use_hints && parameters.hints[i] &&
|
||||
if (use_hints && parameters.hints[i] && !parameters.hints[i]->segment_hints.empty() &&
|
||||
parameters.hints[i]->IsValid(parameters.coordinates[i], facade))
|
||||
{
|
||||
phantom_node_pairs[i].first = parameters.hints[i]->phantom;
|
||||
std::transform(parameters.hints[i]->segment_hints.begin(),
|
||||
parameters.hints[i]->segment_hints.end(),
|
||||
std::back_inserter(alternatives[i].first),
|
||||
[](const auto &seg_hint) { return seg_hint.phantom; });
|
||||
// we don't set the second one - it will be marked as invalid
|
||||
continue;
|
||||
}
|
||||
|
||||
if (use_bearings && parameters.bearings[i])
|
||||
{
|
||||
if (use_radiuses && parameters.radiuses[i])
|
||||
{
|
||||
phantom_node_pairs[i] =
|
||||
facade.NearestPhantomNodeWithAlternativeFromBigComponent(
|
||||
alternatives[i] = facade.NearestCandidatesWithAlternativeFromBigComponent(
|
||||
parameters.coordinates[i],
|
||||
*parameters.radiuses[i],
|
||||
parameters.bearings[i]->bearing,
|
||||
parameters.bearings[i]->range,
|
||||
approach,
|
||||
use_radiuses ? parameters.radiuses[i] : boost::none,
|
||||
use_bearings ? parameters.bearings[i] : boost::none,
|
||||
use_approaches && parameters.approaches[i] ? parameters.approaches[i].get()
|
||||
: engine::Approach::UNRESTRICTED,
|
||||
use_all_edges);
|
||||
}
|
||||
else
|
||||
{
|
||||
phantom_node_pairs[i] =
|
||||
facade.NearestPhantomNodeWithAlternativeFromBigComponent(
|
||||
parameters.coordinates[i],
|
||||
parameters.bearings[i]->bearing,
|
||||
parameters.bearings[i]->range,
|
||||
approach,
|
||||
use_all_edges);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (use_radiuses && parameters.radiuses[i])
|
||||
{
|
||||
phantom_node_pairs[i] =
|
||||
facade.NearestPhantomNodeWithAlternativeFromBigComponent(
|
||||
parameters.coordinates[i],
|
||||
*parameters.radiuses[i],
|
||||
approach,
|
||||
use_all_edges);
|
||||
}
|
||||
else
|
||||
{
|
||||
phantom_node_pairs[i] =
|
||||
facade.NearestPhantomNodeWithAlternativeFromBigComponent(
|
||||
parameters.coordinates[i], approach, use_all_edges);
|
||||
}
|
||||
}
|
||||
|
||||
// we didn't find a fitting node, return error
|
||||
if (!phantom_node_pairs[i].first.IsValid())
|
||||
if (alternatives[i].first.empty())
|
||||
{
|
||||
// This ensures the list of phantom nodes only consists of valid nodes.
|
||||
// We can use this on the call-site to detect an error.
|
||||
phantom_node_pairs.pop_back();
|
||||
alternatives.pop_back();
|
||||
break;
|
||||
}
|
||||
BOOST_ASSERT(phantom_node_pairs[i].first.IsValid());
|
||||
BOOST_ASSERT(phantom_node_pairs[i].second.IsValid());
|
||||
|
||||
BOOST_ASSERT(!alternatives[i].first.empty());
|
||||
}
|
||||
return phantom_node_pairs;
|
||||
return alternatives;
|
||||
}
|
||||
|
||||
std::string MissingPhantomErrorMessage(const std::vector<PhantomNodePair> &phantom_nodes,
|
||||
std::string
|
||||
MissingPhantomErrorMessage(const std::vector<PhantomCandidateAlternatives> &alternatives,
|
||||
const std::vector<util::Coordinate> &coordinates) const
|
||||
{
|
||||
BOOST_ASSERT(phantom_nodes.size() < coordinates.size());
|
||||
auto mismatch = std::mismatch(phantom_nodes.begin(),
|
||||
phantom_nodes.end(),
|
||||
BOOST_ASSERT(alternatives.size() < coordinates.size());
|
||||
auto mismatch =
|
||||
std::mismatch(alternatives.begin(),
|
||||
alternatives.end(),
|
||||
coordinates.begin(),
|
||||
coordinates.end(),
|
||||
[](const auto &phantom_node, const auto &coordinate) {
|
||||
return phantom_node.first.input_location == coordinate;
|
||||
[](const auto &candidates_pair, const auto &coordinate) {
|
||||
return std::any_of(candidates_pair.first.begin(),
|
||||
candidates_pair.first.end(),
|
||||
[&](const auto &phantom) {
|
||||
return phantom.input_location == coordinate;
|
||||
});
|
||||
std::size_t missing_index = std::distance(phantom_nodes.begin(), mismatch.first);
|
||||
});
|
||||
std::size_t missing_index = std::distance(alternatives.begin(), mismatch.first);
|
||||
return std::string("Could not find a matching segment for coordinate ") +
|
||||
std::to_string(missing_index);
|
||||
}
|
||||
|
||||
@ -31,7 +31,7 @@ class TripPlugin final : public BasePlugin
|
||||
const int max_locations_trip;
|
||||
|
||||
InternalRouteResult ComputeRoute(const RoutingAlgorithmsInterface &algorithms,
|
||||
const std::vector<PhantomNode> &phantom_node_list,
|
||||
const std::vector<PhantomNodeCandidates> &candidates_list,
|
||||
const std::vector<NodeID> &trip,
|
||||
const bool roundtrip) const;
|
||||
|
||||
|
||||
@ -19,19 +19,21 @@ namespace engine
|
||||
class RoutingAlgorithmsInterface
|
||||
{
|
||||
public:
|
||||
virtual ~RoutingAlgorithmsInterface() = default;
|
||||
|
||||
virtual InternalManyRoutesResult
|
||||
AlternativePathSearch(const PhantomNodes &phantom_node_pair,
|
||||
AlternativePathSearch(const PhantomEndpointCandidates &endpoint_candidates,
|
||||
unsigned number_of_alternatives) const = 0;
|
||||
|
||||
virtual InternalRouteResult
|
||||
ShortestPathSearch(const std::vector<PhantomNodes> &phantom_node_pair,
|
||||
ShortestPathSearch(const std::vector<PhantomNodeCandidates> &waypoint_candidates,
|
||||
const boost::optional<bool> continue_straight_at_waypoint) const = 0;
|
||||
|
||||
virtual InternalRouteResult
|
||||
DirectShortestPathSearch(const PhantomNodes &phantom_node_pair) const = 0;
|
||||
DirectShortestPathSearch(const PhantomEndpointCandidates &endpoint_candidates) const = 0;
|
||||
|
||||
virtual std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
|
||||
ManyToManySearch(const std::vector<PhantomNode> &phantom_nodes,
|
||||
ManyToManySearch(const std::vector<PhantomNodeCandidates> &candidates_list,
|
||||
const std::vector<std::size_t> &source_indices,
|
||||
const std::vector<std::size_t> &target_indices,
|
||||
const bool calculate_distance) const = 0;
|
||||
@ -73,18 +75,18 @@ template <typename Algorithm> class RoutingAlgorithms final : public RoutingAlgo
|
||||
virtual ~RoutingAlgorithms() = default;
|
||||
|
||||
InternalManyRoutesResult
|
||||
AlternativePathSearch(const PhantomNodes &phantom_node_pair,
|
||||
AlternativePathSearch(const PhantomEndpointCandidates &endpoint_candidates,
|
||||
unsigned number_of_alternatives) const final override;
|
||||
|
||||
InternalRouteResult ShortestPathSearch(
|
||||
const std::vector<PhantomNodes> &phantom_node_pair,
|
||||
const std::vector<PhantomNodeCandidates> &waypoint_candidates,
|
||||
const boost::optional<bool> continue_straight_at_waypoint) const final override;
|
||||
|
||||
InternalRouteResult
|
||||
DirectShortestPathSearch(const PhantomNodes &phantom_nodes) const final override;
|
||||
InternalRouteResult DirectShortestPathSearch(
|
||||
const PhantomEndpointCandidates &endpoint_candidates) const final override;
|
||||
|
||||
virtual std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
|
||||
ManyToManySearch(const std::vector<PhantomNode> &phantom_nodes,
|
||||
std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
|
||||
ManyToManySearch(const std::vector<PhantomNodeCandidates> &candidates_list,
|
||||
const std::vector<std::size_t> &source_indices,
|
||||
const std::vector<std::size_t> &target_indices,
|
||||
const bool calculate_distance) const final override;
|
||||
@ -150,28 +152,27 @@ template <typename Algorithm> class RoutingAlgorithms final : public RoutingAlgo
|
||||
};
|
||||
|
||||
template <typename Algorithm>
|
||||
InternalManyRoutesResult
|
||||
RoutingAlgorithms<Algorithm>::AlternativePathSearch(const PhantomNodes &phantom_node_pair,
|
||||
unsigned number_of_alternatives) const
|
||||
InternalManyRoutesResult RoutingAlgorithms<Algorithm>::AlternativePathSearch(
|
||||
const PhantomEndpointCandidates &endpoint_candidates, unsigned number_of_alternatives) const
|
||||
{
|
||||
return routing_algorithms::alternativePathSearch(
|
||||
heaps, *facade, phantom_node_pair, number_of_alternatives);
|
||||
heaps, *facade, endpoint_candidates, number_of_alternatives);
|
||||
}
|
||||
|
||||
template <typename Algorithm>
|
||||
InternalRouteResult RoutingAlgorithms<Algorithm>::ShortestPathSearch(
|
||||
const std::vector<PhantomNodes> &phantom_node_pair,
|
||||
const std::vector<PhantomNodeCandidates> &waypoint_candidates,
|
||||
const boost::optional<bool> continue_straight_at_waypoint) const
|
||||
{
|
||||
return routing_algorithms::shortestPathSearch(
|
||||
heaps, *facade, phantom_node_pair, continue_straight_at_waypoint);
|
||||
heaps, *facade, waypoint_candidates, continue_straight_at_waypoint);
|
||||
}
|
||||
|
||||
template <typename Algorithm>
|
||||
InternalRouteResult
|
||||
RoutingAlgorithms<Algorithm>::DirectShortestPathSearch(const PhantomNodes &phantom_nodes) const
|
||||
InternalRouteResult RoutingAlgorithms<Algorithm>::DirectShortestPathSearch(
|
||||
const PhantomEndpointCandidates &endpoint_candidates) const
|
||||
{
|
||||
return routing_algorithms::directShortestPathSearch(heaps, *facade, phantom_nodes);
|
||||
return routing_algorithms::directShortestPathSearch(heaps, *facade, endpoint_candidates);
|
||||
}
|
||||
|
||||
template <typename Algorithm>
|
||||
@ -193,30 +194,31 @@ inline routing_algorithms::SubMatchingList RoutingAlgorithms<Algorithm>::MapMatc
|
||||
|
||||
template <typename Algorithm>
|
||||
std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
|
||||
RoutingAlgorithms<Algorithm>::ManyToManySearch(const std::vector<PhantomNode> &phantom_nodes,
|
||||
RoutingAlgorithms<Algorithm>::ManyToManySearch(
|
||||
const std::vector<PhantomNodeCandidates> &candidates_list,
|
||||
const std::vector<std::size_t> &_source_indices,
|
||||
const std::vector<std::size_t> &_target_indices,
|
||||
const bool calculate_distance) const
|
||||
{
|
||||
BOOST_ASSERT(!phantom_nodes.empty());
|
||||
BOOST_ASSERT(!candidates_list.empty());
|
||||
|
||||
auto source_indices = _source_indices;
|
||||
auto target_indices = _target_indices;
|
||||
|
||||
if (source_indices.empty())
|
||||
{
|
||||
source_indices.resize(phantom_nodes.size());
|
||||
source_indices.resize(candidates_list.size());
|
||||
std::iota(source_indices.begin(), source_indices.end(), 0);
|
||||
}
|
||||
if (target_indices.empty())
|
||||
{
|
||||
target_indices.resize(phantom_nodes.size());
|
||||
target_indices.resize(candidates_list.size());
|
||||
std::iota(target_indices.begin(), target_indices.end(), 0);
|
||||
}
|
||||
|
||||
return routing_algorithms::manyToManySearch(heaps,
|
||||
*facade,
|
||||
phantom_nodes,
|
||||
candidates_list,
|
||||
std::move(source_indices),
|
||||
std::move(target_indices),
|
||||
calculate_distance);
|
||||
|
||||
@ -18,12 +18,12 @@ namespace routing_algorithms
|
||||
|
||||
InternalManyRoutesResult alternativePathSearch(SearchEngineData<ch::Algorithm> &search_engine_data,
|
||||
const DataFacade<ch::Algorithm> &facade,
|
||||
const PhantomNodes &phantom_node_pair,
|
||||
const PhantomEndpointCandidates &endpoint_candidates,
|
||||
unsigned number_of_alternatives);
|
||||
|
||||
InternalManyRoutesResult alternativePathSearch(SearchEngineData<mld::Algorithm> &search_engine_data,
|
||||
const DataFacade<mld::Algorithm> &facade,
|
||||
const PhantomNodes &phantom_node_pair,
|
||||
const PhantomEndpointCandidates &endpoint_candidates,
|
||||
unsigned number_of_alternatives);
|
||||
|
||||
} // namespace routing_algorithms
|
||||
|
||||
@ -24,7 +24,7 @@ namespace routing_algorithms
|
||||
template <typename Algorithm>
|
||||
InternalRouteResult directShortestPathSearch(SearchEngineData<Algorithm> &engine_working_data,
|
||||
const DataFacade<Algorithm> &facade,
|
||||
const PhantomNodes &phantom_nodes);
|
||||
const PhantomEndpointCandidates &endpoint_candidates);
|
||||
|
||||
} // namespace routing_algorithms
|
||||
} // namespace engine
|
||||
|
||||
@ -94,7 +94,7 @@ template <typename Algorithm>
|
||||
std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
|
||||
manyToManySearch(SearchEngineData<Algorithm> &engine_working_data,
|
||||
const DataFacade<Algorithm> &facade,
|
||||
const std::vector<PhantomNode> &phantom_nodes,
|
||||
const std::vector<PhantomNodeCandidates> &candidates_list,
|
||||
const std::vector<std::size_t> &source_indices,
|
||||
const std::vector<std::size_t> &target_indices,
|
||||
const bool calculate_distance);
|
||||
|
||||
@ -34,20 +34,12 @@ namespace engine
|
||||
|
||||
namespace routing_algorithms
|
||||
{
|
||||
static constexpr bool FORWARD_DIRECTION = true;
|
||||
static constexpr bool REVERSE_DIRECTION = false;
|
||||
static constexpr bool DO_NOT_FORCE_LOOPS = false;
|
||||
|
||||
bool needsLoopForward(const PhantomNode &source_phantom, const PhantomNode &target_phantom);
|
||||
bool needsLoopBackwards(const PhantomNode &source_phantom, const PhantomNode &target_phantom);
|
||||
|
||||
bool needsLoopForward(const PhantomNodes &phantoms);
|
||||
bool needsLoopBackwards(const PhantomNodes &phantoms);
|
||||
|
||||
template <typename Heap>
|
||||
void insertNodesInHeaps(Heap &forward_heap, Heap &reverse_heap, const PhantomNodes &nodes)
|
||||
namespace details
|
||||
{
|
||||
template <typename Heap>
|
||||
void insertSourceInForwardHeap(Heap &forward_heap, const PhantomNode &source)
|
||||
{
|
||||
const auto &source = nodes.source_phantom;
|
||||
if (source.IsValidForwardSource())
|
||||
{
|
||||
forward_heap.Insert(source.forward_segment_id.id,
|
||||
@ -61,8 +53,11 @@ void insertNodesInHeaps(Heap &forward_heap, Heap &reverse_heap, const PhantomNod
|
||||
-source.GetReverseWeightPlusOffset(),
|
||||
source.reverse_segment_id.id);
|
||||
}
|
||||
}
|
||||
|
||||
const auto &target = nodes.target_phantom;
|
||||
template <typename Heap>
|
||||
void insertTargetInReverseHeap(Heap &reverse_heap, const PhantomNode &target)
|
||||
{
|
||||
if (target.IsValidForwardTarget())
|
||||
{
|
||||
reverse_heap.Insert(target.forward_segment_id.id,
|
||||
@ -77,9 +72,57 @@ void insertNodesInHeaps(Heap &forward_heap, Heap &reverse_heap, const PhantomNod
|
||||
target.reverse_segment_id.id);
|
||||
}
|
||||
}
|
||||
} // namespace details
|
||||
static constexpr bool FORWARD_DIRECTION = true;
|
||||
static constexpr bool REVERSE_DIRECTION = false;
|
||||
|
||||
// Identify nodes in the forward(reverse) search direction that will require loop forcing
|
||||
// e.g. if source and destination nodes are on the same segment.
|
||||
std::vector<NodeID> getForwardLoopNodes(const PhantomEndpointCandidates &candidates);
|
||||
std::vector<NodeID> getForwardLoopNodes(const PhantomCandidatesToTarget &candidates);
|
||||
std::vector<NodeID> getBackwardLoopNodes(const PhantomEndpointCandidates &candidates);
|
||||
std::vector<NodeID> getBackwardLoopNodes(const PhantomCandidatesToTarget &candidates);
|
||||
|
||||
// Find the specific phantom node endpoints for a given path from a list of candidates.
|
||||
PhantomEndpoints endpointsFromCandidates(const PhantomEndpointCandidates &candidates,
|
||||
const std::vector<NodeID> &path);
|
||||
|
||||
template <typename HeapNodeT>
|
||||
inline bool force_loop(const std::vector<NodeID> &force_nodes, const HeapNodeT &heap_node)
|
||||
{
|
||||
// if loops are forced, they are so at the source
|
||||
return !force_nodes.empty() &&
|
||||
std::find(force_nodes.begin(), force_nodes.end(), heap_node.node) != force_nodes.end() &&
|
||||
heap_node.data.parent == heap_node.node;
|
||||
}
|
||||
|
||||
template <typename Heap>
|
||||
void insertNodesInHeaps(Heap &forward_heap, Heap &reverse_heap, const PhantomEndpoints &endpoints)
|
||||
{
|
||||
details::insertSourceInForwardHeap(forward_heap, endpoints.source_phantom);
|
||||
details::insertTargetInReverseHeap(reverse_heap, endpoints.target_phantom);
|
||||
}
|
||||
|
||||
template <typename Heap>
|
||||
void insertNodesInHeaps(Heap &forward_heap,
|
||||
Heap &reverse_heap,
|
||||
const PhantomEndpointCandidates &endpoint_candidates)
|
||||
{
|
||||
for (const auto &source : endpoint_candidates.source_phantoms)
|
||||
{
|
||||
details::insertSourceInForwardHeap(forward_heap, source);
|
||||
}
|
||||
|
||||
for (const auto &target : endpoint_candidates.target_phantoms)
|
||||
{
|
||||
details::insertTargetInReverseHeap(reverse_heap, target);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename ManyToManyQueryHeap>
|
||||
void insertSourceInHeap(ManyToManyQueryHeap &heap, const PhantomNode &phantom_node)
|
||||
void insertSourceInHeap(ManyToManyQueryHeap &heap, const PhantomNodeCandidates &source_candidates)
|
||||
{
|
||||
for (const auto &phantom_node : source_candidates)
|
||||
{
|
||||
if (phantom_node.IsValidForwardSource())
|
||||
{
|
||||
@ -98,9 +141,12 @@ void insertSourceInHeap(ManyToManyQueryHeap &heap, const PhantomNode &phantom_no
|
||||
-phantom_node.GetReverseDistance()});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename ManyToManyQueryHeap>
|
||||
void insertTargetInHeap(ManyToManyQueryHeap &heap, const PhantomNode &phantom_node)
|
||||
void insertTargetInHeap(ManyToManyQueryHeap &heap, const PhantomNodeCandidates &target_candidates)
|
||||
{
|
||||
for (const auto &phantom_node : target_candidates)
|
||||
{
|
||||
if (phantom_node.IsValidForwardTarget())
|
||||
{
|
||||
@ -119,10 +165,11 @@ void insertTargetInHeap(ManyToManyQueryHeap &heap, const PhantomNode &phantom_no
|
||||
phantom_node.GetReverseDistance()});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename FacadeT>
|
||||
void annotatePath(const FacadeT &facade,
|
||||
const PhantomNodes &phantom_node_pair,
|
||||
const PhantomEndpoints &endpoints,
|
||||
const std::vector<NodeID> &unpacked_nodes,
|
||||
const std::vector<EdgeID> &unpacked_edges,
|
||||
std::vector<PathData> &unpacked_path)
|
||||
@ -133,14 +180,14 @@ void annotatePath(const FacadeT &facade,
|
||||
const auto source_node_id = unpacked_nodes.front();
|
||||
const auto target_node_id = unpacked_nodes.back();
|
||||
const bool start_traversed_in_reverse =
|
||||
phantom_node_pair.source_phantom.forward_segment_id.id != source_node_id;
|
||||
endpoints.source_phantom.forward_segment_id.id != source_node_id;
|
||||
const bool target_traversed_in_reverse =
|
||||
phantom_node_pair.target_phantom.forward_segment_id.id != target_node_id;
|
||||
endpoints.target_phantom.forward_segment_id.id != target_node_id;
|
||||
|
||||
BOOST_ASSERT(phantom_node_pair.source_phantom.forward_segment_id.id == source_node_id ||
|
||||
phantom_node_pair.source_phantom.reverse_segment_id.id == source_node_id);
|
||||
BOOST_ASSERT(phantom_node_pair.target_phantom.forward_segment_id.id == target_node_id ||
|
||||
phantom_node_pair.target_phantom.reverse_segment_id.id == target_node_id);
|
||||
BOOST_ASSERT(endpoints.source_phantom.forward_segment_id.id == source_node_id ||
|
||||
endpoints.source_phantom.reverse_segment_id.id == source_node_id);
|
||||
BOOST_ASSERT(endpoints.target_phantom.forward_segment_id.id == target_node_id ||
|
||||
endpoints.target_phantom.reverse_segment_id.id == target_node_id);
|
||||
|
||||
// datastructures to hold extracted data from geometry
|
||||
std::vector<NodeID> id_vector;
|
||||
@ -180,8 +227,8 @@ void annotatePath(const FacadeT &facade,
|
||||
const auto geometry_index = facade.GetGeometryIndex(node_id);
|
||||
get_segment_geometry(geometry_index);
|
||||
|
||||
BOOST_ASSERT(id_vector.size() > 0);
|
||||
BOOST_ASSERT(datasource_vector.size() > 0);
|
||||
BOOST_ASSERT(!id_vector.empty());
|
||||
BOOST_ASSERT(!datasource_vector.empty());
|
||||
BOOST_ASSERT(weight_vector.size() + 1 == id_vector.size());
|
||||
BOOST_ASSERT(duration_vector.size() + 1 == id_vector.size());
|
||||
|
||||
@ -190,11 +237,11 @@ void annotatePath(const FacadeT &facade,
|
||||
std::size_t start_index = 0;
|
||||
if (is_first_segment)
|
||||
{
|
||||
unsigned short segment_position = phantom_node_pair.source_phantom.fwd_segment_position;
|
||||
unsigned short segment_position = endpoints.source_phantom.fwd_segment_position;
|
||||
if (start_traversed_in_reverse)
|
||||
{
|
||||
segment_position = weight_vector.size() -
|
||||
phantom_node_pair.source_phantom.fwd_segment_position - 1;
|
||||
segment_position =
|
||||
weight_vector.size() - endpoints.source_phantom.fwd_segment_position - 1;
|
||||
}
|
||||
BOOST_ASSERT(segment_position >= 0);
|
||||
start_index = static_cast<std::size_t>(segment_position);
|
||||
@ -214,7 +261,7 @@ void annotatePath(const FacadeT &facade,
|
||||
datasource_vector[segment_idx],
|
||||
boost::none});
|
||||
}
|
||||
BOOST_ASSERT(unpacked_path.size() > 0);
|
||||
BOOST_ASSERT(!unpacked_path.empty());
|
||||
|
||||
const auto turn_duration = facade.GetDurationPenaltyForEdgeID(turn_id);
|
||||
const auto turn_weight = facade.GetWeightPenaltyForEdgeID(turn_id);
|
||||
@ -237,19 +284,17 @@ void annotatePath(const FacadeT &facade,
|
||||
{
|
||||
if (is_local_path)
|
||||
{
|
||||
start_index =
|
||||
weight_vector.size() - phantom_node_pair.source_phantom.fwd_segment_position - 1;
|
||||
start_index = weight_vector.size() - endpoints.source_phantom.fwd_segment_position - 1;
|
||||
}
|
||||
end_index =
|
||||
weight_vector.size() - phantom_node_pair.target_phantom.fwd_segment_position - 1;
|
||||
end_index = weight_vector.size() - endpoints.target_phantom.fwd_segment_position - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (is_local_path)
|
||||
{
|
||||
start_index = phantom_node_pair.source_phantom.fwd_segment_position;
|
||||
start_index = endpoints.source_phantom.fwd_segment_position;
|
||||
}
|
||||
end_index = phantom_node_pair.target_phantom.fwd_segment_position;
|
||||
end_index = endpoints.target_phantom.fwd_segment_position;
|
||||
}
|
||||
|
||||
// Given the following compressed geometry:
|
||||
@ -277,11 +322,11 @@ void annotatePath(const FacadeT &facade,
|
||||
if (!unpacked_path.empty())
|
||||
{
|
||||
const auto source_weight = start_traversed_in_reverse
|
||||
? phantom_node_pair.source_phantom.reverse_weight
|
||||
: phantom_node_pair.source_phantom.forward_weight;
|
||||
? endpoints.source_phantom.reverse_weight
|
||||
: endpoints.source_phantom.forward_weight;
|
||||
const auto source_duration = start_traversed_in_reverse
|
||||
? phantom_node_pair.source_phantom.reverse_duration
|
||||
: phantom_node_pair.source_phantom.forward_duration;
|
||||
? endpoints.source_phantom.reverse_duration
|
||||
: endpoints.source_phantom.forward_duration;
|
||||
// The above code will create segments for (v, w), (w,x), (x, y) and (y, Z).
|
||||
// However the first segment duration needs to be adjusted to the fact that the source
|
||||
// phantom is in the middle of the segment. We do this by subtracting v--s from the
|
||||
@ -358,12 +403,11 @@ double getPathDistance(const DataFacade<Algorithm> &facade,
|
||||
template <typename AlgorithmT>
|
||||
InternalRouteResult extractRoute(const DataFacade<AlgorithmT> &facade,
|
||||
const EdgeWeight weight,
|
||||
const PhantomNodes &phantom_nodes,
|
||||
const PhantomEndpointCandidates &endpoint_candidates,
|
||||
const std::vector<NodeID> &unpacked_nodes,
|
||||
const std::vector<EdgeID> &unpacked_edges)
|
||||
{
|
||||
InternalRouteResult raw_route_data;
|
||||
raw_route_data.segment_end_coordinates = {phantom_nodes};
|
||||
|
||||
// No path found for both target nodes?
|
||||
if (INVALID_EDGE_WEIGHT == weight)
|
||||
@ -371,15 +415,18 @@ InternalRouteResult extractRoute(const DataFacade<AlgorithmT> &facade,
|
||||
return raw_route_data;
|
||||
}
|
||||
|
||||
auto phantom_endpoints = endpointsFromCandidates(endpoint_candidates, unpacked_nodes);
|
||||
raw_route_data.leg_endpoints = {phantom_endpoints};
|
||||
|
||||
raw_route_data.shortest_path_weight = weight;
|
||||
raw_route_data.unpacked_path_segments.resize(1);
|
||||
raw_route_data.source_traversed_in_reverse.push_back(
|
||||
(unpacked_nodes.front() != phantom_nodes.source_phantom.forward_segment_id.id));
|
||||
(unpacked_nodes.front() != phantom_endpoints.source_phantom.forward_segment_id.id));
|
||||
raw_route_data.target_traversed_in_reverse.push_back(
|
||||
(unpacked_nodes.back() != phantom_nodes.target_phantom.forward_segment_id.id));
|
||||
(unpacked_nodes.back() != phantom_endpoints.target_phantom.forward_segment_id.id));
|
||||
|
||||
annotatePath(facade,
|
||||
phantom_nodes,
|
||||
phantom_endpoints,
|
||||
unpacked_nodes,
|
||||
unpacked_edges,
|
||||
raw_route_data.unpacked_path_segments.front());
|
||||
|
||||
@ -120,8 +120,8 @@ void routingStep(const DataFacade<Algorithm> &facade,
|
||||
NodeID &middle_node_id,
|
||||
EdgeWeight &upper_bound,
|
||||
EdgeWeight min_edge_offset,
|
||||
const bool force_loop_forward,
|
||||
const bool force_loop_reverse)
|
||||
const std::vector<NodeID> &force_loop_forward_nodes,
|
||||
const std::vector<NodeID> &force_loop_reverse_nodes)
|
||||
{
|
||||
auto heapNode = forward_heap.DeleteMinGetHeapNode();
|
||||
const auto reverseHeapNode = reverse_heap.GetHeapNodeIfWasInserted(heapNode.node);
|
||||
@ -131,9 +131,8 @@ void routingStep(const DataFacade<Algorithm> &facade,
|
||||
const EdgeWeight new_weight = reverseHeapNode->weight + heapNode.weight;
|
||||
if (new_weight < upper_bound)
|
||||
{
|
||||
// if loops are forced, they are so at the source
|
||||
if ((force_loop_forward && heapNode.data.parent == heapNode.node) ||
|
||||
(force_loop_reverse && reverseHeapNode->data.parent == heapNode.node) ||
|
||||
if (force_loop(force_loop_forward_nodes, heapNode) ||
|
||||
force_loop(force_loop_reverse_nodes, heapNode) ||
|
||||
// in this case we are looking at a bi-directional way where the source
|
||||
// and target phantom are on the same edge based node
|
||||
new_weight < 0)
|
||||
@ -398,7 +397,7 @@ template <typename RandomIter, typename FacadeT>
|
||||
void unpackPath(const FacadeT &facade,
|
||||
RandomIter packed_path_begin,
|
||||
RandomIter packed_path_end,
|
||||
const PhantomNodes &phantom_nodes,
|
||||
const PhantomEndpoints &route_endpoints,
|
||||
std::vector<PathData> &unpacked_path)
|
||||
{
|
||||
const auto nodes_number = std::distance(packed_path_begin, packed_path_end);
|
||||
@ -422,7 +421,7 @@ void unpackPath(const FacadeT &facade,
|
||||
});
|
||||
}
|
||||
|
||||
annotatePath(facade, phantom_nodes, unpacked_nodes, unpacked_edges, unpacked_path);
|
||||
annotatePath(facade, route_endpoints, unpacked_nodes, unpacked_edges, unpacked_path);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -467,12 +466,35 @@ void search(SearchEngineData<Algorithm> &engine_working_data,
|
||||
const DataFacade<Algorithm> &facade,
|
||||
SearchEngineData<Algorithm>::QueryHeap &forward_heap,
|
||||
SearchEngineData<Algorithm>::QueryHeap &reverse_heap,
|
||||
std::int32_t &weight,
|
||||
EdgeWeight &weight,
|
||||
std::vector<NodeID> &packed_leg,
|
||||
const bool force_loop_forward,
|
||||
const bool force_loop_reverse,
|
||||
const PhantomNodes &phantom_nodes,
|
||||
const int duration_upper_bound = INVALID_EDGE_WEIGHT);
|
||||
const std::vector<NodeID> &force_loop_forward_node,
|
||||
const std::vector<NodeID> &force_loop_reverse_node,
|
||||
const EdgeWeight duration_upper_bound = INVALID_EDGE_WEIGHT);
|
||||
|
||||
template <typename PhantomEndpointT>
|
||||
void search(SearchEngineData<Algorithm> &engine_working_data,
|
||||
const DataFacade<Algorithm> &facade,
|
||||
SearchEngineData<Algorithm>::QueryHeap &forward_heap,
|
||||
SearchEngineData<Algorithm>::QueryHeap &reverse_heap,
|
||||
EdgeWeight &weight,
|
||||
std::vector<NodeID> &packed_leg,
|
||||
const std::vector<NodeID> &force_loop_forward_node,
|
||||
const std::vector<NodeID> &force_loop_reverse_node,
|
||||
const PhantomEndpointT & /*endpoints*/,
|
||||
const EdgeWeight duration_upper_bound = INVALID_EDGE_WEIGHT)
|
||||
{
|
||||
// Avoid templating the CH search implementations.
|
||||
return search(engine_working_data,
|
||||
facade,
|
||||
forward_heap,
|
||||
reverse_heap,
|
||||
weight,
|
||||
packed_leg,
|
||||
force_loop_forward_node,
|
||||
force_loop_reverse_node,
|
||||
duration_upper_bound);
|
||||
}
|
||||
|
||||
// Requires the heaps for be empty
|
||||
// If heaps should be adjusted to be initialized outside of this function,
|
||||
|
||||
@ -33,24 +33,75 @@ namespace
|
||||
template <typename MultiLevelPartition>
|
||||
inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition,
|
||||
NodeID node,
|
||||
const PhantomNodes &phantom_nodes)
|
||||
const PhantomNode &source,
|
||||
const PhantomNode &target)
|
||||
{
|
||||
auto level = [&partition, node](const SegmentID &source, const SegmentID &target) {
|
||||
if (source.enabled && target.enabled)
|
||||
return partition.GetQueryLevel(source.id, target.id, node);
|
||||
return INVALID_LEVEL_ID;
|
||||
};
|
||||
return std::min(std::min(level(phantom_nodes.source_phantom.forward_segment_id,
|
||||
phantom_nodes.target_phantom.forward_segment_id),
|
||||
level(phantom_nodes.source_phantom.forward_segment_id,
|
||||
phantom_nodes.target_phantom.reverse_segment_id)),
|
||||
std::min(level(phantom_nodes.source_phantom.reverse_segment_id,
|
||||
phantom_nodes.target_phantom.forward_segment_id),
|
||||
level(phantom_nodes.source_phantom.reverse_segment_id,
|
||||
phantom_nodes.target_phantom.reverse_segment_id)));
|
||||
|
||||
return std::min(std::min(level(source.forward_segment_id, target.forward_segment_id),
|
||||
level(source.forward_segment_id, target.reverse_segment_id)),
|
||||
std::min(level(source.reverse_segment_id, target.forward_segment_id),
|
||||
level(source.reverse_segment_id, target.reverse_segment_id)));
|
||||
}
|
||||
|
||||
inline bool checkParentCellRestriction(CellID, const PhantomNodes &) { return true; }
|
||||
template <typename MultiLevelPartition>
|
||||
inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition,
|
||||
NodeID node,
|
||||
const PhantomEndpoints &endpoints)
|
||||
{
|
||||
return getNodeQueryLevel(partition, node, endpoints.source_phantom, endpoints.target_phantom);
|
||||
}
|
||||
|
||||
template <typename MultiLevelPartition>
|
||||
inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition,
|
||||
NodeID node,
|
||||
const PhantomCandidatesToTarget &endpoint_candidates)
|
||||
{
|
||||
auto min_level = std::accumulate(
|
||||
endpoint_candidates.source_phantoms.begin(),
|
||||
endpoint_candidates.source_phantoms.end(),
|
||||
INVALID_LEVEL_ID,
|
||||
[&](LevelID current_level, const PhantomNode &source) {
|
||||
return std::min(
|
||||
current_level,
|
||||
getNodeQueryLevel(partition, node, source, endpoint_candidates.target_phantom));
|
||||
});
|
||||
return min_level;
|
||||
}
|
||||
|
||||
template <typename MultiLevelPartition>
|
||||
inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition,
|
||||
NodeID node,
|
||||
const PhantomEndpointCandidates &endpoint_candidates)
|
||||
{
|
||||
auto min_level = std::accumulate(
|
||||
endpoint_candidates.source_phantoms.begin(),
|
||||
endpoint_candidates.source_phantoms.end(),
|
||||
INVALID_LEVEL_ID,
|
||||
[&](LevelID level_1, const PhantomNode &source) {
|
||||
return std::min(
|
||||
level_1,
|
||||
std::accumulate(endpoint_candidates.target_phantoms.begin(),
|
||||
endpoint_candidates.target_phantoms.end(),
|
||||
level_1,
|
||||
[&](LevelID level_2, const PhantomNode &target) {
|
||||
return std::min(
|
||||
level_2,
|
||||
getNodeQueryLevel(partition, node, source, target));
|
||||
}));
|
||||
});
|
||||
return min_level;
|
||||
}
|
||||
|
||||
template <typename PhantomCandidateT>
|
||||
inline bool checkParentCellRestriction(CellID, const PhantomCandidateT &)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Restricted search (Args is LevelID, CellID):
|
||||
// * use the fixed level for queries
|
||||
@ -72,17 +123,23 @@ inline bool checkParentCellRestriction(CellID cell, LevelID, CellID parent)
|
||||
template <typename MultiLevelPartition>
|
||||
inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition,
|
||||
const NodeID node,
|
||||
const PhantomNode &phantom_node)
|
||||
const PhantomNodeCandidates &candidates)
|
||||
{
|
||||
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;
|
||||
auto highest_different_level = [&partition, node](const SegmentID &segment) {
|
||||
return segment.enabled ? partition.GetHighestDifferentLevel(segment.id, node)
|
||||
: 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));
|
||||
|
||||
auto node_level =
|
||||
std::accumulate(candidates.begin(),
|
||||
candidates.end(),
|
||||
INVALID_LEVEL_ID,
|
||||
[&](LevelID current_level, const PhantomNode &phantom_node) {
|
||||
auto highest_level =
|
||||
std::min(highest_different_level(phantom_node.forward_segment_id),
|
||||
highest_different_level(phantom_node.reverse_segment_id));
|
||||
return std::min(current_level, highest_level);
|
||||
});
|
||||
return node_level;
|
||||
}
|
||||
|
||||
@ -92,31 +149,17 @@ inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition,
|
||||
template <typename MultiLevelPartition>
|
||||
inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition,
|
||||
NodeID node,
|
||||
const std::vector<PhantomNode> &phantom_nodes,
|
||||
const std::vector<PhantomNodeCandidates> &candidates_list,
|
||||
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]));
|
||||
}
|
||||
auto init = getNodeQueryLevel(partition, node, candidates_list[phantom_index]);
|
||||
auto result = std::accumulate(
|
||||
phantom_indices.begin(), phantom_indices.end(), init, [&](LevelID level, size_t index) {
|
||||
return std::min(level, getNodeQueryLevel(partition, node, candidates_list[index]));
|
||||
});
|
||||
return result;
|
||||
}
|
||||
} // namespace
|
||||
@ -229,7 +272,7 @@ template <bool DIRECTION, typename Algorithm, typename... Args>
|
||||
void relaxOutgoingEdges(const DataFacade<Algorithm> &facade,
|
||||
typename SearchEngineData<Algorithm>::QueryHeap &forward_heap,
|
||||
const typename SearchEngineData<Algorithm>::QueryHeap::HeapNode &heapNode,
|
||||
Args... args)
|
||||
const Args &... args)
|
||||
{
|
||||
const auto &partition = facade.GetMultiLevelPartition();
|
||||
const auto &cells = facade.GetCellStorage();
|
||||
@ -344,9 +387,9 @@ void routingStep(const DataFacade<Algorithm> &facade,
|
||||
typename SearchEngineData<Algorithm>::QueryHeap &reverse_heap,
|
||||
NodeID &middle_node,
|
||||
EdgeWeight &path_upper_bound,
|
||||
const bool force_loop_forward,
|
||||
const bool force_loop_reverse,
|
||||
Args... args)
|
||||
const std::vector<NodeID> &force_loop_forward_nodes,
|
||||
const std::vector<NodeID> &force_loop_reverse_nodes,
|
||||
const Args &... args)
|
||||
{
|
||||
const auto heapNode = forward_heap.DeleteMinGetHeapNode();
|
||||
const auto weight = heapNode.weight;
|
||||
@ -366,9 +409,9 @@ void routingStep(const DataFacade<Algorithm> &facade,
|
||||
|
||||
// MLD uses loops forcing only to prune single node paths in forward and/or
|
||||
// backward direction (there is no need to force loops in MLD but in CH)
|
||||
if (!(force_loop_forward && heapNode.data.parent == heapNode.node) &&
|
||||
!(force_loop_reverse && reverseHeapNode->data.parent == heapNode.node) &&
|
||||
(path_weight >= 0) && (path_weight < path_upper_bound))
|
||||
if (!force_loop(force_loop_forward_nodes, heapNode) &&
|
||||
!force_loop(force_loop_reverse_nodes, heapNode) && (path_weight >= 0) &&
|
||||
(path_weight < path_upper_bound))
|
||||
{
|
||||
middle_node = heapNode.node;
|
||||
path_upper_bound = path_weight;
|
||||
@ -393,10 +436,10 @@ UnpackedPath search(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,
|
||||
const std::vector<NodeID> &force_loop_forward_nodes,
|
||||
const std::vector<NodeID> &force_loop_reverse_nodes,
|
||||
EdgeWeight weight_upper_bound,
|
||||
Args... args)
|
||||
const Args &... args)
|
||||
{
|
||||
if (forward_heap.Empty() || reverse_heap.Empty())
|
||||
{
|
||||
@ -423,8 +466,8 @@ UnpackedPath search(SearchEngineData<Algorithm> &engine_working_data,
|
||||
reverse_heap,
|
||||
middle,
|
||||
weight,
|
||||
force_loop_forward,
|
||||
force_loop_reverse,
|
||||
force_loop_forward_nodes,
|
||||
force_loop_reverse_nodes,
|
||||
args...);
|
||||
if (!forward_heap.Empty())
|
||||
forward_heap_min = forward_heap.MinKey();
|
||||
@ -436,8 +479,8 @@ UnpackedPath search(SearchEngineData<Algorithm> &engine_working_data,
|
||||
forward_heap,
|
||||
middle,
|
||||
weight,
|
||||
force_loop_reverse,
|
||||
force_loop_forward,
|
||||
force_loop_reverse_nodes,
|
||||
force_loop_forward_nodes,
|
||||
args...);
|
||||
if (!reverse_heap.Empty())
|
||||
reverse_heap_min = reverse_heap.MinKey();
|
||||
@ -494,12 +537,13 @@ UnpackedPath search(SearchEngineData<Algorithm> &engine_working_data,
|
||||
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,
|
||||
std::tie(subpath_weight, subpath_nodes, subpath_edges) =
|
||||
search(engine_working_data,
|
||||
facade,
|
||||
forward_heap,
|
||||
reverse_heap,
|
||||
force_loop_forward,
|
||||
force_loop_reverse,
|
||||
force_loop_forward_nodes,
|
||||
force_loop_reverse_nodes,
|
||||
INVALID_EDGE_WEIGHT,
|
||||
sublevel,
|
||||
parent_cell_id);
|
||||
@ -517,16 +561,16 @@ UnpackedPath search(SearchEngineData<Algorithm> &engine_working_data,
|
||||
}
|
||||
|
||||
// Alias to be compatible with the CH-based search
|
||||
template <typename Algorithm>
|
||||
template <typename Algorithm, typename PhantomEndpointT>
|
||||
inline void search(SearchEngineData<Algorithm> &engine_working_data,
|
||||
const DataFacade<Algorithm> &facade,
|
||||
typename SearchEngineData<Algorithm>::QueryHeap &forward_heap,
|
||||
typename SearchEngineData<Algorithm>::QueryHeap &reverse_heap,
|
||||
EdgeWeight &weight,
|
||||
std::vector<NodeID> &unpacked_nodes,
|
||||
const bool force_loop_forward,
|
||||
const bool force_loop_reverse,
|
||||
const PhantomNodes &phantom_nodes,
|
||||
const std::vector<NodeID> &force_loop_forward_node,
|
||||
const std::vector<NodeID> &force_loop_reverse_node,
|
||||
const PhantomEndpointT &endpoints,
|
||||
const EdgeWeight weight_upper_bound = INVALID_EDGE_WEIGHT)
|
||||
{
|
||||
// TODO: change search calling interface to use unpacked_edges result
|
||||
@ -534,10 +578,10 @@ inline void search(SearchEngineData<Algorithm> &engine_working_data,
|
||||
facade,
|
||||
forward_heap,
|
||||
reverse_heap,
|
||||
force_loop_forward,
|
||||
force_loop_reverse,
|
||||
force_loop_forward_node,
|
||||
force_loop_reverse_node,
|
||||
weight_upper_bound,
|
||||
phantom_nodes);
|
||||
endpoints);
|
||||
}
|
||||
|
||||
// TODO: refactor CH-related stub to use unpacked_edges
|
||||
@ -545,7 +589,7 @@ template <typename RandomIter, typename FacadeT>
|
||||
void unpackPath(const FacadeT &facade,
|
||||
RandomIter packed_path_begin,
|
||||
RandomIter packed_path_end,
|
||||
const PhantomNodes &phantom_nodes,
|
||||
const PhantomEndpoints &route_endpoints,
|
||||
std::vector<PathData> &unpacked_path)
|
||||
{
|
||||
const auto nodes_number = std::distance(packed_path_begin, packed_path_end);
|
||||
@ -568,7 +612,7 @@ void unpackPath(const FacadeT &facade,
|
||||
});
|
||||
}
|
||||
|
||||
annotatePath(facade, phantom_nodes, unpacked_nodes, unpacked_edges, unpacked_path);
|
||||
annotatePath(facade, route_endpoints, unpacked_nodes, unpacked_edges, unpacked_path);
|
||||
}
|
||||
|
||||
template <typename Algorithm>
|
||||
@ -583,8 +627,8 @@ double getNetworkDistance(SearchEngineData<Algorithm> &engine_working_data,
|
||||
forward_heap.Clear();
|
||||
reverse_heap.Clear();
|
||||
|
||||
const PhantomNodes phantom_nodes{source_phantom, target_phantom};
|
||||
insertNodesInHeaps(forward_heap, reverse_heap, phantom_nodes);
|
||||
const PhantomEndpoints endpoints{source_phantom, target_phantom};
|
||||
insertNodesInHeaps(forward_heap, reverse_heap, endpoints);
|
||||
|
||||
EdgeWeight weight = INVALID_EDGE_WEIGHT;
|
||||
std::vector<NodeID> unpacked_nodes;
|
||||
@ -593,10 +637,10 @@ double getNetworkDistance(SearchEngineData<Algorithm> &engine_working_data,
|
||||
facade,
|
||||
forward_heap,
|
||||
reverse_heap,
|
||||
DO_NOT_FORCE_LOOPS,
|
||||
DO_NOT_FORCE_LOOPS,
|
||||
{},
|
||||
{},
|
||||
weight_upper_bound,
|
||||
phantom_nodes);
|
||||
endpoints);
|
||||
|
||||
if (weight == INVALID_EDGE_WEIGHT)
|
||||
{
|
||||
@ -605,7 +649,7 @@ double getNetworkDistance(SearchEngineData<Algorithm> &engine_working_data,
|
||||
|
||||
std::vector<PathData> unpacked_path;
|
||||
|
||||
annotatePath(facade, phantom_nodes, unpacked_nodes, unpacked_edges, unpacked_path);
|
||||
annotatePath(facade, endpoints, unpacked_nodes, unpacked_edges, unpacked_path);
|
||||
|
||||
return getPathDistance(facade, unpacked_path, source_phantom, target_phantom);
|
||||
}
|
||||
|
||||
@ -14,9 +14,10 @@ namespace routing_algorithms
|
||||
{
|
||||
|
||||
template <typename Algorithm>
|
||||
InternalRouteResult shortestPathSearch(SearchEngineData<Algorithm> &engine_working_data,
|
||||
InternalRouteResult
|
||||
shortestPathSearch(SearchEngineData<Algorithm> &engine_working_data,
|
||||
const DataFacade<Algorithm> &facade,
|
||||
const std::vector<PhantomNodes> &phantom_nodes_vector,
|
||||
const std::vector<PhantomNodeCandidates> &waypoint_candidates,
|
||||
const boost::optional<bool> continue_straight_at_waypoint);
|
||||
|
||||
} // namespace routing_algorithms
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -23,6 +23,7 @@
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include "storage/io.hpp"
|
||||
#include "traffic_signals.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
@ -68,7 +69,7 @@ class EdgeBasedGraphFactory
|
||||
EdgeBasedNodeDataContainer &node_data_container,
|
||||
const CompressedEdgeContainer &compressed_edge_container,
|
||||
const std::unordered_set<NodeID> &barrier_nodes,
|
||||
const std::unordered_set<NodeID> &traffic_lights,
|
||||
const TrafficSignals &traffic_signals,
|
||||
const std::vector<util::Coordinate> &coordinates,
|
||||
const NameTable &name_table,
|
||||
const std::unordered_set<EdgeID> &segregated_edges,
|
||||
@ -134,7 +135,7 @@ class EdgeBasedGraphFactory
|
||||
const util::NodeBasedDynamicGraph &m_node_based_graph;
|
||||
|
||||
const std::unordered_set<NodeID> &m_barrier_nodes;
|
||||
const std::unordered_set<NodeID> &m_traffic_lights;
|
||||
const TrafficSignals &m_traffic_signals;
|
||||
const CompressedEdgeContainer &m_compressed_edge_container;
|
||||
|
||||
const NameTable &name_table;
|
||||
|
||||
@ -8,8 +8,11 @@
|
||||
#include "extractor/scripting_environment.hpp"
|
||||
|
||||
#include "storage/tar_fwd.hpp"
|
||||
#include "traffic_lights.hpp"
|
||||
#include "traffic_signals.hpp"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
@ -25,15 +28,19 @@ namespace extractor
|
||||
class ExtractionContainers
|
||||
{
|
||||
using ReferencedWays = std::unordered_map<OSMWayID, NodesOfWay>;
|
||||
using ReferencedTrafficSignals =
|
||||
std::pair<std::unordered_set<OSMNodeID>, std::unordered_multimap<OSMNodeID, OSMNodeID>>;
|
||||
// The relationship between way and nodes is lost during node preparation.
|
||||
// We identify the ways and nodes relevant to restrictions/overrides prior to
|
||||
// We identify the ways and nodes relevant to restrictions/overrides/signals prior to
|
||||
// node processing so that they can be referenced in the preparation phase.
|
||||
ReferencedWays IdentifyRestrictionWays();
|
||||
ReferencedWays IdentifyManeuverOverrideWays();
|
||||
ReferencedTrafficSignals IdentifyTrafficSignals();
|
||||
|
||||
void PrepareNodes();
|
||||
void PrepareManeuverOverrides(const ReferencedWays &maneuver_override_ways);
|
||||
void PrepareRestrictions(const ReferencedWays &restriction_ways);
|
||||
void PrepareTrafficSignals(const ReferencedTrafficSignals &referenced_traffic_signals);
|
||||
void PrepareEdges(ScriptingEnvironment &scripting_environment);
|
||||
|
||||
void WriteNodes(storage::tar::FileWriter &file_out) const;
|
||||
@ -50,9 +57,9 @@ class ExtractionContainers
|
||||
using NameOffsets = std::vector<size_t>;
|
||||
using WayIDVector = std::vector<OSMWayID>;
|
||||
using WayNodeIDOffsets = std::vector<size_t>;
|
||||
using InputTrafficSignal = std::pair<OSMNodeID, TrafficLightClass::Direction>;
|
||||
|
||||
std::vector<OSMNodeID> barrier_nodes;
|
||||
std::vector<OSMNodeID> traffic_signals;
|
||||
NodeIDVector used_node_id_list;
|
||||
NodeVector all_nodes_list;
|
||||
EdgeVector all_edges_list;
|
||||
@ -65,6 +72,9 @@ class ExtractionContainers
|
||||
|
||||
unsigned max_internal_node_id;
|
||||
|
||||
std::vector<InputTrafficSignal> external_traffic_signals;
|
||||
TrafficSignals internal_traffic_signals;
|
||||
|
||||
// List of restrictions (conditional and unconditional) before we transform them into the
|
||||
// output types. Input containers reference OSMNodeIDs. We can only transform them to the
|
||||
// correct internal IDs after we've read everything. Without a multi-parse approach,
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
#ifndef EXTRACTION_NODE_HPP
|
||||
#define EXTRACTION_NODE_HPP
|
||||
|
||||
#include "traffic_lights.hpp"
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
@ -8,9 +10,13 @@ namespace extractor
|
||||
|
||||
struct ExtractionNode
|
||||
{
|
||||
ExtractionNode() : traffic_lights(false), barrier(false) {}
|
||||
void clear() { traffic_lights = barrier = false; }
|
||||
bool traffic_lights;
|
||||
ExtractionNode() : traffic_lights(TrafficLightClass::NONE), barrier(false) {}
|
||||
void clear()
|
||||
{
|
||||
traffic_lights = TrafficLightClass::NONE;
|
||||
barrier = false;
|
||||
}
|
||||
TrafficLightClass::Direction traffic_lights;
|
||||
bool barrier;
|
||||
};
|
||||
} // namespace extractor
|
||||
|
||||
@ -43,6 +43,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "util/guidance/turn_lanes.hpp"
|
||||
|
||||
#include "restriction_graph.hpp"
|
||||
#include "traffic_signals.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
namespace osrm
|
||||
@ -64,7 +65,8 @@ class Extractor
|
||||
|
||||
std::tuple<LaneDescriptionMap,
|
||||
std::vector<TurnRestriction>,
|
||||
std::vector<UnresolvedManeuverOverride>>
|
||||
std::vector<UnresolvedManeuverOverride>,
|
||||
TrafficSignals>
|
||||
ParseOSMData(ScriptingEnvironment &scripting_environment, const unsigned number_of_threads);
|
||||
|
||||
EdgeID BuildEdgeExpandedGraph(
|
||||
@ -73,7 +75,7 @@ class Extractor
|
||||
const std::vector<util::Coordinate> &coordinates,
|
||||
const CompressedEdgeContainer &compressed_edge_container,
|
||||
const std::unordered_set<NodeID> &barrier_nodes,
|
||||
const std::unordered_set<NodeID> &traffic_lights,
|
||||
const TrafficSignals &traffic_signals,
|
||||
const RestrictionGraph &restriction_graph,
|
||||
const std::unordered_set<EdgeID> &segregated_edges,
|
||||
const NameTable &name_table,
|
||||
|
||||
@ -444,10 +444,9 @@ inline void readConditionalRestrictions(const boost::filesystem::path &path,
|
||||
}
|
||||
|
||||
// reads .osrm file which is a temporary file of osrm-extract
|
||||
template <typename BarrierOutIter, typename TrafficSignalsOutIter, typename PackedOSMIDsT>
|
||||
template <typename BarrierOutIter, typename PackedOSMIDsT>
|
||||
void readRawNBGraph(const boost::filesystem::path &path,
|
||||
BarrierOutIter barriers,
|
||||
TrafficSignalsOutIter traffic_signals,
|
||||
std::vector<util::Coordinate> &coordinates,
|
||||
PackedOSMIDsT &osm_node_ids,
|
||||
std::vector<extractor::NodeBasedEdge> &edge_list,
|
||||
@ -471,8 +470,6 @@ void readRawNBGraph(const boost::filesystem::path &path,
|
||||
|
||||
reader.ReadStreaming<NodeID>("/extractor/barriers", barriers);
|
||||
|
||||
reader.ReadStreaming<NodeID>("/extractor/traffic_lights", traffic_signals);
|
||||
|
||||
storage::serialization::read(reader, "/extractor/edges", edge_list);
|
||||
storage::serialization::read(reader, "/extractor/annotations", annotations);
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
#include "extractor/scripting_environment.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include "traffic_signals.hpp"
|
||||
#include "util/node_based_graph.hpp"
|
||||
|
||||
#include <memory>
|
||||
@ -25,7 +26,7 @@ class GraphCompressor
|
||||
|
||||
public:
|
||||
void Compress(const std::unordered_set<NodeID> &barrier_nodes,
|
||||
const std::unordered_set<NodeID> &traffic_lights,
|
||||
const TrafficSignals &traffic_signals,
|
||||
ScriptingEnvironment &scripting_environment,
|
||||
std::vector<TurnRestriction> &turn_restrictions,
|
||||
std::vector<UnresolvedManeuverOverride> &maneuver_overrides,
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
#include "extractor/packed_osm_ids.hpp"
|
||||
#include "extractor/scripting_environment.hpp"
|
||||
|
||||
#include "traffic_signals.hpp"
|
||||
#include "util/coordinate.hpp"
|
||||
#include "util/node_based_graph.hpp"
|
||||
|
||||
@ -40,11 +41,11 @@ class NodeBasedGraphFactory
|
||||
NodeBasedGraphFactory(const boost::filesystem::path &input_file,
|
||||
ScriptingEnvironment &scripting_environment,
|
||||
std::vector<TurnRestriction> &turn_restrictions,
|
||||
std::vector<UnresolvedManeuverOverride> &maneuver_overrides);
|
||||
std::vector<UnresolvedManeuverOverride> &maneuver_overrides,
|
||||
const TrafficSignals &traffic_signals);
|
||||
|
||||
auto const &GetGraph() const { return compressed_output_graph; }
|
||||
auto const &GetBarriers() const { return barriers; }
|
||||
auto const &GetTrafficSignals() const { return traffic_signals; }
|
||||
auto const &GetCompressedEdges() const { return compressed_edge_container; }
|
||||
auto const &GetCoordinates() const { return coordinates; }
|
||||
auto const &GetAnnotationData() const { return annotation_data; }
|
||||
@ -68,7 +69,8 @@ class NodeBasedGraphFactory
|
||||
// edges into a single representative form
|
||||
void Compress(ScriptingEnvironment &scripting_environment,
|
||||
std::vector<TurnRestriction> &turn_restrictions,
|
||||
std::vector<UnresolvedManeuverOverride> &maneuver_overrides);
|
||||
std::vector<UnresolvedManeuverOverride> &maneuver_overrides,
|
||||
const TrafficSignals &traffic_signals);
|
||||
|
||||
// Most ways are bidirectional, making the geometry in forward and backward direction the same,
|
||||
// except for reversal. We make use of this fact by keeping only one representation of the
|
||||
@ -89,7 +91,6 @@ class NodeBasedGraphFactory
|
||||
|
||||
// General Information about the graph, not used outside of extractor
|
||||
std::unordered_set<NodeID> barriers;
|
||||
std::unordered_set<NodeID> traffic_signals;
|
||||
|
||||
std::vector<util::Coordinate> coordinates;
|
||||
|
||||
|
||||
@ -58,7 +58,7 @@ class RasterGrid
|
||||
for (unsigned int y = 0; y < ydim; y++)
|
||||
{
|
||||
// read one line from file.
|
||||
file_reader.ReadLine(&buffer[0], xdim * 11);
|
||||
file_reader.ReadLine(buffer.data(), xdim * 11);
|
||||
boost::algorithm::trim(buffer);
|
||||
|
||||
std::vector<std::string> result;
|
||||
|
||||
@ -5,6 +5,8 @@
|
||||
|
||||
#include <boost/optional/optional.hpp>
|
||||
|
||||
#include <osmium/tags/filter.hpp>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@ -43,7 +45,7 @@ class RestrictionParser
|
||||
public:
|
||||
RestrictionParser(bool use_turn_restrictions,
|
||||
bool parse_conditionals,
|
||||
std::vector<std::string> &restrictions);
|
||||
const std::vector<std::string> &restrictions);
|
||||
std::vector<InputTurnRestriction> TryParse(const osmium::Relation &relation) const;
|
||||
|
||||
private:
|
||||
@ -51,7 +53,8 @@ class RestrictionParser
|
||||
|
||||
bool use_turn_restrictions;
|
||||
bool parse_conditionals;
|
||||
std::vector<std::string> restrictions;
|
||||
std::set<std::string> restrictions;
|
||||
osmium::tags::KeyFilter filter;
|
||||
};
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
|
||||
25
include/extractor/traffic_lights.hpp
Normal file
25
include/extractor/traffic_lights.hpp
Normal file
@ -0,0 +1,25 @@
|
||||
#ifndef OSRM_EXTRACTOR_TRAFFIC_LIGHTS_DATA_HPP_
|
||||
#define OSRM_EXTRACTOR_TRAFFIC_LIGHTS_DATA_HPP_
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
|
||||
namespace TrafficLightClass
|
||||
{
|
||||
// The traffic light annotation is extracted from node tags.
|
||||
// The directions in which the traffic light applies are relative to the way containing the node.
|
||||
enum Direction
|
||||
{
|
||||
NONE = 0,
|
||||
DIRECTION_ALL = 1,
|
||||
DIRECTION_FORWARD = 2,
|
||||
DIRECTION_REVERSE = 3
|
||||
};
|
||||
} // namespace TrafficLightClass
|
||||
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
|
||||
#endif // OSRM_EXTRACTOR_TRAFFIC_LIGHTS_DATA_HPP_
|
||||
28
include/extractor/traffic_signals.hpp
Normal file
28
include/extractor/traffic_signals.hpp
Normal file
@ -0,0 +1,28 @@
|
||||
#ifndef OSRM_EXTRACTOR_TRAFFIC_SIGNALS_HPP
|
||||
#define OSRM_EXTRACTOR_TRAFFIC_SIGNALS_HPP
|
||||
|
||||
#include "util/typedefs.hpp"
|
||||
#include <unordered_set>
|
||||
|
||||
#include <boost/unordered_set.hpp>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
|
||||
struct TrafficSignals
|
||||
{
|
||||
std::unordered_set<NodeID> bidirectional_nodes;
|
||||
std::unordered_set<std::pair<NodeID, NodeID>, boost::hash<std::pair<NodeID, NodeID>>>
|
||||
unidirectional_segments;
|
||||
|
||||
inline bool HasSignal(NodeID from, NodeID to) const
|
||||
{
|
||||
return bidirectional_nodes.count(to) > 0 || unidirectional_segments.count({from, to}) > 0;
|
||||
}
|
||||
};
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
|
||||
#endif // OSRM_EXTRACTOR_TRAFFIC_SIGNALS_HPP
|
||||
@ -2,8 +2,7 @@
|
||||
#define OSRM_BINDINGS_NODE_SUPPORT_HPP
|
||||
|
||||
#include "nodejs/json_v8_renderer.hpp"
|
||||
#include "util/json_renderer.hpp"
|
||||
|
||||
#include "engine/api/flatbuffers/fbresult_generated.h"
|
||||
#include "osrm/approach.hpp"
|
||||
#include "osrm/bearing.hpp"
|
||||
#include "osrm/coordinate.hpp"
|
||||
@ -18,6 +17,7 @@
|
||||
#include "osrm/table_parameters.hpp"
|
||||
#include "osrm/tile_parameters.hpp"
|
||||
#include "osrm/trip_parameters.hpp"
|
||||
#include "util/json_renderer.hpp"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
@ -26,6 +26,7 @@
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@ -46,7 +47,7 @@ using table_parameters_ptr = std::unique_ptr<osrm::TableParameters>;
|
||||
|
||||
struct PluginParameters
|
||||
{
|
||||
bool renderJSONToBuffer = false;
|
||||
bool renderToBuffer = false;
|
||||
};
|
||||
|
||||
using ObjectOrString = typename mapbox::util::variant<osrm::json::Object, std::string>;
|
||||
@ -96,6 +97,17 @@ inline void ParseResult(const osrm::Status &result_status, osrm::json::Object &r
|
||||
}
|
||||
|
||||
inline void ParseResult(const osrm::Status & /*result_status*/, const std::string & /*unused*/) {}
|
||||
inline void ParseResult(const osrm::Status &result_status,
|
||||
const flatbuffers::FlatBufferBuilder &fbs_builder)
|
||||
{
|
||||
auto fbs_result = osrm::engine::api::fbresult::GetFBResult(fbs_builder.GetBufferPointer());
|
||||
|
||||
if (result_status == osrm::Status::Error)
|
||||
{
|
||||
BOOST_ASSERT(fbs_result->code());
|
||||
throw std::logic_error(fbs_result->code()->message()->c_str());
|
||||
}
|
||||
}
|
||||
|
||||
inline engine_config_ptr argumentsToEngineConfig(const Nan::FunctionCallbackInfo<v8::Value> &args)
|
||||
{
|
||||
@ -725,6 +737,36 @@ inline bool argumentsToParameter(const Nan::FunctionCallbackInfo<v8::Value> &arg
|
||||
}
|
||||
}
|
||||
|
||||
if (Nan::Has(obj, Nan::New("format").ToLocalChecked()).FromJust())
|
||||
{
|
||||
v8::Local<v8::Value> format =
|
||||
Nan::Get(obj, Nan::New("format").ToLocalChecked()).ToLocalChecked();
|
||||
if (format.IsEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!format->IsString())
|
||||
{
|
||||
Nan::ThrowError("format must be a string: \"json\" or \"flatbuffers\"");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string format_str = *Nan::Utf8String(format);
|
||||
if (format_str == "json")
|
||||
{
|
||||
params->format = osrm::engine::api::BaseParameters::OutputFormatType::JSON;
|
||||
}
|
||||
else if (format_str == "flatbuffers")
|
||||
{
|
||||
params->format = osrm::engine::api::BaseParameters::OutputFormatType::FLATBUFFERS;
|
||||
}
|
||||
else
|
||||
{
|
||||
Nan::ThrowError("format must be a string: \"json\" or \"flatbuffers\"");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -885,17 +927,18 @@ inline bool parseCommonParameters(const v8::Local<v8::Object> &obj, ParamType &p
|
||||
return true;
|
||||
}
|
||||
|
||||
inline PluginParameters
|
||||
argumentsToPluginParameters(const Nan::FunctionCallbackInfo<v8::Value> &args)
|
||||
inline PluginParameters argumentsToPluginParameters(
|
||||
const Nan::FunctionCallbackInfo<v8::Value> &args,
|
||||
const boost::optional<osrm::engine::api::BaseParameters::OutputFormatType> &output_format = {})
|
||||
{
|
||||
if (args.Length() < 3 || !args[1]->IsObject())
|
||||
{
|
||||
return {};
|
||||
// output to buffer by default for Flatbuffers
|
||||
return {output_format == osrm::engine::api::BaseParameters::OutputFormatType::FLATBUFFERS};
|
||||
}
|
||||
v8::Local<v8::Object> obj = Nan::To<v8::Object>(args[1]).ToLocalChecked();
|
||||
if (Nan::Has(obj, Nan::New("format").ToLocalChecked()).FromJust())
|
||||
{
|
||||
|
||||
v8::Local<v8::Value> format =
|
||||
Nan::Get(obj, Nan::New("format").ToLocalChecked()).ToLocalChecked();
|
||||
if (format.IsEmpty())
|
||||
@ -905,7 +948,7 @@ argumentsToPluginParameters(const Nan::FunctionCallbackInfo<v8::Value> &args)
|
||||
|
||||
if (!format->IsString())
|
||||
{
|
||||
Nan::ThrowError("format must be a string: \"object\" or \"json_buffer\"");
|
||||
Nan::ThrowError("format must be a string: \"object\" or \"buffer\"");
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -914,20 +957,35 @@ argumentsToPluginParameters(const Nan::FunctionCallbackInfo<v8::Value> &args)
|
||||
|
||||
if (format_str == "object")
|
||||
{
|
||||
if (output_format == osrm::engine::api::BaseParameters::OutputFormatType::FLATBUFFERS)
|
||||
{
|
||||
Nan::ThrowError("Flatbuffers result can only output to buffer.");
|
||||
return {true};
|
||||
}
|
||||
return {false};
|
||||
}
|
||||
else if (format_str == "buffer")
|
||||
{
|
||||
return {true};
|
||||
}
|
||||
else if (format_str == "json_buffer")
|
||||
{
|
||||
if (output_format &&
|
||||
output_format != osrm::engine::api::BaseParameters::OutputFormatType::JSON)
|
||||
{
|
||||
Nan::ThrowError("Deprecated `json_buffer` can only be used with JSON format");
|
||||
}
|
||||
return {true};
|
||||
}
|
||||
else
|
||||
{
|
||||
Nan::ThrowError("format must be a string: \"object\" or \"json_buffer\"");
|
||||
Nan::ThrowError("format must be a string: \"object\" or \"buffer\"");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
// output to buffer by default for Flatbuffers
|
||||
return {output_format == osrm::engine::api::BaseParameters::OutputFormatType::FLATBUFFERS};
|
||||
}
|
||||
|
||||
inline route_parameters_ptr
|
||||
|
||||
@ -125,13 +125,13 @@ template <typename NodeEntryT, typename EdgeEntryT> class RemappableGraph
|
||||
|
||||
NodeID GetID(const NodeT &node) const
|
||||
{
|
||||
BOOST_ASSERT(&node >= &nodes[0] && &node <= &nodes.back());
|
||||
return (&node - &nodes[0]);
|
||||
BOOST_ASSERT(&node >= nodes.data() && &node <= &nodes.back());
|
||||
return (&node - nodes.data());
|
||||
}
|
||||
EdgeID GetID(const EdgeT &edge) const
|
||||
{
|
||||
BOOST_ASSERT(&edge >= &edges[0] && &edge <= &edges.back());
|
||||
return (&edge - &edges[0]);
|
||||
BOOST_ASSERT(&edge >= edges.data() && &edge <= &edges.back());
|
||||
return (&edge - edges.data());
|
||||
}
|
||||
|
||||
NodeIterator Begin() { return nodes.begin(); }
|
||||
@ -142,7 +142,7 @@ template <typename NodeEntryT, typename EdgeEntryT> class RemappableGraph
|
||||
// removes the edges from the graph that return true for the filter, returns new end
|
||||
template <typename FilterT> auto RemoveEdges(NodeT &node, FilterT filter)
|
||||
{
|
||||
BOOST_ASSERT(&node >= &nodes[0] && &node <= &nodes.back());
|
||||
BOOST_ASSERT(&node >= nodes.data() && &node <= &nodes.back());
|
||||
// required since we are not on std++17 yet, otherwise we are missing an argument_type
|
||||
const auto negate_filter = [&](const EdgeT &edge) { return !filter(edge); };
|
||||
const auto center = std::stable_partition(BeginEdges(node), EndEdges(node), negate_filter);
|
||||
|
||||
@ -78,10 +78,17 @@ struct BaseParametersGrammar : boost::spirit::qi::grammar<Iterator, Signature>
|
||||
: BaseParametersGrammar::base_type(root_rule)
|
||||
{
|
||||
const auto add_hint = [](engine::api::BaseParameters &base_parameters,
|
||||
const boost::optional<std::string> &hint_string) {
|
||||
if (hint_string)
|
||||
const std::vector<std::string> &hint_strings) {
|
||||
if (!hint_strings.empty())
|
||||
{
|
||||
base_parameters.hints.emplace_back(engine::Hint::FromBase64(hint_string.get()));
|
||||
std::vector<engine::SegmentHint> location_hints(hint_strings.size());
|
||||
std::transform(hint_strings.begin(),
|
||||
hint_strings.end(),
|
||||
location_hints.begin(),
|
||||
[](const auto &hint_string) {
|
||||
return engine::SegmentHint::FromBase64(hint_string);
|
||||
});
|
||||
base_parameters.hints.push_back(engine::Hint{std::move(location_hints)});
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -145,8 +152,9 @@ struct BaseParametersGrammar : boost::spirit::qi::grammar<Iterator, Signature>
|
||||
(-(qi::double_ | unlimited_rule) %
|
||||
';')[ph::bind(&engine::api::BaseParameters::radiuses, qi::_r1) = qi::_1];
|
||||
|
||||
hints_rule = qi::lit("hints=") >
|
||||
(-qi::as_string[qi::repeat(engine::ENCODED_HINT_SIZE)[base64_char]])[ph::bind(
|
||||
hints_rule =
|
||||
qi::lit("hints=") >
|
||||
(*qi::as_string[qi::repeat(engine::ENCODED_SEGMENT_HINT_SIZE)[base64_char]])[ph::bind(
|
||||
add_hint, qi::_r1, qi::_1)] %
|
||||
';';
|
||||
|
||||
|
||||
@ -36,9 +36,6 @@
|
||||
#include "util/vector_view.hpp"
|
||||
|
||||
#include "util/filtered_graph.hpp"
|
||||
#include "util/packed_vector.hpp"
|
||||
#include "util/vector_view.hpp"
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace storage
|
||||
|
||||
@ -49,8 +49,8 @@ struct Segment final
|
||||
|
||||
struct SpeedSource final
|
||||
{
|
||||
SpeedSource() : speed(0), rate() {}
|
||||
unsigned speed;
|
||||
SpeedSource() : speed(0.), rate() {}
|
||||
double speed;
|
||||
boost::optional<double> rate;
|
||||
std::uint8_t source;
|
||||
};
|
||||
|
||||
@ -25,7 +25,7 @@ template <typename T> class DistTableWrapper
|
||||
DistTableWrapper(std::vector<T> table, std::size_t number_of_nodes)
|
||||
: table_(std::move(table)), number_of_nodes_(number_of_nodes)
|
||||
{
|
||||
BOOST_ASSERT_MSG(table.size() == 0, "table is empty");
|
||||
BOOST_ASSERT_MSG(!table_.empty(), "table is empty");
|
||||
BOOST_ASSERT_MSG(number_of_nodes_ * number_of_nodes_ <= table_.size(),
|
||||
"number_of_nodes_ is invalid");
|
||||
}
|
||||
|
||||
@ -126,8 +126,8 @@ class FilteredGraphImpl<util::StaticGraph<EdgeDataT, Ownership>, Ownership>
|
||||
|
||||
FilteredGraphImpl() = default;
|
||||
|
||||
FilteredGraphImpl(Graph graph, Vector<bool> edge_filter_)
|
||||
: graph(std::move(graph)), edge_filter(std::move(edge_filter_))
|
||||
FilteredGraphImpl(Graph graph_, Vector<bool> edge_filter_)
|
||||
: graph(std::move(graph_)), edge_filter(std::move(edge_filter_))
|
||||
{
|
||||
BOOST_ASSERT(edge_filter.empty() || edge_filter.size() == graph.GetNumberOfEdges());
|
||||
}
|
||||
|
||||
@ -68,7 +68,7 @@ write(storage::tar::FileWriter &writer,
|
||||
|
||||
/***
|
||||
* Static RTree for serving nearest neighbour queries
|
||||
* // All coordinates are pojected first to Web Mercator before the bounding boxes
|
||||
* // All coordinates are projected first to Web Mercator before the bounding boxes
|
||||
* // are computed, this means the internal distance metric doesn not represent meters!
|
||||
*/
|
||||
|
||||
@ -556,7 +556,7 @@ class StaticRTree
|
||||
}
|
||||
|
||||
// Override filter and terminator for the desired behaviour.
|
||||
std::vector<EdgeDataT> Nearest(const Coordinate input_coordinate,
|
||||
std::vector<CandidateSegment> Nearest(const Coordinate input_coordinate,
|
||||
const std::size_t max_results) const
|
||||
{
|
||||
return Nearest(
|
||||
@ -567,13 +567,13 @@ class StaticRTree
|
||||
});
|
||||
}
|
||||
|
||||
// Override filter and terminator for the desired behaviour.
|
||||
// Return edges in distance order with the coordinate of the closest point on the edge.
|
||||
template <typename FilterT, typename TerminationT>
|
||||
std::vector<EdgeDataT> Nearest(const Coordinate input_coordinate,
|
||||
std::vector<CandidateSegment> Nearest(const Coordinate input_coordinate,
|
||||
const FilterT filter,
|
||||
const TerminationT terminate) const
|
||||
{
|
||||
std::vector<EdgeDataT> results;
|
||||
std::vector<CandidateSegment> results;
|
||||
auto projected_coordinate = web_mercator::fromWGS84(input_coordinate);
|
||||
Coordinate fixed_projected_coordinate{projected_coordinate};
|
||||
// initialize queue with root element
|
||||
@ -603,10 +603,10 @@ class StaticRTree
|
||||
}
|
||||
else
|
||||
{ // current candidate is an actual road segment
|
||||
// We deliberatly make a copy here, we mutate the value below
|
||||
auto edge_data = m_objects[current_query_node.segment_index];
|
||||
const auto ¤t_candidate =
|
||||
CandidateSegment{current_query_node.fixed_projected_coordinate, edge_data};
|
||||
const auto &edge_data = m_objects[current_query_node.segment_index];
|
||||
// We deliberately make an edge data copy here, we mutate the value below
|
||||
CandidateSegment current_candidate{current_query_node.fixed_projected_coordinate,
|
||||
edge_data};
|
||||
|
||||
// to allow returns of no-results if too restrictive filtering, this needs to be
|
||||
// done here even though performance would indicate that we want to stop after
|
||||
@ -621,11 +621,11 @@ class StaticRTree
|
||||
{
|
||||
continue;
|
||||
}
|
||||
edge_data.forward_segment_id.enabled &= use_segment.first;
|
||||
edge_data.reverse_segment_id.enabled &= use_segment.second;
|
||||
current_candidate.data.forward_segment_id.enabled &= use_segment.first;
|
||||
current_candidate.data.reverse_segment_id.enabled &= use_segment.second;
|
||||
|
||||
// store phantom node in result vector
|
||||
results.push_back(std::move(edge_data));
|
||||
results.push_back(std::move(current_candidate));
|
||||
}
|
||||
}
|
||||
|
||||
@ -676,7 +676,7 @@ class StaticRTree
|
||||
* Iterates over all the children of a TreeNode and inserts them into the search
|
||||
* priority queue using their distance from the search coordinate as the
|
||||
* priority metric.
|
||||
* The closests distance to a box from our point is also the closest distance
|
||||
* The closest distance to a box from our point is also the closest distance
|
||||
* to the closest line in that box (assuming the boxes hug their contents).
|
||||
*/
|
||||
template <class QueueT>
|
||||
|
||||
@ -5,6 +5,7 @@ api_version = 4
|
||||
Set = require('lib/set')
|
||||
Sequence = require('lib/sequence')
|
||||
Handlers = require("lib/way_handlers")
|
||||
TrafficSignal = require("lib/traffic_signal")
|
||||
find_access_tag = require("lib/access").find_access_tag
|
||||
limit = require("lib/maxspeed").limit
|
||||
Measure = require("lib/measure")
|
||||
@ -235,10 +236,7 @@ function process_node(profile, node, result)
|
||||
end
|
||||
|
||||
-- check if node is a traffic light
|
||||
local tag = node:get_value_by_key("highway")
|
||||
if tag and "traffic_signals" == tag then
|
||||
result.traffic_lights = true
|
||||
end
|
||||
result.traffic_lights = TrafficSignal.get_value(node)
|
||||
end
|
||||
|
||||
function handle_bicycle_tags(profile,way,result,data)
|
||||
|
||||
@ -6,6 +6,7 @@ Set = require('lib/set')
|
||||
Sequence = require('lib/sequence')
|
||||
Handlers = require("lib/way_handlers")
|
||||
Relations = require("lib/relations")
|
||||
TrafficSignal = require("lib/traffic_signal")
|
||||
find_access_tag = require("lib/access").find_access_tag
|
||||
limit = require("lib/maxspeed").limit
|
||||
Utils = require("lib/utils")
|
||||
@ -360,10 +361,7 @@ function process_node(profile, node, result, relations)
|
||||
end
|
||||
|
||||
-- check if node is a traffic light
|
||||
local tag = node:get_value_by_key("highway")
|
||||
if "traffic_signals" == tag then
|
||||
result.traffic_lights = true
|
||||
end
|
||||
result.traffic_lights = TrafficSignal.get_value(node)
|
||||
end
|
||||
|
||||
function process_way(profile, way, result, relations)
|
||||
|
||||
@ -157,6 +157,7 @@ function process_node(profile, node, result)
|
||||
-- check if node is a traffic light
|
||||
local tag = node:get_value_by_key("highway")
|
||||
if "traffic_signals" == tag then
|
||||
-- Direction should only apply to vehicles
|
||||
result.traffic_lights = true
|
||||
end
|
||||
end
|
||||
|
||||
26
profiles/lib/traffic_signal.lua
Normal file
26
profiles/lib/traffic_signal.lua
Normal file
@ -0,0 +1,26 @@
|
||||
-- Assigns traffic light value to node as defined by
|
||||
-- include/extractor/traffic_lights.hpp
|
||||
|
||||
local TrafficSignal = {}
|
||||
|
||||
function TrafficSignal.get_value(node)
|
||||
local tag = node:get_value_by_key("highway")
|
||||
if "traffic_signals" == tag then
|
||||
local direction = node:get_value_by_key("traffic_signals:direction")
|
||||
if direction then
|
||||
if "forward" == direction then
|
||||
return traffic_lights.direction_forward
|
||||
end
|
||||
if "backward" == direction then
|
||||
return traffic_lights.direction_reverse
|
||||
end
|
||||
end
|
||||
-- return traffic_lights.direction_all
|
||||
return true
|
||||
end
|
||||
-- return traffic_lights.none
|
||||
return false
|
||||
end
|
||||
|
||||
return TrafficSignal
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
-- Primary road: 36km/h = 36000m/3600s = 100m/10s
|
||||
-- Secondary road: 18km/h = 18000m/3600s = 100m/20s
|
||||
-- Tertiary road: 12km/h = 12000m/3600s = 100m/30s
|
||||
TrafficSignal = require("lib/traffic_signal")
|
||||
|
||||
api_version = 4
|
||||
|
||||
@ -38,13 +39,10 @@ function setup()
|
||||
end
|
||||
|
||||
function process_node (profile, node, result)
|
||||
local traffic_signal = node:get_value_by_key("highway")
|
||||
|
||||
if traffic_signal and traffic_signal == "traffic_signals" then
|
||||
result.traffic_lights = true
|
||||
-- check if node is a traffic light
|
||||
result.traffic_lights = TrafficSignal.get_value(node)
|
||||
-- TODO: a way to set the penalty value
|
||||
end
|
||||
end
|
||||
|
||||
function process_way (profile, way, result)
|
||||
local highway = way:get_value_by_key("highway")
|
||||
|
||||
@ -239,10 +239,10 @@ makeWaypoint(const util::Coordinate &location, const double &distance, std::stri
|
||||
util::json::Object makeWaypoint(const util::Coordinate &location,
|
||||
const double &distance,
|
||||
std::string name,
|
||||
const Hint &hint)
|
||||
const Hint &location_hints)
|
||||
{
|
||||
auto waypoint = makeWaypoint(location, distance, std::move(name));
|
||||
waypoint.values["hint"] = hint.ToBase64();
|
||||
waypoint.values["hint"] = location_hints.ToBase64();
|
||||
return waypoint;
|
||||
}
|
||||
|
||||
|
||||
@ -39,7 +39,7 @@ MMapMemoryAllocator::MMapMemoryAllocator(const storage::StorageConfig &config)
|
||||
// prior to C++17 (which we're not using), those return a `const char *`,
|
||||
// which isn't compatible with the `char *` that AllocatedRegion expects
|
||||
// for it's memory_ptr
|
||||
allocated_regions.push_back({&(rtree_filename[0]), std::move(fake_layout)});
|
||||
allocated_regions.push_back({rtree_filename.data(), std::move(fake_layout)});
|
||||
}
|
||||
|
||||
auto files = storage.GetStaticFiles();
|
||||
|
||||
@ -15,8 +15,6 @@
|
||||
#include <boost/numeric/conversion/cast.hpp>
|
||||
#include <boost/range/iterator_range.hpp>
|
||||
|
||||
#include "engine/guidance/collapsing_utility.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
|
||||
@ -3,10 +3,10 @@
|
||||
#include "engine/datafacade/datafacade_base.hpp"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/unordered_set.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <ostream>
|
||||
#include <tuple>
|
||||
|
||||
namespace osrm
|
||||
@ -14,7 +14,7 @@ namespace osrm
|
||||
namespace engine
|
||||
{
|
||||
|
||||
bool Hint::IsValid(const util::Coordinate new_input_coordinates,
|
||||
bool SegmentHint::IsValid(const util::Coordinate new_input_coordinates,
|
||||
const datafacade::BaseDataFacade &facade) const
|
||||
{
|
||||
auto is_same_input_coordinate = new_input_coordinates.lon == phantom.input_location.lon &&
|
||||
@ -25,7 +25,7 @@ bool Hint::IsValid(const util::Coordinate new_input_coordinates,
|
||||
return is_same_input_coordinate && phantom.IsValid() && facade.GetCheckSum() == data_checksum;
|
||||
}
|
||||
|
||||
std::string Hint::ToBase64() const
|
||||
std::string SegmentHint::ToBase64() const
|
||||
{
|
||||
auto base64 = encodeBase64Bytewise(*this);
|
||||
|
||||
@ -36,9 +36,9 @@ std::string Hint::ToBase64() const
|
||||
return base64;
|
||||
}
|
||||
|
||||
Hint Hint::FromBase64(const std::string &base64Hint)
|
||||
SegmentHint SegmentHint::FromBase64(const std::string &base64Hint)
|
||||
{
|
||||
BOOST_ASSERT_MSG(base64Hint.size() == ENCODED_HINT_SIZE, "Hint has invalid size");
|
||||
BOOST_ASSERT_MSG(base64Hint.size() == ENCODED_SEGMENT_HINT_SIZE, "Hint has invalid size");
|
||||
|
||||
// We need mutability but don't want to change the API
|
||||
auto encoded = base64Hint;
|
||||
@ -47,15 +47,82 @@ Hint Hint::FromBase64(const std::string &base64Hint)
|
||||
std::replace(begin(encoded), end(encoded), '-', '+');
|
||||
std::replace(begin(encoded), end(encoded), '_', '/');
|
||||
|
||||
return decodeBase64Bytewise<Hint>(encoded);
|
||||
return decodeBase64Bytewise<SegmentHint>(encoded);
|
||||
}
|
||||
|
||||
bool operator==(const Hint &lhs, const Hint &rhs)
|
||||
bool operator==(const SegmentHint &lhs, const SegmentHint &rhs)
|
||||
{
|
||||
return std::tie(lhs.phantom, lhs.data_checksum) == std::tie(rhs.phantom, rhs.data_checksum);
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const Hint &hint) { return out << hint.ToBase64(); }
|
||||
bool operator!=(const SegmentHint &lhs, const SegmentHint &rhs) { return !(lhs == rhs); }
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const SegmentHint &hint)
|
||||
{
|
||||
return out << hint.ToBase64();
|
||||
}
|
||||
|
||||
std::string Hint::ToBase64() const
|
||||
{
|
||||
std::string res;
|
||||
for (const auto &hint : segment_hints)
|
||||
{
|
||||
res += hint.ToBase64();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Hint Hint::FromBase64(const std::string &base64Hint)
|
||||
{
|
||||
|
||||
BOOST_ASSERT_MSG(base64Hint.size() % ENCODED_SEGMENT_HINT_SIZE == 0,
|
||||
"SegmentHint has invalid size");
|
||||
|
||||
auto num_hints = base64Hint.size() / ENCODED_SEGMENT_HINT_SIZE;
|
||||
std::vector<SegmentHint> res(num_hints);
|
||||
|
||||
for (const auto i : util::irange<std::size_t>(0UL, num_hints))
|
||||
{
|
||||
auto start_offset = i * ENCODED_SEGMENT_HINT_SIZE;
|
||||
auto end_offset = start_offset + ENCODED_SEGMENT_HINT_SIZE;
|
||||
res[i] = SegmentHint::FromBase64(
|
||||
std::string(base64Hint.begin() + start_offset, base64Hint.begin() + end_offset));
|
||||
}
|
||||
|
||||
return {std::move(res)};
|
||||
}
|
||||
|
||||
bool Hint::IsValid(const util::Coordinate new_input_coordinates,
|
||||
const datafacade::BaseDataFacade &facade) const
|
||||
{
|
||||
const auto all_valid =
|
||||
std::all_of(segment_hints.begin(), segment_hints.end(), [&](const auto &seg_hint) {
|
||||
return seg_hint.IsValid(new_input_coordinates, facade);
|
||||
});
|
||||
if (!all_valid)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check hints do not contain duplicate segment pairs
|
||||
// We can't allow duplicates as search heaps do not support it.
|
||||
boost::unordered_set<NodeID> forward_segments;
|
||||
boost::unordered_set<NodeID> reverse_segments;
|
||||
for (const auto &seg_hint : segment_hints)
|
||||
{
|
||||
const auto forward_res = forward_segments.insert(seg_hint.phantom.forward_segment_id.id);
|
||||
if (!forward_res.second)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const auto backward_res = reverse_segments.insert(seg_hint.phantom.reverse_segment_id.id);
|
||||
if (!backward_res.second)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace engine
|
||||
} // namespace osrm
|
||||
|
||||
@ -4,21 +4,16 @@
|
||||
#include "engine/api/match_api.hpp"
|
||||
#include "engine/api/match_parameters.hpp"
|
||||
#include "engine/api/match_parameters_tidy.hpp"
|
||||
#include "engine/map_matching/bayes_classifier.hpp"
|
||||
#include "engine/map_matching/sub_matching.hpp"
|
||||
#include "util/coordinate_calculation.hpp"
|
||||
#include "util/integer_range.hpp"
|
||||
#include "util/json_util.hpp"
|
||||
#include "util/string_util.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
@ -28,7 +23,7 @@ namespace engine
|
||||
namespace plugins
|
||||
{
|
||||
|
||||
// Filters PhantomNodes to obtain a set of viable candiates
|
||||
// Filters PhantomNodes to obtain a set of viable candidates
|
||||
void filterCandidates(const std::vector<util::Coordinate> &coordinates,
|
||||
MatchPlugin::CandidateLists &candidates_lists)
|
||||
{
|
||||
@ -272,20 +267,26 @@ Status MatchPlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms,
|
||||
|
||||
// FIXME we only run this to obtain the geometry
|
||||
// The clean way would be to get this directly from the map matching plugin
|
||||
PhantomNodes current_phantom_node_pair;
|
||||
for (unsigned i = 0; i < sub_matchings[index].nodes.size() - 1; ++i)
|
||||
{
|
||||
current_phantom_node_pair.source_phantom = sub_matchings[index].nodes[i];
|
||||
current_phantom_node_pair.target_phantom = sub_matchings[index].nodes[i + 1];
|
||||
BOOST_ASSERT(current_phantom_node_pair.source_phantom.IsValid());
|
||||
BOOST_ASSERT(current_phantom_node_pair.target_phantom.IsValid());
|
||||
sub_routes[index].segment_end_coordinates.emplace_back(current_phantom_node_pair);
|
||||
PhantomEndpoints current_endpoints{sub_matchings[index].nodes[i],
|
||||
sub_matchings[index].nodes[i + 1]};
|
||||
BOOST_ASSERT(current_endpoints.source_phantom.IsValid());
|
||||
BOOST_ASSERT(current_endpoints.target_phantom.IsValid());
|
||||
sub_routes[index].leg_endpoints.push_back(current_endpoints);
|
||||
}
|
||||
|
||||
std::vector<PhantomNodeCandidates> waypoint_candidates;
|
||||
waypoint_candidates.reserve(sub_matchings[index].nodes.size());
|
||||
std::transform(sub_matchings[index].nodes.begin(),
|
||||
sub_matchings[index].nodes.end(),
|
||||
std::back_inserter(waypoint_candidates),
|
||||
[](const auto &phantom) { return PhantomNodeCandidates{phantom}; });
|
||||
|
||||
// force uturns to be on
|
||||
// we split the phantom nodes anyway and only have bi-directional phantom nodes for
|
||||
// possible uturns
|
||||
sub_routes[index] =
|
||||
algorithms.ShortestPathSearch(sub_routes[index].segment_end_coordinates, {false});
|
||||
sub_routes[index] = algorithms.ShortestPathSearch(waypoint_candidates, {false});
|
||||
BOOST_ASSERT(sub_routes[index].shortest_path_weight != INVALID_EDGE_WEIGHT);
|
||||
if (collapse_legs)
|
||||
{
|
||||
|
||||
@ -1,10 +1,7 @@
|
||||
#include "engine/plugins/nearest.hpp"
|
||||
#include "engine/api/nearest_api.hpp"
|
||||
#include "engine/api/nearest_parameters.hpp"
|
||||
#include "engine/phantom_node.hpp"
|
||||
#include "util/integer_range.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
@ -2,17 +2,11 @@
|
||||
|
||||
#include "engine/api/table_api.hpp"
|
||||
#include "engine/api/table_parameters.hpp"
|
||||
#include "engine/routing_algorithms/many_to_many.hpp"
|
||||
#include "engine/search_engine_data.hpp"
|
||||
#include "util/coordinate_calculation.hpp"
|
||||
#include "util/json_container.hpp"
|
||||
#include "util/string_util.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
@ -47,7 +41,7 @@ Status TablePlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms,
|
||||
return Error("InvalidOptions", "Coordinates are invalid", result);
|
||||
}
|
||||
|
||||
if (params.bearings.size() > 0 && params.coordinates.size() != params.bearings.size())
|
||||
if (!params.bearings.empty() && params.coordinates.size() != params.bearings.size())
|
||||
{
|
||||
return Error(
|
||||
"InvalidOptions", "Number of bearings does not match number of coordinates", result);
|
||||
@ -79,7 +73,7 @@ Status TablePlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms,
|
||||
"NoSegment", MissingPhantomErrorMessage(phantom_nodes, params.coordinates), result);
|
||||
}
|
||||
|
||||
auto snapped_phantoms = SnapPhantomNodes(phantom_nodes);
|
||||
auto snapped_phantoms = SnapPhantomNodes(std::move(phantom_nodes));
|
||||
|
||||
bool request_distance = params.annotations & api::TableParameters::AnnotationsType::Distance;
|
||||
bool request_duration = params.annotations & api::TableParameters::AnnotationsType::Duration;
|
||||
@ -117,9 +111,11 @@ Status TablePlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms,
|
||||
params.fallback_coordinate_type ==
|
||||
api::TableParameters::FallbackCoordinateType::Input
|
||||
? util::coordinate_calculation::greatCircleDistance(
|
||||
source.input_location, destination.input_location)
|
||||
candidatesInputLocation(source),
|
||||
candidatesInputLocation(destination))
|
||||
: util::coordinate_calculation::greatCircleDistance(
|
||||
source.location, destination.location);
|
||||
candidatesSnappedLocation(source),
|
||||
candidatesSnappedLocation(destination));
|
||||
|
||||
result_tables_pair.first[table_index] =
|
||||
distance_estimate / (double)params.fallback_speed;
|
||||
|
||||
@ -20,7 +20,6 @@
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
|
||||
@ -4,18 +4,12 @@
|
||||
#include "engine/api/trip_parameters.hpp"
|
||||
#include "engine/trip/trip_brute_force.hpp"
|
||||
#include "engine/trip/trip_farthest_insertion.hpp"
|
||||
#include "engine/trip/trip_nearest_neighbour.hpp"
|
||||
#include "util/dist_table_wrapper.hpp" // to access the dist table more easily
|
||||
#include "util/json_container.hpp"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@ -41,40 +35,33 @@ bool IsSupportedParameterCombination(const bool fixed_start,
|
||||
|
||||
// given the node order in which to visit, compute the actual route (with geometry, travel time and
|
||||
// so on) and return the result
|
||||
InternalRouteResult TripPlugin::ComputeRoute(const RoutingAlgorithmsInterface &algorithms,
|
||||
const std::vector<PhantomNode> &snapped_phantoms,
|
||||
InternalRouteResult
|
||||
TripPlugin::ComputeRoute(const RoutingAlgorithmsInterface &algorithms,
|
||||
const std::vector<PhantomNodeCandidates> &waypoint_candidates,
|
||||
const std::vector<NodeID> &trip,
|
||||
const bool roundtrip) const
|
||||
{
|
||||
InternalRouteResult min_route;
|
||||
// given the final trip, compute total duration and return the route and location permutation
|
||||
PhantomNodes viapoint;
|
||||
|
||||
// computes a roundtrip from the nodes in trip
|
||||
for (auto node = trip.begin(); node < trip.end() - 1; ++node)
|
||||
{
|
||||
const auto from_node = *node;
|
||||
const auto to_node = *std::next(node);
|
||||
|
||||
viapoint = PhantomNodes{snapped_phantoms[from_node], snapped_phantoms[to_node]};
|
||||
min_route.segment_end_coordinates.emplace_back(viapoint);
|
||||
}
|
||||
|
||||
// TODO make a more efficient solution that doesn't require copying all the waypoints vectors.
|
||||
std::vector<PhantomNodeCandidates> trip_candidates;
|
||||
std::transform(trip.begin(),
|
||||
trip.end(),
|
||||
std::back_inserter(trip_candidates),
|
||||
[&](const auto &node) { return waypoint_candidates[node]; });
|
||||
// return back to the first node if it is a round trip
|
||||
if (roundtrip)
|
||||
{
|
||||
viapoint = PhantomNodes{snapped_phantoms[trip.back()], snapped_phantoms[trip.front()]};
|
||||
min_route.segment_end_coordinates.emplace_back(viapoint);
|
||||
trip_candidates.push_back(waypoint_candidates[trip.front()]);
|
||||
// trip comes out to be something like 0 1 4 3 2 0
|
||||
BOOST_ASSERT(min_route.segment_end_coordinates.size() == trip.size());
|
||||
BOOST_ASSERT(trip_candidates.size() == trip.size() + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// trip comes out to be something like 0 1 4 3 2, so the sizes don't match
|
||||
BOOST_ASSERT(min_route.segment_end_coordinates.size() == trip.size() - 1);
|
||||
// trip comes out to be something like 0 1 4 3 2
|
||||
BOOST_ASSERT(trip_candidates.size() == trip.size());
|
||||
}
|
||||
|
||||
min_route = algorithms.ShortestPathSearch(min_route.segment_end_coordinates, {false});
|
||||
auto min_route = algorithms.ShortestPathSearch(trip_candidates, {false});
|
||||
BOOST_ASSERT_MSG(min_route.shortest_path_weight < INVALID_EDGE_WEIGHT, "unroutable route");
|
||||
return min_route;
|
||||
}
|
||||
@ -226,7 +213,7 @@ Status TripPlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms,
|
||||
return Error("InvalidValue", "Invalid source or destination value.", result);
|
||||
}
|
||||
|
||||
auto snapped_phantoms = SnapPhantomNodes(phantom_node_pairs);
|
||||
auto snapped_phantoms = SnapPhantomNodes(std::move(phantom_node_pairs));
|
||||
|
||||
BOOST_ASSERT(snapped_phantoms.size() == number_of_locations);
|
||||
|
||||
|
||||
@ -5,12 +5,10 @@
|
||||
|
||||
#include "util/for_each_pair.hpp"
|
||||
#include "util/integer_range.hpp"
|
||||
#include "util/json_container.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@ -95,19 +93,10 @@ Status ViaRoutePlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithm
|
||||
}
|
||||
BOOST_ASSERT(phantom_node_pairs.size() == route_parameters.coordinates.size());
|
||||
|
||||
auto snapped_phantoms = SnapPhantomNodes(phantom_node_pairs);
|
||||
|
||||
std::vector<PhantomNodes> start_end_nodes;
|
||||
auto build_phantom_pairs = [&start_end_nodes](const PhantomNode &first_node,
|
||||
const PhantomNode &second_node) {
|
||||
start_end_nodes.push_back(PhantomNodes{first_node, second_node});
|
||||
};
|
||||
util::for_each_pair(snapped_phantoms, build_phantom_pairs);
|
||||
auto snapped_phantoms = SnapPhantomNodes(std::move(phantom_node_pairs));
|
||||
|
||||
api::RouteAPI route_api{facade, route_parameters};
|
||||
|
||||
InternalManyRoutesResult routes;
|
||||
|
||||
// TODO: in v6 we should remove the boolean and only keep the number parameter.
|
||||
// For now just force them to be in sync. and keep backwards compatibility.
|
||||
const auto wants_alternatives =
|
||||
@ -115,20 +104,23 @@ Status ViaRoutePlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithm
|
||||
(route_parameters.alternatives || route_parameters.number_of_alternatives > 0);
|
||||
const auto number_of_alternatives = std::max(1u, route_parameters.number_of_alternatives);
|
||||
|
||||
InternalManyRoutesResult routes;
|
||||
// Alternatives do not support vias, only direct s,t queries supported
|
||||
// See the implementation notes and high-level outline.
|
||||
// https://github.com/Project-OSRM/osrm-backend/issues/3905
|
||||
if (1 == start_end_nodes.size() && algorithms.HasAlternativePathSearch() && wants_alternatives)
|
||||
if (2 == snapped_phantoms.size() && algorithms.HasAlternativePathSearch() && wants_alternatives)
|
||||
{
|
||||
routes = algorithms.AlternativePathSearch(start_end_nodes.front(), number_of_alternatives);
|
||||
routes = algorithms.AlternativePathSearch({snapped_phantoms[0], snapped_phantoms[1]},
|
||||
number_of_alternatives);
|
||||
}
|
||||
else if (1 == start_end_nodes.size() && algorithms.HasDirectShortestPathSearch())
|
||||
else if (2 == snapped_phantoms.size() && algorithms.HasDirectShortestPathSearch())
|
||||
{
|
||||
routes = algorithms.DirectShortestPathSearch(start_end_nodes.front());
|
||||
routes = algorithms.DirectShortestPathSearch({snapped_phantoms[0], snapped_phantoms[1]});
|
||||
}
|
||||
else
|
||||
{
|
||||
routes = algorithms.ShortestPathSearch(start_end_nodes, route_parameters.continue_straight);
|
||||
routes =
|
||||
algorithms.ShortestPathSearch(snapped_phantoms, route_parameters.continue_straight);
|
||||
}
|
||||
|
||||
// The post condition for all path searches is we have at least one route in our result.
|
||||
@ -160,18 +152,29 @@ Status ViaRoutePlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithm
|
||||
}
|
||||
}
|
||||
|
||||
route_api.MakeResponse(routes, start_end_nodes, result);
|
||||
route_api.MakeResponse(routes, snapped_phantoms, result);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto first_component_id = snapped_phantoms.front().component.id;
|
||||
auto not_in_same_component = std::any_of(snapped_phantoms.begin(),
|
||||
snapped_phantoms.end(),
|
||||
[first_component_id](const PhantomNode &node) {
|
||||
return node.component.id != first_component_id;
|
||||
const auto all_in_same_component =
|
||||
[](const std::vector<PhantomNodeCandidates> &waypoint_candidates) {
|
||||
return std::any_of(waypoint_candidates.front().begin(),
|
||||
waypoint_candidates.front().end(),
|
||||
// For each of the first possible phantoms, check if all other
|
||||
// positions in the list have a phantom from the same component.
|
||||
[&](const PhantomNode &phantom) {
|
||||
const auto component_id = phantom.component.id;
|
||||
return std::all_of(
|
||||
std::next(waypoint_candidates.begin()),
|
||||
std::end(waypoint_candidates),
|
||||
[component_id](const PhantomNodeCandidates &candidates) {
|
||||
return candidatesHaveComponent(candidates,
|
||||
component_id);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
if (not_in_same_component)
|
||||
if (!all_in_same_component(snapped_phantoms))
|
||||
{
|
||||
return Error("NoRoute", "Impossible route between points", result);
|
||||
}
|
||||
|
||||
@ -190,8 +190,8 @@ void computeWeightAndSharingOfViaPath(SearchEngineData<Algorithm> &engine_workin
|
||||
s_v_middle,
|
||||
upper_bound_s_v_path_weight,
|
||||
min_edge_offset,
|
||||
DO_NOT_FORCE_LOOPS,
|
||||
DO_NOT_FORCE_LOOPS);
|
||||
{},
|
||||
{});
|
||||
}
|
||||
// compute path <v,..,t> by reusing backward search from node t
|
||||
NodeID v_t_middle = SPECIAL_NODEID;
|
||||
@ -205,8 +205,8 @@ void computeWeightAndSharingOfViaPath(SearchEngineData<Algorithm> &engine_workin
|
||||
v_t_middle,
|
||||
upper_bound_of_v_t_path_weight,
|
||||
min_edge_offset,
|
||||
DO_NOT_FORCE_LOOPS,
|
||||
DO_NOT_FORCE_LOOPS);
|
||||
{},
|
||||
{});
|
||||
}
|
||||
*real_weight_of_via_path = upper_bound_s_v_path_weight + upper_bound_of_v_t_path_weight;
|
||||
|
||||
@ -351,8 +351,8 @@ bool viaNodeCandidatePassesTTest(SearchEngineData<Algorithm> &engine_working_dat
|
||||
*s_v_middle,
|
||||
upper_bound_s_v_path_weight,
|
||||
min_edge_offset,
|
||||
DO_NOT_FORCE_LOOPS,
|
||||
DO_NOT_FORCE_LOOPS);
|
||||
{},
|
||||
{});
|
||||
}
|
||||
|
||||
if (INVALID_EDGE_WEIGHT == upper_bound_s_v_path_weight)
|
||||
@ -372,8 +372,8 @@ bool viaNodeCandidatePassesTTest(SearchEngineData<Algorithm> &engine_working_dat
|
||||
*v_t_middle,
|
||||
upper_bound_of_v_t_path_weight,
|
||||
min_edge_offset,
|
||||
DO_NOT_FORCE_LOOPS,
|
||||
DO_NOT_FORCE_LOOPS);
|
||||
{},
|
||||
{});
|
||||
}
|
||||
|
||||
if (INVALID_EDGE_WEIGHT == upper_bound_of_v_t_path_weight)
|
||||
@ -539,25 +539,13 @@ bool viaNodeCandidatePassesTTest(SearchEngineData<Algorithm> &engine_working_dat
|
||||
{
|
||||
if (!forward_heap3.Empty())
|
||||
{
|
||||
routingStep<FORWARD_DIRECTION>(facade,
|
||||
forward_heap3,
|
||||
reverse_heap3,
|
||||
middle,
|
||||
upper_bound,
|
||||
min_edge_offset,
|
||||
DO_NOT_FORCE_LOOPS,
|
||||
DO_NOT_FORCE_LOOPS);
|
||||
routingStep<FORWARD_DIRECTION>(
|
||||
facade, forward_heap3, reverse_heap3, middle, upper_bound, min_edge_offset, {}, {});
|
||||
}
|
||||
if (!reverse_heap3.Empty())
|
||||
{
|
||||
routingStep<REVERSE_DIRECTION>(facade,
|
||||
reverse_heap3,
|
||||
forward_heap3,
|
||||
middle,
|
||||
upper_bound,
|
||||
min_edge_offset,
|
||||
DO_NOT_FORCE_LOOPS,
|
||||
DO_NOT_FORCE_LOOPS);
|
||||
routingStep<REVERSE_DIRECTION>(
|
||||
facade, reverse_heap3, forward_heap3, middle, upper_bound, min_edge_offset, {}, {});
|
||||
}
|
||||
}
|
||||
return (upper_bound <= t_test_path_weight);
|
||||
@ -566,15 +554,12 @@ bool viaNodeCandidatePassesTTest(SearchEngineData<Algorithm> &engine_working_dat
|
||||
|
||||
InternalManyRoutesResult alternativePathSearch(SearchEngineData<Algorithm> &engine_working_data,
|
||||
const DataFacade<Algorithm> &facade,
|
||||
const PhantomNodes &phantom_node_pair,
|
||||
const PhantomEndpointCandidates &endpoint_candidates,
|
||||
unsigned /*number_of_alternatives*/)
|
||||
{
|
||||
InternalRouteResult primary_route;
|
||||
InternalRouteResult secondary_route;
|
||||
|
||||
primary_route.segment_end_coordinates = {phantom_node_pair};
|
||||
secondary_route.segment_end_coordinates = {phantom_node_pair};
|
||||
|
||||
std::vector<NodeID> alternative_path;
|
||||
std::vector<NodeID> via_node_candidate_list;
|
||||
std::vector<SearchSpaceEdge> forward_search_space;
|
||||
@ -592,15 +577,13 @@ InternalManyRoutesResult alternativePathSearch(SearchEngineData<Algorithm> &engi
|
||||
|
||||
EdgeWeight upper_bound_to_shortest_path_weight = INVALID_EDGE_WEIGHT;
|
||||
NodeID middle_node = SPECIAL_NODEID;
|
||||
const EdgeWeight min_edge_offset =
|
||||
std::min(phantom_node_pair.source_phantom.forward_segment_id.enabled
|
||||
? -phantom_node_pair.source_phantom.GetForwardWeightPlusOffset()
|
||||
: 0,
|
||||
phantom_node_pair.source_phantom.reverse_segment_id.enabled
|
||||
? -phantom_node_pair.source_phantom.GetReverseWeightPlusOffset()
|
||||
: 0);
|
||||
|
||||
insertNodesInHeaps(forward_heap1, reverse_heap1, phantom_node_pair);
|
||||
insertNodesInHeaps(forward_heap1, reverse_heap1, endpoint_candidates);
|
||||
// get offset to account for offsets on phantom nodes on compressed edges
|
||||
EdgeWeight min_edge_offset = forward_heap1.Empty() ? 0 : std::min(0, forward_heap1.MinKey());
|
||||
BOOST_ASSERT(min_edge_offset <= 0);
|
||||
// we only every insert negative offsets for nodes in the forward heap
|
||||
BOOST_ASSERT(reverse_heap1.Empty() || reverse_heap1.MinKey() >= 0);
|
||||
|
||||
// search from s and t till new_min/(1+epsilon) > weight_of_shortest_path
|
||||
while (0 < (forward_heap1.Size() + reverse_heap1.Size()))
|
||||
@ -790,7 +773,7 @@ InternalManyRoutesResult alternativePathSearch(SearchEngineData<Algorithm> &engi
|
||||
&v_t_middle,
|
||||
min_edge_offset))
|
||||
{
|
||||
// select first admissable
|
||||
// select first admissible
|
||||
selected_via_node = candidate.node;
|
||||
break;
|
||||
}
|
||||
@ -799,20 +782,23 @@ InternalManyRoutesResult alternativePathSearch(SearchEngineData<Algorithm> &engi
|
||||
// Unpack shortest path and alternative, if they exist
|
||||
if (INVALID_EDGE_WEIGHT != upper_bound_to_shortest_path_weight)
|
||||
{
|
||||
auto phantom_endpoints = endpointsFromCandidates(endpoint_candidates, packed_shortest_path);
|
||||
primary_route.leg_endpoints = {phantom_endpoints};
|
||||
|
||||
BOOST_ASSERT(!packed_shortest_path.empty());
|
||||
primary_route.unpacked_path_segments.resize(1);
|
||||
primary_route.source_traversed_in_reverse.push_back(
|
||||
(packed_shortest_path.front() !=
|
||||
phantom_node_pair.source_phantom.forward_segment_id.id));
|
||||
phantom_endpoints.source_phantom.forward_segment_id.id));
|
||||
primary_route.target_traversed_in_reverse.push_back((
|
||||
packed_shortest_path.back() != phantom_node_pair.target_phantom.forward_segment_id.id));
|
||||
packed_shortest_path.back() != phantom_endpoints.target_phantom.forward_segment_id.id));
|
||||
|
||||
unpackPath(facade,
|
||||
// -- packed input
|
||||
packed_shortest_path.begin(),
|
||||
packed_shortest_path.end(),
|
||||
// -- start of route
|
||||
phantom_node_pair,
|
||||
phantom_endpoints,
|
||||
// -- unpacked output
|
||||
primary_route.unpacked_path_segments.front());
|
||||
primary_route.shortest_path_weight = upper_bound_to_shortest_path_weight;
|
||||
@ -830,19 +816,23 @@ InternalManyRoutesResult alternativePathSearch(SearchEngineData<Algorithm> &engi
|
||||
v_t_middle,
|
||||
packed_alternate_path);
|
||||
|
||||
auto phantom_endpoints =
|
||||
endpointsFromCandidates(endpoint_candidates, packed_alternate_path);
|
||||
secondary_route.leg_endpoints = {phantom_endpoints};
|
||||
|
||||
secondary_route.unpacked_path_segments.resize(1);
|
||||
secondary_route.source_traversed_in_reverse.push_back(
|
||||
(packed_alternate_path.front() !=
|
||||
phantom_node_pair.source_phantom.forward_segment_id.id));
|
||||
phantom_endpoints.source_phantom.forward_segment_id.id));
|
||||
secondary_route.target_traversed_in_reverse.push_back(
|
||||
(packed_alternate_path.back() !=
|
||||
phantom_node_pair.target_phantom.forward_segment_id.id));
|
||||
phantom_endpoints.target_phantom.forward_segment_id.id));
|
||||
|
||||
// unpack the alternate path
|
||||
unpackPath(facade,
|
||||
packed_alternate_path.begin(),
|
||||
packed_alternate_path.end(),
|
||||
phantom_node_pair,
|
||||
phantom_endpoints,
|
||||
secondary_route.unpacked_path_segments.front());
|
||||
|
||||
secondary_route.shortest_path_weight = weight_of_via_path;
|
||||
|
||||
@ -133,12 +133,13 @@ double getLongerByFactorBasedOnDuration(const EdgeWeight duration)
|
||||
return a + b / (duration - d) + c / std::pow(duration - d, 3);
|
||||
}
|
||||
|
||||
Parameters parametersFromRequest(const PhantomNodes &phantom_node_pair)
|
||||
Parameters parametersFromRequest(const PhantomEndpointCandidates &endpoint_candidates)
|
||||
{
|
||||
Parameters parameters;
|
||||
|
||||
const auto distance = util::coordinate_calculation::greatCircleDistance(
|
||||
phantom_node_pair.source_phantom.location, phantom_node_pair.target_phantom.location);
|
||||
candidatesSnappedLocation(endpoint_candidates.source_phantoms),
|
||||
candidatesSnappedLocation(endpoint_candidates.target_phantoms));
|
||||
|
||||
// 10km
|
||||
if (distance < 10000.)
|
||||
@ -547,7 +548,7 @@ void unpackPackedPaths(InputIt first,
|
||||
OutIt out,
|
||||
SearchEngineData<Algorithm> &search_engine_data,
|
||||
const Facade &facade,
|
||||
const PhantomNodes &phantom_node_pair)
|
||||
const PhantomEndpointCandidates &endpoint_candidates)
|
||||
{
|
||||
util::static_assert_iter_category<InputIt, std::input_iterator_tag>();
|
||||
util::static_assert_iter_category<OutIt, std::output_iterator_tag>();
|
||||
@ -600,7 +601,7 @@ void unpackPackedPaths(InputIt first,
|
||||
}
|
||||
else
|
||||
{ // an overlay graph edge
|
||||
LevelID level = getNodeQueryLevel(partition, source, phantom_node_pair); // XXX
|
||||
LevelID level = getNodeQueryLevel(partition, source, endpoint_candidates); // XXX
|
||||
CellID parent_cell_id = partition.GetCell(level, source);
|
||||
BOOST_ASSERT(parent_cell_id == partition.GetCell(level, target));
|
||||
|
||||
@ -624,8 +625,8 @@ void unpackPackedPaths(InputIt first,
|
||||
facade,
|
||||
forward_heap,
|
||||
reverse_heap,
|
||||
DO_NOT_FORCE_LOOPS,
|
||||
DO_NOT_FORCE_LOOPS,
|
||||
{},
|
||||
{},
|
||||
INVALID_EDGE_WEIGHT,
|
||||
sublevel,
|
||||
parent_cell_id);
|
||||
@ -656,13 +657,13 @@ void unpackPackedPaths(InputIt first,
|
||||
inline std::vector<WeightedViaNode>
|
||||
makeCandidateVias(SearchEngineData<Algorithm> &search_engine_data,
|
||||
const Facade &facade,
|
||||
const PhantomNodes &phantom_node_pair,
|
||||
const PhantomEndpointCandidates &endpoint_candidates,
|
||||
const Parameters ¶meters)
|
||||
{
|
||||
Heap &forward_heap = *search_engine_data.forward_heap_1;
|
||||
Heap &reverse_heap = *search_engine_data.reverse_heap_1;
|
||||
|
||||
insertNodesInHeaps(forward_heap, reverse_heap, phantom_node_pair);
|
||||
insertNodesInHeaps(forward_heap, reverse_heap, endpoint_candidates);
|
||||
if (forward_heap.Empty() || reverse_heap.Empty())
|
||||
{
|
||||
return {};
|
||||
@ -712,9 +713,9 @@ makeCandidateVias(SearchEngineData<Algorithm> &search_engine_data,
|
||||
reverse_heap,
|
||||
overlap_via,
|
||||
overlap_weight,
|
||||
DO_NOT_FORCE_LOOPS,
|
||||
DO_NOT_FORCE_LOOPS,
|
||||
phantom_node_pair);
|
||||
{},
|
||||
{},
|
||||
endpoint_candidates);
|
||||
|
||||
if (!forward_heap.Empty())
|
||||
forward_heap_min = forward_heap.MinKey();
|
||||
@ -738,9 +739,9 @@ makeCandidateVias(SearchEngineData<Algorithm> &search_engine_data,
|
||||
forward_heap,
|
||||
overlap_via,
|
||||
overlap_weight,
|
||||
DO_NOT_FORCE_LOOPS,
|
||||
DO_NOT_FORCE_LOOPS,
|
||||
phantom_node_pair);
|
||||
{},
|
||||
{},
|
||||
endpoint_candidates);
|
||||
|
||||
if (!reverse_heap.Empty())
|
||||
reverse_heap_min = reverse_heap.MinKey();
|
||||
@ -776,10 +777,10 @@ makeCandidateVias(SearchEngineData<Algorithm> &search_engine_data,
|
||||
// https://github.com/Project-OSRM/osrm-backend/issues/3905
|
||||
InternalManyRoutesResult alternativePathSearch(SearchEngineData<Algorithm> &search_engine_data,
|
||||
const Facade &facade,
|
||||
const PhantomNodes &phantom_node_pair,
|
||||
const PhantomEndpointCandidates &endpoint_candidates,
|
||||
unsigned number_of_alternatives)
|
||||
{
|
||||
Parameters parameters = parametersFromRequest(phantom_node_pair);
|
||||
Parameters parameters = parametersFromRequest(endpoint_candidates);
|
||||
|
||||
const auto max_number_of_alternatives = number_of_alternatives;
|
||||
const auto max_number_of_alternatives_to_unpack =
|
||||
@ -798,7 +799,7 @@ InternalManyRoutesResult alternativePathSearch(SearchEngineData<Algorithm> &sear
|
||||
|
||||
// Do forward and backward search, save search space overlap as via candidates.
|
||||
auto candidate_vias =
|
||||
makeCandidateVias(search_engine_data, facade, phantom_node_pair, parameters);
|
||||
makeCandidateVias(search_engine_data, facade, endpoint_candidates, parameters);
|
||||
|
||||
const auto by_weight = [](const auto &lhs, const auto &rhs) { return lhs.weight < rhs.weight; };
|
||||
auto shortest_path_via_it =
|
||||
@ -813,8 +814,6 @@ InternalManyRoutesResult alternativePathSearch(SearchEngineData<Algorithm> &sear
|
||||
if (!has_shortest_path)
|
||||
{
|
||||
InternalRouteResult invalid;
|
||||
invalid.shortest_path_weight = INVALID_EDGE_WEIGHT;
|
||||
invalid.segment_end_coordinates = {phantom_node_pair};
|
||||
return invalid;
|
||||
}
|
||||
|
||||
@ -900,7 +899,7 @@ InternalManyRoutesResult alternativePathSearch(SearchEngineData<Algorithm> &sear
|
||||
std::back_inserter(unpacked_paths),
|
||||
search_engine_data,
|
||||
facade,
|
||||
phantom_node_pair);
|
||||
endpoint_candidates);
|
||||
|
||||
//
|
||||
// Filter and rank a second time. This time instead of being fast and doing
|
||||
@ -927,7 +926,7 @@ InternalManyRoutesResult alternativePathSearch(SearchEngineData<Algorithm> &sear
|
||||
routes.reserve(number_of_unpacked_paths);
|
||||
|
||||
const auto unpacked_path_to_route = [&](const WeightedViaNodeUnpackedPath &path) {
|
||||
return extractRoute(facade, path.via.weight, phantom_node_pair, path.nodes, path.edges);
|
||||
return extractRoute(facade, path.via.weight, endpoint_candidates, path.nodes, path.edges);
|
||||
};
|
||||
|
||||
std::transform(unpacked_paths_first,
|
||||
|
||||
@ -19,7 +19,7 @@ namespace routing_algorithms
|
||||
template <>
|
||||
InternalRouteResult directShortestPathSearch(SearchEngineData<ch::Algorithm> &engine_working_data,
|
||||
const DataFacade<ch::Algorithm> &facade,
|
||||
const PhantomNodes &phantom_nodes)
|
||||
const PhantomEndpointCandidates &endpoint_candidates)
|
||||
{
|
||||
engine_working_data.InitializeOrClearFirstThreadLocalStorage(facade.GetNumberOfNodes());
|
||||
auto &forward_heap = *engine_working_data.forward_heap_1;
|
||||
@ -29,7 +29,7 @@ InternalRouteResult directShortestPathSearch(SearchEngineData<ch::Algorithm> &en
|
||||
|
||||
EdgeWeight weight = INVALID_EDGE_WEIGHT;
|
||||
std::vector<NodeID> packed_leg;
|
||||
insertNodesInHeaps(forward_heap, reverse_heap, phantom_nodes);
|
||||
insertNodesInHeaps(forward_heap, reverse_heap, endpoint_candidates);
|
||||
|
||||
search(engine_working_data,
|
||||
facade,
|
||||
@ -37,9 +37,9 @@ InternalRouteResult directShortestPathSearch(SearchEngineData<ch::Algorithm> &en
|
||||
reverse_heap,
|
||||
weight,
|
||||
packed_leg,
|
||||
DO_NOT_FORCE_LOOPS,
|
||||
DO_NOT_FORCE_LOOPS,
|
||||
phantom_nodes);
|
||||
{},
|
||||
{},
|
||||
endpoint_candidates);
|
||||
|
||||
std::vector<NodeID> unpacked_nodes;
|
||||
std::vector<EdgeID> unpacked_edges;
|
||||
@ -60,19 +60,19 @@ InternalRouteResult directShortestPathSearch(SearchEngineData<ch::Algorithm> &en
|
||||
});
|
||||
}
|
||||
|
||||
return extractRoute(facade, weight, phantom_nodes, unpacked_nodes, unpacked_edges);
|
||||
return extractRoute(facade, weight, endpoint_candidates, unpacked_nodes, unpacked_edges);
|
||||
}
|
||||
|
||||
template <>
|
||||
InternalRouteResult directShortestPathSearch(SearchEngineData<mld::Algorithm> &engine_working_data,
|
||||
const DataFacade<mld::Algorithm> &facade,
|
||||
const PhantomNodes &phantom_nodes)
|
||||
const PhantomEndpointCandidates &endpoint_candidates)
|
||||
{
|
||||
engine_working_data.InitializeOrClearFirstThreadLocalStorage(facade.GetNumberOfNodes(),
|
||||
facade.GetMaxBorderNodeID() + 1);
|
||||
auto &forward_heap = *engine_working_data.forward_heap_1;
|
||||
auto &reverse_heap = *engine_working_data.reverse_heap_1;
|
||||
insertNodesInHeaps(forward_heap, reverse_heap, phantom_nodes);
|
||||
insertNodesInHeaps(forward_heap, reverse_heap, endpoint_candidates);
|
||||
|
||||
// TODO: when structured bindings will be allowed change to
|
||||
// auto [weight, source_node, target_node, unpacked_edges] = ...
|
||||
@ -83,12 +83,12 @@ InternalRouteResult directShortestPathSearch(SearchEngineData<mld::Algorithm> &e
|
||||
facade,
|
||||
forward_heap,
|
||||
reverse_heap,
|
||||
DO_NOT_FORCE_LOOPS,
|
||||
DO_NOT_FORCE_LOOPS,
|
||||
{},
|
||||
{},
|
||||
INVALID_EDGE_WEIGHT,
|
||||
phantom_nodes);
|
||||
endpoint_candidates);
|
||||
|
||||
return extractRoute(facade, weight, phantom_nodes, unpacked_nodes, unpacked_edges);
|
||||
return extractRoute(facade, weight, endpoint_candidates, unpacked_nodes, unpacked_edges);
|
||||
}
|
||||
|
||||
} // namespace routing_algorithms
|
||||
|
||||
@ -49,7 +49,7 @@ void relaxOutgoingEdges(
|
||||
const DataFacade<Algorithm> &facade,
|
||||
const typename SearchEngineData<Algorithm>::ManyToManyQueryHeap::HeapNode &heapNode,
|
||||
typename SearchEngineData<Algorithm>::ManyToManyQueryHeap &query_heap,
|
||||
const PhantomNode &)
|
||||
const PhantomNodeCandidates &)
|
||||
{
|
||||
if (stallAtNode<DIRECTION>(facade, heapNode, query_heap))
|
||||
{
|
||||
@ -99,7 +99,7 @@ void forwardRoutingStep(const DataFacade<Algorithm> &facade,
|
||||
std::vector<EdgeDuration> &durations_table,
|
||||
std::vector<EdgeDistance> &distances_table,
|
||||
std::vector<NodeID> &middle_nodes_table,
|
||||
const PhantomNode &phantom_node)
|
||||
const PhantomNodeCandidates &candidates)
|
||||
{
|
||||
// Take a copy of the extracted node because otherwise could be modified later if toHeapNode is
|
||||
// the same
|
||||
@ -151,14 +151,14 @@ void forwardRoutingStep(const DataFacade<Algorithm> &facade,
|
||||
}
|
||||
}
|
||||
|
||||
relaxOutgoingEdges<FORWARD_DIRECTION>(facade, heapNode, query_heap, phantom_node);
|
||||
relaxOutgoingEdges<FORWARD_DIRECTION>(facade, heapNode, query_heap, candidates);
|
||||
}
|
||||
|
||||
void backwardRoutingStep(const DataFacade<Algorithm> &facade,
|
||||
const unsigned column_index,
|
||||
typename SearchEngineData<Algorithm>::ManyToManyQueryHeap &query_heap,
|
||||
std::vector<NodeBucket> &search_space_with_buckets,
|
||||
const PhantomNode &phantom_node)
|
||||
const PhantomNodeCandidates &candidates)
|
||||
{
|
||||
// Take a copy (no ref &) of the extracted node because otherwise could be modified later if
|
||||
// toHeapNode is the same
|
||||
@ -172,7 +172,7 @@ void backwardRoutingStep(const DataFacade<Algorithm> &facade,
|
||||
heapNode.data.duration,
|
||||
heapNode.data.distance);
|
||||
|
||||
relaxOutgoingEdges<REVERSE_DIRECTION>(facade, heapNode, query_heap, phantom_node);
|
||||
relaxOutgoingEdges<REVERSE_DIRECTION>(facade, heapNode, query_heap, candidates);
|
||||
}
|
||||
|
||||
} // namespace ch
|
||||
@ -181,7 +181,7 @@ 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<PhantomNodeCandidates> &candidates_list,
|
||||
const std::vector<std::size_t> &source_indices,
|
||||
const std::vector<std::size_t> &target_indices,
|
||||
const bool calculate_distance)
|
||||
@ -202,18 +202,18 @@ manyToManySearch(SearchEngineData<ch::Algorithm> &engine_working_data,
|
||||
for (std::uint32_t column_index = 0; column_index < target_indices.size(); ++column_index)
|
||||
{
|
||||
const auto index = target_indices[column_index];
|
||||
const auto &phantom = phantom_nodes[index];
|
||||
const auto &target_candidates = candidates_list[index];
|
||||
|
||||
engine_working_data.InitializeOrClearManyToManyThreadLocalStorage(
|
||||
facade.GetNumberOfNodes());
|
||||
auto &query_heap = *(engine_working_data.many_to_many_heap);
|
||||
insertTargetInHeap(query_heap, phantom);
|
||||
insertTargetInHeap(query_heap, target_candidates);
|
||||
|
||||
// Explore search space
|
||||
while (!query_heap.Empty())
|
||||
{
|
||||
backwardRoutingStep(
|
||||
facade, column_index, query_heap, search_space_with_buckets, phantom);
|
||||
facade, column_index, query_heap, search_space_with_buckets, target_candidates);
|
||||
}
|
||||
}
|
||||
|
||||
@ -224,13 +224,13 @@ manyToManySearch(SearchEngineData<ch::Algorithm> &engine_working_data,
|
||||
for (std::uint32_t row_index = 0; row_index < source_indices.size(); ++row_index)
|
||||
{
|
||||
const auto source_index = source_indices[row_index];
|
||||
const auto &source_phantom = phantom_nodes[source_index];
|
||||
const auto &source_candidates = candidates_list[source_index];
|
||||
|
||||
// Clear heap and insert source nodes
|
||||
engine_working_data.InitializeOrClearManyToManyThreadLocalStorage(
|
||||
facade.GetNumberOfNodes());
|
||||
auto &query_heap = *(engine_working_data.many_to_many_heap);
|
||||
insertSourceInHeap(query_heap, source_phantom);
|
||||
insertSourceInHeap(query_heap, source_candidates);
|
||||
|
||||
// Explore search space
|
||||
while (!query_heap.Empty())
|
||||
@ -244,7 +244,7 @@ manyToManySearch(SearchEngineData<ch::Algorithm> &engine_working_data,
|
||||
durations_table,
|
||||
distances_table,
|
||||
middle_nodes_table,
|
||||
source_phantom);
|
||||
source_candidates);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -25,7 +25,7 @@ using PackedPath = std::vector<PackedEdge>;
|
||||
template <typename MultiLevelPartition>
|
||||
inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition,
|
||||
const NodeID node,
|
||||
const PhantomNode &phantom_node,
|
||||
const PhantomNodeCandidates &phantom_node,
|
||||
const LevelID maximal_level)
|
||||
{
|
||||
const auto node_level = getNodeQueryLevel(partition, node, phantom_node);
|
||||
@ -96,7 +96,7 @@ void relaxOutgoingEdges(
|
||||
const DataFacade<mld::Algorithm> &facade,
|
||||
const typename SearchEngineData<mld::Algorithm>::ManyToManyQueryHeap::HeapNode &heapNode,
|
||||
typename SearchEngineData<mld::Algorithm>::ManyToManyQueryHeap &query_heap,
|
||||
Args... args)
|
||||
const Args &... args)
|
||||
{
|
||||
BOOST_ASSERT(!facade.ExcludeNode(heapNode.node));
|
||||
|
||||
@ -214,26 +214,27 @@ template <bool DIRECTION>
|
||||
std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
|
||||
oneToManySearch(SearchEngineData<Algorithm> &engine_working_data,
|
||||
const DataFacade<Algorithm> &facade,
|
||||
const std::vector<PhantomNode> &phantom_nodes,
|
||||
std::size_t phantom_index,
|
||||
const std::vector<std::size_t> &phantom_indices,
|
||||
const std::vector<PhantomNodeCandidates> &candidates_list,
|
||||
std::size_t source_index,
|
||||
const std::vector<std::size_t> &target_indices,
|
||||
const bool calculate_distance)
|
||||
{
|
||||
std::vector<EdgeWeight> weights_table(phantom_indices.size(), INVALID_EDGE_WEIGHT);
|
||||
std::vector<EdgeDuration> durations_table(phantom_indices.size(), MAXIMAL_EDGE_DURATION);
|
||||
std::vector<EdgeDistance> distances_table(calculate_distance ? phantom_indices.size() : 0,
|
||||
std::vector<EdgeWeight> weights_table(target_indices.size(), INVALID_EDGE_WEIGHT);
|
||||
std::vector<EdgeDuration> durations_table(target_indices.size(), MAXIMAL_EDGE_DURATION);
|
||||
std::vector<EdgeDistance> distances_table(calculate_distance ? target_indices.size() : 0,
|
||||
MAXIMAL_EDGE_DISTANCE);
|
||||
std::vector<NodeID> middle_nodes_table(phantom_indices.size(), SPECIAL_NODEID);
|
||||
std::vector<NodeID> middle_nodes_table(target_indices.size(), SPECIAL_NODEID);
|
||||
|
||||
// Collect destination (source) nodes into a map
|
||||
std::unordered_multimap<NodeID, std::tuple<std::size_t, EdgeWeight, EdgeDuration, EdgeDistance>>
|
||||
target_nodes_index;
|
||||
target_nodes_index.reserve(phantom_indices.size());
|
||||
for (std::size_t index = 0; index < phantom_indices.size(); ++index)
|
||||
target_nodes_index.reserve(target_indices.size());
|
||||
for (std::size_t index = 0; index < target_indices.size(); ++index)
|
||||
{
|
||||
const auto &phantom_index = phantom_indices[index];
|
||||
const auto &phantom_node = phantom_nodes[phantom_index];
|
||||
const auto &target_candidates = candidates_list[target_indices[index]];
|
||||
|
||||
for (const auto &phantom_node : target_candidates)
|
||||
{
|
||||
if (DIRECTION == FORWARD_DIRECTION)
|
||||
{
|
||||
if (phantom_node.IsValidForwardTarget())
|
||||
@ -243,6 +244,7 @@ oneToManySearch(SearchEngineData<Algorithm> &engine_working_data,
|
||||
phantom_node.GetForwardWeightPlusOffset(),
|
||||
phantom_node.GetForwardDuration(),
|
||||
phantom_node.GetForwardDistance())});
|
||||
|
||||
if (phantom_node.IsValidReverseTarget())
|
||||
target_nodes_index.insert(
|
||||
{phantom_node.reverse_segment_id.id,
|
||||
@ -260,6 +262,7 @@ oneToManySearch(SearchEngineData<Algorithm> &engine_working_data,
|
||||
-phantom_node.GetForwardWeightPlusOffset(),
|
||||
-phantom_node.GetForwardDuration(),
|
||||
-phantom_node.GetForwardDistance())});
|
||||
|
||||
if (phantom_node.IsValidReverseSource())
|
||||
target_nodes_index.insert(
|
||||
{phantom_node.reverse_segment_id.id,
|
||||
@ -269,6 +272,7 @@ oneToManySearch(SearchEngineData<Algorithm> &engine_working_data,
|
||||
-phantom_node.GetReverseDistance())});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize query heap
|
||||
engine_working_data.InitializeOrClearManyToManyThreadLocalStorage(
|
||||
@ -337,8 +341,10 @@ oneToManySearch(SearchEngineData<Algorithm> &engine_working_data,
|
||||
};
|
||||
|
||||
{ // Place source (destination) adjacent nodes into the heap
|
||||
const auto &phantom_node = phantom_nodes[phantom_index];
|
||||
const auto &source_candidates = candidates_list[source_index];
|
||||
|
||||
for (const auto &phantom_node : source_candidates)
|
||||
{
|
||||
if (DIRECTION == FORWARD_DIRECTION)
|
||||
{
|
||||
if (phantom_node.IsValidForwardSource())
|
||||
@ -376,6 +382,7 @@ oneToManySearch(SearchEngineData<Algorithm> &engine_working_data,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (!query_heap.Empty() && !target_nodes_index.empty())
|
||||
{
|
||||
@ -389,7 +396,7 @@ oneToManySearch(SearchEngineData<Algorithm> &engine_working_data,
|
||||
|
||||
// Relax outgoing edges
|
||||
relaxOutgoingEdges<DIRECTION>(
|
||||
facade, heapNode, query_heap, phantom_nodes, phantom_index, phantom_indices);
|
||||
facade, heapNode, query_heap, candidates_list, source_index, target_indices);
|
||||
}
|
||||
|
||||
return std::make_pair(std::move(durations_table), std::move(distances_table));
|
||||
@ -409,7 +416,7 @@ void forwardRoutingStep(const DataFacade<Algorithm> &facade,
|
||||
std::vector<EdgeDuration> &durations_table,
|
||||
std::vector<EdgeDistance> &distances_table,
|
||||
std::vector<NodeID> &middle_nodes_table,
|
||||
const PhantomNode &phantom_node)
|
||||
const PhantomNodeCandidates &candidates)
|
||||
{
|
||||
// Take a copy of the extracted node because otherwise could be modified later if toHeapNode is
|
||||
// the same
|
||||
@ -455,7 +462,7 @@ void forwardRoutingStep(const DataFacade<Algorithm> &facade,
|
||||
}
|
||||
}
|
||||
|
||||
relaxOutgoingEdges<DIRECTION>(facade, heapNode, query_heap, phantom_node);
|
||||
relaxOutgoingEdges<DIRECTION>(facade, heapNode, query_heap, candidates);
|
||||
}
|
||||
|
||||
template <bool DIRECTION>
|
||||
@ -463,7 +470,7 @@ void backwardRoutingStep(const DataFacade<Algorithm> &facade,
|
||||
const unsigned column_idx,
|
||||
typename SearchEngineData<Algorithm>::ManyToManyQueryHeap &query_heap,
|
||||
std::vector<NodeBucket> &search_space_with_buckets,
|
||||
const PhantomNode &phantom_node)
|
||||
const PhantomNodeCandidates &candidates)
|
||||
{
|
||||
// Take a copy of the extracted node because otherwise could be modified later if toHeapNode is
|
||||
// the same
|
||||
@ -481,7 +488,7 @@ void backwardRoutingStep(const DataFacade<Algorithm> &facade,
|
||||
const auto &partition = facade.GetMultiLevelPartition();
|
||||
const auto maximal_level = partition.GetNumberOfLevels() - 1;
|
||||
|
||||
relaxOutgoingEdges<!DIRECTION>(facade, heapNode, query_heap, phantom_node, maximal_level);
|
||||
relaxOutgoingEdges<!DIRECTION>(facade, heapNode, query_heap, candidates, maximal_level);
|
||||
}
|
||||
|
||||
template <bool DIRECTION>
|
||||
@ -524,7 +531,7 @@ 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<PhantomNodeCandidates> &candidates_list,
|
||||
const std::vector<std::size_t> &source_indices,
|
||||
const std::vector<std::size_t> &target_indices,
|
||||
const bool calculate_distance)
|
||||
@ -545,22 +552,22 @@ manyToManySearch(SearchEngineData<Algorithm> &engine_working_data,
|
||||
for (std::uint32_t column_idx = 0; column_idx < target_indices.size(); ++column_idx)
|
||||
{
|
||||
const auto index = target_indices[column_idx];
|
||||
const auto &target_phantom = phantom_nodes[index];
|
||||
const auto &target_candidates = candidates_list[index];
|
||||
|
||||
engine_working_data.InitializeOrClearManyToManyThreadLocalStorage(
|
||||
facade.GetNumberOfNodes(), facade.GetMaxBorderNodeID() + 1);
|
||||
auto &query_heap = *(engine_working_data.many_to_many_heap);
|
||||
|
||||
if (DIRECTION == FORWARD_DIRECTION)
|
||||
insertTargetInHeap(query_heap, target_phantom);
|
||||
insertTargetInHeap(query_heap, target_candidates);
|
||||
else
|
||||
insertSourceInHeap(query_heap, target_phantom);
|
||||
insertSourceInHeap(query_heap, target_candidates);
|
||||
|
||||
// explore search space
|
||||
while (!query_heap.Empty())
|
||||
{
|
||||
backwardRoutingStep<DIRECTION>(
|
||||
facade, column_idx, query_heap, search_space_with_buckets, target_phantom);
|
||||
facade, column_idx, query_heap, search_space_with_buckets, target_candidates);
|
||||
}
|
||||
}
|
||||
|
||||
@ -571,7 +578,7 @@ manyToManySearch(SearchEngineData<Algorithm> &engine_working_data,
|
||||
for (std::uint32_t row_idx = 0; row_idx < source_indices.size(); ++row_idx)
|
||||
{
|
||||
const auto source_index = source_indices[row_idx];
|
||||
const auto &source_phantom = phantom_nodes[source_index];
|
||||
const auto &source_candidates = candidates_list[source_index];
|
||||
|
||||
// Clear heap and insert source nodes
|
||||
engine_working_data.InitializeOrClearManyToManyThreadLocalStorage(
|
||||
@ -580,9 +587,9 @@ manyToManySearch(SearchEngineData<Algorithm> &engine_working_data,
|
||||
auto &query_heap = *(engine_working_data.many_to_many_heap);
|
||||
|
||||
if (DIRECTION == FORWARD_DIRECTION)
|
||||
insertSourceInHeap(query_heap, source_phantom);
|
||||
insertSourceInHeap(query_heap, source_candidates);
|
||||
else
|
||||
insertTargetInHeap(query_heap, source_phantom);
|
||||
insertTargetInHeap(query_heap, source_candidates);
|
||||
|
||||
// Explore search space
|
||||
while (!query_heap.Empty())
|
||||
@ -597,7 +604,7 @@ manyToManySearch(SearchEngineData<Algorithm> &engine_working_data,
|
||||
durations_table,
|
||||
distances_table,
|
||||
middle_nodes_table,
|
||||
source_phantom);
|
||||
source_candidates);
|
||||
}
|
||||
}
|
||||
|
||||
@ -622,7 +629,7 @@ template <>
|
||||
std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
|
||||
manyToManySearch(SearchEngineData<mld::Algorithm> &engine_working_data,
|
||||
const DataFacade<mld::Algorithm> &facade,
|
||||
const std::vector<PhantomNode> &phantom_nodes,
|
||||
const std::vector<PhantomNodeCandidates> &candidates_list,
|
||||
const std::vector<std::size_t> &source_indices,
|
||||
const std::vector<std::size_t> &target_indices,
|
||||
const bool calculate_distance)
|
||||
@ -631,7 +638,7 @@ manyToManySearch(SearchEngineData<mld::Algorithm> &engine_working_data,
|
||||
{ // TODO: check if target_indices.size() == 1 and do a bi-directional search
|
||||
return mld::oneToManySearch<FORWARD_DIRECTION>(engine_working_data,
|
||||
facade,
|
||||
phantom_nodes,
|
||||
candidates_list,
|
||||
source_indices.front(),
|
||||
target_indices,
|
||||
calculate_distance);
|
||||
@ -641,7 +648,7 @@ manyToManySearch(SearchEngineData<mld::Algorithm> &engine_working_data,
|
||||
{
|
||||
return mld::oneToManySearch<REVERSE_DIRECTION>(engine_working_data,
|
||||
facade,
|
||||
phantom_nodes,
|
||||
candidates_list,
|
||||
target_indices.front(),
|
||||
source_indices,
|
||||
calculate_distance);
|
||||
@ -651,7 +658,7 @@ manyToManySearch(SearchEngineData<mld::Algorithm> &engine_working_data,
|
||||
{
|
||||
return mld::manyToManySearch<REVERSE_DIRECTION>(engine_working_data,
|
||||
facade,
|
||||
phantom_nodes,
|
||||
candidates_list,
|
||||
target_indices,
|
||||
source_indices,
|
||||
calculate_distance);
|
||||
@ -659,7 +666,7 @@ manyToManySearch(SearchEngineData<mld::Algorithm> &engine_working_data,
|
||||
|
||||
return mld::manyToManySearch<FORWARD_DIRECTION>(engine_working_data,
|
||||
facade,
|
||||
phantom_nodes,
|
||||
candidates_list,
|
||||
source_indices,
|
||||
target_indices,
|
||||
calculate_distance);
|
||||
|
||||
@ -7,30 +7,104 @@ namespace engine
|
||||
namespace routing_algorithms
|
||||
{
|
||||
|
||||
bool needsLoopForward(const PhantomNode &source_phantom, const PhantomNode &target_phantom)
|
||||
bool requiresForwardLoop(const PhantomNode &source, const PhantomNode &target)
|
||||
{
|
||||
return source_phantom.IsValidForwardSource() && target_phantom.IsValidForwardTarget() &&
|
||||
source_phantom.forward_segment_id.id == target_phantom.forward_segment_id.id &&
|
||||
source_phantom.GetForwardWeightPlusOffset() >
|
||||
target_phantom.GetForwardWeightPlusOffset();
|
||||
return source.IsValidForwardSource() && target.IsValidForwardTarget() &&
|
||||
source.forward_segment_id.id == target.forward_segment_id.id &&
|
||||
source.GetForwardWeightPlusOffset() > target.GetForwardWeightPlusOffset();
|
||||
}
|
||||
|
||||
bool needsLoopBackwards(const PhantomNode &source_phantom, const PhantomNode &target_phantom)
|
||||
bool requiresBackwardLoop(const PhantomNode &source, const PhantomNode &target)
|
||||
{
|
||||
return source_phantom.IsValidReverseSource() && target_phantom.IsValidReverseTarget() &&
|
||||
source_phantom.reverse_segment_id.id == target_phantom.reverse_segment_id.id &&
|
||||
source_phantom.GetReverseWeightPlusOffset() >
|
||||
target_phantom.GetReverseWeightPlusOffset();
|
||||
return source.IsValidReverseSource() && target.IsValidReverseTarget() &&
|
||||
source.reverse_segment_id.id == target.reverse_segment_id.id &&
|
||||
source.GetReverseWeightPlusOffset() > target.GetReverseWeightPlusOffset();
|
||||
}
|
||||
|
||||
bool needsLoopForward(const PhantomNodes &phantoms)
|
||||
std::vector<NodeID> getForwardLoopNodes(const PhantomEndpointCandidates &endpoint_candidates)
|
||||
{
|
||||
return needsLoopForward(phantoms.source_phantom, phantoms.target_phantom);
|
||||
std::vector<NodeID> res;
|
||||
for (const auto &source_phantom : endpoint_candidates.source_phantoms)
|
||||
{
|
||||
auto requires_loop =
|
||||
std::any_of(endpoint_candidates.target_phantoms.begin(),
|
||||
endpoint_candidates.target_phantoms.end(),
|
||||
[&](const auto &target_phantom) {
|
||||
return requiresForwardLoop(source_phantom, target_phantom);
|
||||
});
|
||||
if (requires_loop)
|
||||
{
|
||||
res.push_back(source_phantom.forward_segment_id.id);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool needsLoopBackwards(const PhantomNodes &phantoms)
|
||||
std::vector<NodeID> getForwardLoopNodes(const PhantomCandidatesToTarget &endpoint_candidates)
|
||||
{
|
||||
return needsLoopBackwards(phantoms.source_phantom, phantoms.target_phantom);
|
||||
std::vector<NodeID> res;
|
||||
for (const auto &source_phantom : endpoint_candidates.source_phantoms)
|
||||
{
|
||||
if (requiresForwardLoop(source_phantom, endpoint_candidates.target_phantom))
|
||||
{
|
||||
res.push_back(source_phantom.forward_segment_id.id);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<NodeID> getBackwardLoopNodes(const PhantomEndpointCandidates &endpoint_candidates)
|
||||
{
|
||||
std::vector<NodeID> res;
|
||||
for (const auto &source_phantom : endpoint_candidates.source_phantoms)
|
||||
{
|
||||
auto requires_loop =
|
||||
std::any_of(endpoint_candidates.target_phantoms.begin(),
|
||||
endpoint_candidates.target_phantoms.end(),
|
||||
[&](const auto &target_phantom) {
|
||||
return requiresBackwardLoop(source_phantom, target_phantom);
|
||||
});
|
||||
if (requires_loop)
|
||||
{
|
||||
res.push_back(source_phantom.reverse_segment_id.id);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<NodeID> getBackwardLoopNodes(const PhantomCandidatesToTarget &endpoint_candidates)
|
||||
{
|
||||
std::vector<NodeID> res;
|
||||
for (const auto &source_phantom : endpoint_candidates.source_phantoms)
|
||||
{
|
||||
if (requiresBackwardLoop(source_phantom, endpoint_candidates.target_phantom))
|
||||
{
|
||||
res.push_back(source_phantom.reverse_segment_id.id);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
PhantomEndpoints endpointsFromCandidates(const PhantomEndpointCandidates &candidates,
|
||||
const std::vector<NodeID> &path)
|
||||
{
|
||||
auto source_it = std::find_if(candidates.source_phantoms.begin(),
|
||||
candidates.source_phantoms.end(),
|
||||
[&path](const auto &source_phantom) {
|
||||
return path.front() == source_phantom.forward_segment_id.id ||
|
||||
path.front() == source_phantom.reverse_segment_id.id;
|
||||
});
|
||||
BOOST_ASSERT(source_it != candidates.source_phantoms.end());
|
||||
|
||||
auto target_it = std::find_if(candidates.target_phantoms.begin(),
|
||||
candidates.target_phantoms.end(),
|
||||
[&path](const auto &target_phantom) {
|
||||
return path.back() == target_phantom.forward_segment_id.id ||
|
||||
path.back() == target_phantom.reverse_segment_id.id;
|
||||
});
|
||||
BOOST_ASSERT(target_it != candidates.target_phantoms.end());
|
||||
|
||||
return PhantomEndpoints{*source_it, *target_it};
|
||||
}
|
||||
|
||||
} // namespace routing_algorithms
|
||||
|
||||
@ -95,9 +95,8 @@ void search(SearchEngineData<Algorithm> & /*engine_working_data*/,
|
||||
SearchEngineData<Algorithm>::QueryHeap &reverse_heap,
|
||||
EdgeWeight &weight,
|
||||
std::vector<NodeID> &packed_leg,
|
||||
const bool force_loop_forward,
|
||||
const bool force_loop_reverse,
|
||||
const PhantomNodes & /*phantom_nodes*/,
|
||||
const std::vector<NodeID> &force_loop_forward_nodes,
|
||||
const std::vector<NodeID> &force_loop_reverse_nodes,
|
||||
const EdgeWeight weight_upper_bound)
|
||||
{
|
||||
if (forward_heap.Empty() || reverse_heap.Empty())
|
||||
@ -126,8 +125,8 @@ void search(SearchEngineData<Algorithm> & /*engine_working_data*/,
|
||||
middle,
|
||||
weight,
|
||||
min_edge_offset,
|
||||
force_loop_forward,
|
||||
force_loop_reverse);
|
||||
force_loop_forward_nodes,
|
||||
force_loop_reverse_nodes);
|
||||
}
|
||||
if (!reverse_heap.Empty())
|
||||
{
|
||||
@ -137,8 +136,8 @@ void search(SearchEngineData<Algorithm> & /*engine_working_data*/,
|
||||
middle,
|
||||
weight,
|
||||
min_edge_offset,
|
||||
force_loop_reverse,
|
||||
force_loop_forward);
|
||||
force_loop_reverse_nodes,
|
||||
force_loop_forward_nodes);
|
||||
}
|
||||
}
|
||||
|
||||
@ -179,7 +178,8 @@ double getNetworkDistance(SearchEngineData<Algorithm> &engine_working_data,
|
||||
forward_heap.Clear();
|
||||
reverse_heap.Clear();
|
||||
|
||||
insertNodesInHeaps(forward_heap, reverse_heap, {source_phantom, target_phantom});
|
||||
PhantomEndpoints endpoints{source_phantom, target_phantom};
|
||||
insertNodesInHeaps(forward_heap, reverse_heap, endpoints);
|
||||
|
||||
EdgeWeight weight = INVALID_EDGE_WEIGHT;
|
||||
std::vector<NodeID> packed_path;
|
||||
@ -189,9 +189,9 @@ double getNetworkDistance(SearchEngineData<Algorithm> &engine_working_data,
|
||||
reverse_heap,
|
||||
weight,
|
||||
packed_path,
|
||||
DO_NOT_FORCE_LOOPS,
|
||||
DO_NOT_FORCE_LOOPS,
|
||||
{source_phantom, target_phantom},
|
||||
{},
|
||||
{},
|
||||
endpoints,
|
||||
weight_upper_bound);
|
||||
|
||||
if (weight == INVALID_EDGE_WEIGHT)
|
||||
|
||||
@ -12,13 +12,13 @@ namespace routing_algorithms
|
||||
template InternalRouteResult
|
||||
shortestPathSearch(SearchEngineData<ch::Algorithm> &engine_working_data,
|
||||
const DataFacade<ch::Algorithm> &facade,
|
||||
const std::vector<PhantomNodes> &phantom_nodes_vector,
|
||||
const std::vector<PhantomNodeCandidates> &waypoint_candidates,
|
||||
const boost::optional<bool> continue_straight_at_waypoint);
|
||||
|
||||
template InternalRouteResult
|
||||
shortestPathSearch(SearchEngineData<mld::Algorithm> &engine_working_data,
|
||||
const DataFacade<mld::Algorithm> &facade,
|
||||
const std::vector<PhantomNodes> &phantom_nodes_vector,
|
||||
const std::vector<PhantomNodeCandidates> &waypoint_candidates,
|
||||
const boost::optional<bool> continue_straight_at_waypoint);
|
||||
|
||||
} // namespace routing_algorithms
|
||||
|
||||
@ -59,7 +59,7 @@ EdgeBasedGraphFactory::EdgeBasedGraphFactory(
|
||||
EdgeBasedNodeDataContainer &node_data_container,
|
||||
const CompressedEdgeContainer &compressed_edge_container,
|
||||
const std::unordered_set<NodeID> &barrier_nodes,
|
||||
const std::unordered_set<NodeID> &traffic_lights,
|
||||
const TrafficSignals &traffic_signals,
|
||||
const std::vector<util::Coordinate> &coordinates,
|
||||
const NameTable &name_table,
|
||||
const std::unordered_set<EdgeID> &segregated_edges,
|
||||
@ -67,7 +67,7 @@ EdgeBasedGraphFactory::EdgeBasedGraphFactory(
|
||||
: m_edge_based_node_container(node_data_container), m_connectivity_checksum(0),
|
||||
m_number_of_edge_based_nodes(0), m_coordinates(coordinates),
|
||||
m_node_based_graph(node_based_graph), m_barrier_nodes(barrier_nodes),
|
||||
m_traffic_lights(traffic_lights), m_compressed_edge_container(compressed_edge_container),
|
||||
m_traffic_signals(traffic_signals), m_compressed_edge_container(compressed_edge_container),
|
||||
name_table(name_table), segregated_edges(segregated_edges),
|
||||
lane_description_map(lane_description_map)
|
||||
{
|
||||
@ -414,6 +414,48 @@ EdgeBasedGraphFactory::GenerateEdgeExpandedNodes(const WayRestrictionMap &way_re
|
||||
// the only consumer of this mapping).
|
||||
mapping.push_back(NBGToEBG{node_u, node_v, edge_based_node_id, SPECIAL_NODEID});
|
||||
|
||||
// We also want to include duplicate via edges in the list of segments that
|
||||
// an input location can snap to. Without this, it would be possible to not find
|
||||
// certain routes that end on a via-way, because they are only routable via the
|
||||
// duplicated edge.
|
||||
const auto &forward_geometry = m_compressed_edge_container.GetBucketReference(eid);
|
||||
const auto segment_count = forward_geometry.size();
|
||||
|
||||
NodeID current_edge_source_coordinate_id = node_u;
|
||||
const EdgeData &forward_data = m_node_based_graph.GetEdgeData(eid);
|
||||
|
||||
const auto edge_id_to_segment_id = [](const NodeID edge_based_node_id) {
|
||||
if (edge_based_node_id == SPECIAL_NODEID)
|
||||
{
|
||||
return SegmentID{SPECIAL_SEGMENTID, false};
|
||||
}
|
||||
|
||||
return SegmentID{edge_based_node_id, true};
|
||||
};
|
||||
|
||||
// Add segments of edge-based nodes
|
||||
for (const auto i : util::irange(std::size_t{0}, segment_count))
|
||||
{
|
||||
const NodeID current_edge_target_coordinate_id = forward_geometry[i].node_id;
|
||||
|
||||
// don't add node-segments for penalties
|
||||
if (current_edge_target_coordinate_id == current_edge_source_coordinate_id)
|
||||
continue;
|
||||
|
||||
BOOST_ASSERT(current_edge_target_coordinate_id !=
|
||||
current_edge_source_coordinate_id);
|
||||
|
||||
// build edges
|
||||
m_edge_based_node_segments.emplace_back(edge_id_to_segment_id(edge_based_node_id),
|
||||
SegmentID{SPECIAL_SEGMENTID, false},
|
||||
current_edge_source_coordinate_id,
|
||||
current_edge_target_coordinate_id,
|
||||
i,
|
||||
forward_data.flags.startpoint);
|
||||
|
||||
current_edge_source_coordinate_id = current_edge_target_coordinate_id;
|
||||
}
|
||||
|
||||
edge_based_node_id++;
|
||||
progress.PrintStatus(progress_counter++);
|
||||
}
|
||||
@ -581,8 +623,26 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
BOOST_ASSERT(!edge_data1.reversed);
|
||||
BOOST_ASSERT(!edge_data2.reversed);
|
||||
|
||||
// We write out the mapping between the edge-expanded edges and the original nodes.
|
||||
// Since each edge represents a possible maneuver, external programs can use this to
|
||||
// quickly perform updates to edge weights in order to penalize certain turns.
|
||||
|
||||
// If this edge is 'trivial' -- where the compressed edge corresponds exactly to an
|
||||
// original OSM segment -- we can pull the turn's preceding node ID directly with
|
||||
// `node_along_road_entering`;
|
||||
// otherwise, we need to look up the node immediately preceding the turn from the
|
||||
// compressed edge container.
|
||||
const bool isTrivial = m_compressed_edge_container.IsTrivial(node_based_edge_from);
|
||||
|
||||
const auto &from_node =
|
||||
isTrivial ? node_along_road_entering
|
||||
: m_compressed_edge_container.GetLastEdgeSourceID(node_based_edge_from);
|
||||
|
||||
// compute weight and duration penalties
|
||||
const auto is_traffic_light = m_traffic_lights.count(intersection_node);
|
||||
// In theory we shouldn't get a directed traffic light on a turn, as it indicates that
|
||||
// the traffic signal direction was potentially ambiguously annotated on the junction
|
||||
// node But we'll check anyway.
|
||||
const auto is_traffic_light = m_traffic_signals.HasSignal(from_node, intersection_node);
|
||||
const auto is_uturn =
|
||||
guidance::getTurnDirection(turn_angle) == guidance::DirectionModifier::UTurn;
|
||||
|
||||
@ -648,20 +708,6 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
true,
|
||||
false};
|
||||
|
||||
// We write out the mapping between the edge-expanded edges and the original nodes.
|
||||
// Since each edge represents a possible maneuver, external programs can use this to
|
||||
// quickly perform updates to edge weights in order to penalize certain turns.
|
||||
|
||||
// If this edge is 'trivial' -- where the compressed edge corresponds exactly to an
|
||||
// original OSM segment -- we can pull the turn's preceding node ID directly with
|
||||
// `node_along_road_entering`;
|
||||
// otherwise, we need to look up the node immediately preceding the turn from the
|
||||
// compressed edge container.
|
||||
const bool isTrivial = m_compressed_edge_container.IsTrivial(node_based_edge_from);
|
||||
|
||||
const auto &from_node =
|
||||
isTrivial ? node_along_road_entering
|
||||
: m_compressed_edge_container.GetLastEdgeSourceID(node_based_edge_from);
|
||||
const auto &to_node =
|
||||
m_compressed_edge_container.GetFirstEdgeTargetID(node_based_edge_to);
|
||||
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
#include "util/exception.hpp"
|
||||
#include "util/exception_utils.hpp"
|
||||
#include "util/for_each_indexed.hpp"
|
||||
#include "util/for_each_pair.hpp"
|
||||
#include "util/log.hpp"
|
||||
#include "util/timing_util.hpp"
|
||||
|
||||
@ -413,6 +414,7 @@ void ExtractionContainers::PrepareData(ScriptingEnvironment &scripting_environme
|
||||
|
||||
const auto restriction_ways = IdentifyRestrictionWays();
|
||||
const auto maneuver_override_ways = IdentifyManeuverOverrideWays();
|
||||
const auto traffic_signals = IdentifyTrafficSignals();
|
||||
|
||||
PrepareNodes();
|
||||
WriteNodes(writer);
|
||||
@ -422,6 +424,7 @@ void ExtractionContainers::PrepareData(ScriptingEnvironment &scripting_environme
|
||||
WriteEdges(writer);
|
||||
WriteMetadata(writer);
|
||||
|
||||
PrepareTrafficSignals(traffic_signals);
|
||||
PrepareManeuverOverrides(maneuver_override_ways);
|
||||
PrepareRestrictions(restriction_ways);
|
||||
WriteCharData(name_file_name);
|
||||
@ -911,25 +914,6 @@ void ExtractionContainers::WriteNodes(storage::tar::FileWriter &writer) const
|
||||
log << "ok, after " << TIMER_SEC(write_nodes) << "s";
|
||||
}
|
||||
|
||||
{
|
||||
util::UnbufferedLog log;
|
||||
log << "Writing traffic light nodes ... ";
|
||||
TIMER_START(write_nodes);
|
||||
std::vector<NodeID> internal_traffic_signals;
|
||||
for (const auto osm_id : traffic_signals)
|
||||
{
|
||||
const auto node_id = mapExternalToInternalNodeID(
|
||||
used_node_id_list.begin(), used_node_id_list.end(), osm_id);
|
||||
if (node_id != SPECIAL_NODEID)
|
||||
{
|
||||
internal_traffic_signals.push_back(node_id);
|
||||
}
|
||||
}
|
||||
storage::serialization::write(
|
||||
writer, "/extractor/traffic_lights", internal_traffic_signals);
|
||||
log << "ok, after " << TIMER_SEC(write_nodes) << "s";
|
||||
}
|
||||
|
||||
util::Log() << "Processed " << max_internal_node_id << " nodes";
|
||||
}
|
||||
|
||||
@ -983,6 +967,50 @@ ExtractionContainers::ReferencedWays ExtractionContainers::IdentifyManeuverOverr
|
||||
return maneuver_override_ways;
|
||||
}
|
||||
|
||||
void ExtractionContainers::PrepareTrafficSignals(
|
||||
const ExtractionContainers::ReferencedTrafficSignals &referenced_traffic_signals)
|
||||
{
|
||||
const auto &bidirectional_signal_nodes = referenced_traffic_signals.first;
|
||||
const auto &unidirectional_signal_segments = referenced_traffic_signals.second;
|
||||
|
||||
util::UnbufferedLog log;
|
||||
log << "Preparing traffic light signals for " << bidirectional_signal_nodes.size()
|
||||
<< " bidirectional, " << unidirectional_signal_segments.size()
|
||||
<< " unidirectional nodes ...";
|
||||
TIMER_START(prepare_traffic_signals);
|
||||
|
||||
std::unordered_set<NodeID> bidirectional;
|
||||
std::unordered_set<std::pair<NodeID, NodeID>, boost::hash<std::pair<NodeID, NodeID>>>
|
||||
unidirectional;
|
||||
|
||||
for (const auto &osm_node : bidirectional_signal_nodes)
|
||||
{
|
||||
const auto node_id = mapExternalToInternalNodeID(
|
||||
used_node_id_list.begin(), used_node_id_list.end(), osm_node);
|
||||
if (node_id != SPECIAL_NODEID)
|
||||
{
|
||||
bidirectional.insert(node_id);
|
||||
}
|
||||
}
|
||||
for (const auto &to_from : unidirectional_signal_segments)
|
||||
{
|
||||
const auto to_node_id = mapExternalToInternalNodeID(
|
||||
used_node_id_list.begin(), used_node_id_list.end(), to_from.first);
|
||||
const auto from_node_id = mapExternalToInternalNodeID(
|
||||
used_node_id_list.begin(), used_node_id_list.end(), to_from.second);
|
||||
if (from_node_id != SPECIAL_NODEID && to_node_id != SPECIAL_NODEID)
|
||||
{
|
||||
unidirectional.insert({from_node_id, to_node_id});
|
||||
}
|
||||
}
|
||||
|
||||
internal_traffic_signals.bidirectional_nodes = std::move(bidirectional);
|
||||
internal_traffic_signals.unidirectional_segments = std::move(unidirectional);
|
||||
|
||||
TIMER_STOP(prepare_traffic_signals);
|
||||
log << "ok, after " << TIMER_SEC(prepare_traffic_signals) << "s";
|
||||
}
|
||||
|
||||
void ExtractionContainers::PrepareManeuverOverrides(const ReferencedWays &maneuver_override_ways)
|
||||
{
|
||||
auto const osm_node_to_internal_nbn = [&](auto const osm_node) {
|
||||
@ -1163,6 +1191,93 @@ ExtractionContainers::ReferencedWays ExtractionContainers::IdentifyRestrictionWa
|
||||
return restriction_ways;
|
||||
}
|
||||
|
||||
ExtractionContainers::ReferencedTrafficSignals ExtractionContainers::IdentifyTrafficSignals()
|
||||
{
|
||||
util::UnbufferedLog log;
|
||||
log << "Collecting traffic signal information on " << external_traffic_signals.size()
|
||||
<< " signals...";
|
||||
TIMER_START(identify_traffic_signals);
|
||||
|
||||
// Temporary store for nodes containing a unidirectional signal.
|
||||
std::unordered_map<OSMNodeID, TrafficLightClass::Direction> unidirectional_signals;
|
||||
|
||||
// For each node that has a unidirectional traffic signal, we store the node(s)
|
||||
// that lead up to the signal.
|
||||
std::unordered_multimap<OSMNodeID, OSMNodeID> signal_segments;
|
||||
|
||||
std::unordered_set<OSMNodeID> bidirectional_signals;
|
||||
|
||||
const auto mark_signals = [&](auto const &traffic_signal) {
|
||||
if (traffic_signal.second == TrafficLightClass::DIRECTION_FORWARD ||
|
||||
traffic_signal.second == TrafficLightClass::DIRECTION_REVERSE)
|
||||
{
|
||||
unidirectional_signals.insert({traffic_signal.first, traffic_signal.second});
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(traffic_signal.second == TrafficLightClass::DIRECTION_ALL);
|
||||
bidirectional_signals.insert(traffic_signal.first);
|
||||
}
|
||||
};
|
||||
std::for_each(external_traffic_signals.begin(), external_traffic_signals.end(), mark_signals);
|
||||
|
||||
// Extract all the segments that lead up to unidirectional traffic signals.
|
||||
const auto set_segments = [&](const size_t way_list_idx, auto const & /*unused*/) {
|
||||
const auto node_start_offset =
|
||||
used_node_id_list.begin() + way_node_id_offsets[way_list_idx];
|
||||
const auto node_end_offset =
|
||||
used_node_id_list.begin() + way_node_id_offsets[way_list_idx + 1];
|
||||
|
||||
for (auto node_it = node_start_offset; node_it < node_end_offset; node_it++)
|
||||
{
|
||||
const auto sig = unidirectional_signals.find(*node_it);
|
||||
if (sig != unidirectional_signals.end())
|
||||
{
|
||||
if (sig->second == TrafficLightClass::DIRECTION_FORWARD)
|
||||
{
|
||||
if (node_it != node_start_offset)
|
||||
{
|
||||
// Previous node leads to signal
|
||||
signal_segments.insert({*node_it, *(node_it - 1)});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(sig->second == TrafficLightClass::DIRECTION_REVERSE);
|
||||
if (node_it + 1 != node_end_offset)
|
||||
{
|
||||
// Next node leads to signal
|
||||
signal_segments.insert({*node_it, *(node_it + 1)});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
util::for_each_indexed(ways_list.cbegin(), ways_list.cend(), set_segments);
|
||||
|
||||
util::for_each_pair(
|
||||
signal_segments, [](const auto pair_a, const auto pair_b) {
|
||||
if (pair_a.first == pair_b.first)
|
||||
{
|
||||
// If a node is appearing multiple times in this map, then it's ambiguous.
|
||||
// The node is an intersection and the traffic direction is being use for multiple
|
||||
// ways. We can't be certain of the original intent. See:
|
||||
// https://wiki.openstreetmap.org/wiki/Key:traffic_signals:direction
|
||||
|
||||
// OSRM will include the signal for all intersecting ways in the specified
|
||||
// direction, but let's flag this as a concern.
|
||||
util::Log(logWARNING)
|
||||
<< "OSM node " << pair_a.first
|
||||
<< " has a unidirectional traffic signal ambiguously applied to multiple ways";
|
||||
}
|
||||
});
|
||||
|
||||
TIMER_STOP(identify_traffic_signals);
|
||||
log << "ok, after " << TIMER_SEC(identify_traffic_signals) << "s";
|
||||
|
||||
return {std::move(bidirectional_signals), std::move(signal_segments)};
|
||||
}
|
||||
|
||||
void ExtractionContainers::PrepareRestrictions(const ReferencedWays &restriction_ways)
|
||||
{
|
||||
|
||||
|
||||
@ -75,7 +75,7 @@ void SetClassNames(const std::vector<std::string> &class_names,
|
||||
if (!class_names.empty())
|
||||
{
|
||||
// add class names that were never used explicitly on a way
|
||||
// this makes sure we can correctly validate unkown class names later
|
||||
// this makes sure we can correctly validate unknown class names later
|
||||
for (const auto &name : class_names)
|
||||
{
|
||||
if (!isValidClassName(name))
|
||||
@ -207,7 +207,8 @@ int Extractor::run(ScriptingEnvironment &scripting_environment)
|
||||
LaneDescriptionMap turn_lane_map;
|
||||
std::vector<TurnRestriction> turn_restrictions;
|
||||
std::vector<UnresolvedManeuverOverride> unresolved_maneuver_overrides;
|
||||
std::tie(turn_lane_map, turn_restrictions, unresolved_maneuver_overrides) =
|
||||
TrafficSignals traffic_signals;
|
||||
std::tie(turn_lane_map, turn_restrictions, unresolved_maneuver_overrides, traffic_signals) =
|
||||
ParseOSMData(scripting_environment, number_of_threads);
|
||||
|
||||
// Transform the node-based graph that OSM is based on into an edge-based graph
|
||||
@ -229,7 +230,8 @@ int Extractor::run(ScriptingEnvironment &scripting_environment)
|
||||
NodeBasedGraphFactory node_based_graph_factory(config.GetPath(".osrm"),
|
||||
scripting_environment,
|
||||
turn_restrictions,
|
||||
unresolved_maneuver_overrides);
|
||||
unresolved_maneuver_overrides,
|
||||
traffic_signals);
|
||||
|
||||
NameTable name_table;
|
||||
files::readNames(config.GetPath(".osrm.names"), name_table);
|
||||
@ -264,7 +266,6 @@ int Extractor::run(ScriptingEnvironment &scripting_environment)
|
||||
node_based_graph_factory.GetCompressedEdges().PrintStatistics();
|
||||
|
||||
const auto &barrier_nodes = node_based_graph_factory.GetBarriers();
|
||||
const auto &traffic_signals = node_based_graph_factory.GetTrafficSignals();
|
||||
// stealing the annotation data from the node-based graph
|
||||
edge_based_nodes_container =
|
||||
EdgeBasedNodeDataContainer({}, std::move(node_based_graph_factory.GetAnnotationData()));
|
||||
@ -360,8 +361,10 @@ int Extractor::run(ScriptingEnvironment &scripting_environment)
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::
|
||||
tuple<LaneDescriptionMap, std::vector<TurnRestriction>, std::vector<UnresolvedManeuverOverride>>
|
||||
std::tuple<LaneDescriptionMap,
|
||||
std::vector<TurnRestriction>,
|
||||
std::vector<UnresolvedManeuverOverride>,
|
||||
TrafficSignals>
|
||||
Extractor::ParseOSMData(ScriptingEnvironment &scripting_environment,
|
||||
const unsigned number_of_threads)
|
||||
{
|
||||
@ -628,7 +631,8 @@ std::
|
||||
|
||||
return std::make_tuple(std::move(turn_lane_map),
|
||||
std::move(extraction_containers.turn_restrictions),
|
||||
std::move(extraction_containers.internal_maneuver_overrides));
|
||||
std::move(extraction_containers.internal_maneuver_overrides),
|
||||
std::move(extraction_containers.internal_traffic_signals));
|
||||
}
|
||||
|
||||
void Extractor::FindComponents(unsigned number_of_edge_based_nodes,
|
||||
@ -699,7 +703,7 @@ EdgeID Extractor::BuildEdgeExpandedGraph(
|
||||
const std::vector<util::Coordinate> &coordinates,
|
||||
const CompressedEdgeContainer &compressed_edge_container,
|
||||
const std::unordered_set<NodeID> &barrier_nodes,
|
||||
const std::unordered_set<NodeID> &traffic_signals,
|
||||
const TrafficSignals &traffic_signals,
|
||||
const RestrictionGraph &restriction_graph,
|
||||
const std::unordered_set<EdgeID> &segregated_edges,
|
||||
const NameTable &name_table,
|
||||
|
||||
@ -78,9 +78,9 @@ void ExtractorCallbacks::ProcessNode(const osmium::Node &input_node,
|
||||
{
|
||||
external_memory.barrier_nodes.push_back(id);
|
||||
}
|
||||
if (result_node.traffic_lights)
|
||||
if (result_node.traffic_lights != TrafficLightClass::NONE)
|
||||
{
|
||||
external_memory.traffic_signals.push_back(id);
|
||||
external_memory.external_traffic_signals.push_back({id, result_node.traffic_lights});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -19,8 +19,10 @@ namespace osrm
|
||||
namespace extractor
|
||||
{
|
||||
|
||||
static constexpr int SECOND_TO_DECISECOND = 10;
|
||||
|
||||
void GraphCompressor::Compress(const std::unordered_set<NodeID> &barrier_nodes,
|
||||
const std::unordered_set<NodeID> &traffic_signals,
|
||||
const TrafficSignals &traffic_signals,
|
||||
ScriptingEnvironment &scripting_environment,
|
||||
std::vector<TurnRestriction> &turn_restrictions,
|
||||
std::vector<UnresolvedManeuverOverride> &maneuver_overrides,
|
||||
@ -207,16 +209,20 @@ void GraphCompressor::Compress(const std::unordered_set<NodeID> &barrier_nodes,
|
||||
rev_edge_data2.annotation_data, rev_edge_data1.annotation_data);
|
||||
|
||||
// Add node penalty when compress edge crosses a traffic signal
|
||||
const bool has_node_penalty = traffic_signals.find(node_v) != traffic_signals.end();
|
||||
EdgeDuration node_duration_penalty = MAXIMAL_EDGE_DURATION;
|
||||
EdgeWeight node_weight_penalty = INVALID_EDGE_WEIGHT;
|
||||
if (has_node_penalty)
|
||||
const bool has_forward_signal = traffic_signals.HasSignal(node_u, node_v);
|
||||
const bool has_reverse_signal = traffic_signals.HasSignal(node_w, node_v);
|
||||
|
||||
EdgeDuration forward_node_duration_penalty = MAXIMAL_EDGE_DURATION;
|
||||
EdgeWeight forward_node_weight_penalty = INVALID_EDGE_WEIGHT;
|
||||
EdgeDuration reverse_node_duration_penalty = MAXIMAL_EDGE_DURATION;
|
||||
EdgeWeight reverse_node_weight_penalty = INVALID_EDGE_WEIGHT;
|
||||
if (has_forward_signal || has_reverse_signal)
|
||||
{
|
||||
// we cannot handle this as node penalty, if it depends on turn direction
|
||||
if (fwd_edge_data1.flags.restricted != fwd_edge_data2.flags.restricted)
|
||||
continue;
|
||||
|
||||
// generate an artifical turn for the turn penalty generation
|
||||
// generate an artificial turn for the turn penalty generation
|
||||
std::vector<ExtractionTurnLeg> roads_on_the_right;
|
||||
std::vector<ExtractionTurnLeg> roads_on_the_left;
|
||||
ExtractionTurn extraction_turn(0,
|
||||
@ -245,8 +251,24 @@ void GraphCompressor::Compress(const std::unordered_set<NodeID> &barrier_nodes,
|
||||
roads_on_the_right,
|
||||
roads_on_the_left);
|
||||
scripting_environment.ProcessTurn(extraction_turn);
|
||||
node_duration_penalty = extraction_turn.duration * 10;
|
||||
node_weight_penalty = extraction_turn.weight * weight_multiplier;
|
||||
|
||||
auto update_direction_penalty =
|
||||
[&extraction_turn, weight_multiplier](bool signal,
|
||||
EdgeDuration &duration_penalty,
|
||||
EdgeWeight &weight_penalty) {
|
||||
if (signal)
|
||||
{
|
||||
duration_penalty = extraction_turn.duration * SECOND_TO_DECISECOND;
|
||||
weight_penalty = extraction_turn.weight * weight_multiplier;
|
||||
}
|
||||
};
|
||||
|
||||
update_direction_penalty(has_forward_signal,
|
||||
forward_node_duration_penalty,
|
||||
forward_node_weight_penalty);
|
||||
update_direction_penalty(has_reverse_signal,
|
||||
reverse_node_duration_penalty,
|
||||
reverse_node_weight_penalty);
|
||||
}
|
||||
|
||||
// Get weights before graph is modified
|
||||
@ -278,27 +300,37 @@ void GraphCompressor::Compress(const std::unordered_set<NodeID> &barrier_nodes,
|
||||
BOOST_ASSERT(0 != reverse_weight1);
|
||||
BOOST_ASSERT(0 != reverse_weight2);
|
||||
|
||||
// add weight of e2's to e1
|
||||
graph.GetEdgeData(forward_e1).weight += forward_weight2;
|
||||
graph.GetEdgeData(reverse_e1).weight += reverse_weight2;
|
||||
|
||||
// add duration of e2's to e1
|
||||
graph.GetEdgeData(forward_e1).duration += forward_duration2;
|
||||
graph.GetEdgeData(reverse_e1).duration += reverse_duration2;
|
||||
|
||||
// add distance of e2's to e1
|
||||
graph.GetEdgeData(forward_e1).distance += forward_distance2;
|
||||
graph.GetEdgeData(reverse_e1).distance += reverse_distance2;
|
||||
|
||||
if (node_weight_penalty != INVALID_EDGE_WEIGHT &&
|
||||
node_duration_penalty != MAXIMAL_EDGE_DURATION)
|
||||
auto apply_e2_to_e1 = [&graph](EdgeID edge,
|
||||
EdgeWeight weight,
|
||||
EdgeDuration duration,
|
||||
EdgeDistance distance,
|
||||
EdgeDuration &duration_penalty,
|
||||
EdgeWeight &weight_penalty) {
|
||||
auto &edge_data = graph.GetEdgeData(edge);
|
||||
edge_data.weight += weight;
|
||||
edge_data.duration += duration;
|
||||
edge_data.distance += distance;
|
||||
if (weight_penalty != INVALID_EDGE_WEIGHT &&
|
||||
duration_penalty != MAXIMAL_EDGE_DURATION)
|
||||
{
|
||||
graph.GetEdgeData(forward_e1).weight += node_weight_penalty;
|
||||
graph.GetEdgeData(reverse_e1).weight += node_weight_penalty;
|
||||
graph.GetEdgeData(forward_e1).duration += node_duration_penalty;
|
||||
graph.GetEdgeData(reverse_e1).duration += node_duration_penalty;
|
||||
edge_data.weight += weight_penalty;
|
||||
edge_data.duration += duration_penalty;
|
||||
// Note: no penalties for distances
|
||||
}
|
||||
};
|
||||
|
||||
apply_e2_to_e1(forward_e1,
|
||||
forward_weight2,
|
||||
forward_duration2,
|
||||
forward_distance2,
|
||||
forward_node_weight_penalty,
|
||||
forward_node_duration_penalty);
|
||||
apply_e2_to_e1(reverse_e1,
|
||||
reverse_weight2,
|
||||
reverse_duration2,
|
||||
reverse_distance2,
|
||||
reverse_node_weight_penalty,
|
||||
reverse_node_duration_penalty);
|
||||
|
||||
// extend e1's to targets of e2's
|
||||
graph.SetTarget(forward_e1, node_w);
|
||||
@ -311,6 +343,25 @@ void GraphCompressor::Compress(const std::unordered_set<NodeID> &barrier_nodes,
|
||||
// update any involved turn relations
|
||||
turn_path_compressor.Compress(node_u, node_v, node_w);
|
||||
|
||||
// Forward and reversed compressed edge lengths need to match.
|
||||
// Set a dummy empty penalty weight if opposite value exists.
|
||||
auto set_dummy_penalty = [](EdgeWeight &weight_penalty,
|
||||
EdgeDuration &duration_penalty,
|
||||
EdgeWeight &other_weight_penalty) {
|
||||
if (weight_penalty == INVALID_EDGE_WEIGHT &&
|
||||
other_weight_penalty != INVALID_EDGE_WEIGHT)
|
||||
{
|
||||
weight_penalty = 0;
|
||||
duration_penalty = 0;
|
||||
}
|
||||
};
|
||||
set_dummy_penalty(forward_node_weight_penalty,
|
||||
forward_node_duration_penalty,
|
||||
reverse_node_weight_penalty);
|
||||
set_dummy_penalty(reverse_node_weight_penalty,
|
||||
reverse_node_duration_penalty,
|
||||
forward_node_weight_penalty);
|
||||
|
||||
// store compressed geometry in container
|
||||
geometry_compressor.CompressEdge(forward_e1,
|
||||
forward_e2,
|
||||
@ -320,8 +371,8 @@ void GraphCompressor::Compress(const std::unordered_set<NodeID> &barrier_nodes,
|
||||
forward_weight2,
|
||||
forward_duration1,
|
||||
forward_duration2,
|
||||
node_weight_penalty,
|
||||
node_duration_penalty);
|
||||
forward_node_weight_penalty,
|
||||
forward_node_duration_penalty);
|
||||
geometry_compressor.CompressEdge(reverse_e1,
|
||||
reverse_e2,
|
||||
node_v,
|
||||
@ -330,8 +381,8 @@ void GraphCompressor::Compress(const std::unordered_set<NodeID> &barrier_nodes,
|
||||
reverse_weight2,
|
||||
reverse_duration1,
|
||||
reverse_duration2,
|
||||
node_weight_penalty,
|
||||
node_duration_penalty);
|
||||
reverse_node_weight_penalty,
|
||||
reverse_node_duration_penalty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,8 +11,6 @@
|
||||
#include "util/bearing.hpp"
|
||||
#include "util/coordinate_calculation.hpp"
|
||||
|
||||
using osrm::util::angularDeviation;
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
|
||||
@ -19,10 +19,11 @@ NodeBasedGraphFactory::NodeBasedGraphFactory(
|
||||
const boost::filesystem::path &input_file,
|
||||
ScriptingEnvironment &scripting_environment,
|
||||
std::vector<TurnRestriction> &turn_restrictions,
|
||||
std::vector<UnresolvedManeuverOverride> &maneuver_overrides)
|
||||
std::vector<UnresolvedManeuverOverride> &maneuver_overrides,
|
||||
const TrafficSignals &traffic_signals)
|
||||
{
|
||||
LoadDataFromFile(input_file);
|
||||
Compress(scripting_environment, turn_restrictions, maneuver_overrides);
|
||||
Compress(scripting_environment, turn_restrictions, maneuver_overrides, traffic_signals);
|
||||
CompressGeometry();
|
||||
CompressAnnotationData();
|
||||
}
|
||||
@ -31,16 +32,10 @@ NodeBasedGraphFactory::NodeBasedGraphFactory(
|
||||
void NodeBasedGraphFactory::LoadDataFromFile(const boost::filesystem::path &input_file)
|
||||
{
|
||||
auto barriers_iter = inserter(barriers, end(barriers));
|
||||
auto traffic_signals_iter = inserter(traffic_signals, end(traffic_signals));
|
||||
std::vector<NodeBasedEdge> edge_list;
|
||||
|
||||
files::readRawNBGraph(input_file,
|
||||
barriers_iter,
|
||||
traffic_signals_iter,
|
||||
coordinates,
|
||||
osm_node_ids,
|
||||
edge_list,
|
||||
annotation_data);
|
||||
files::readRawNBGraph(
|
||||
input_file, barriers_iter, coordinates, osm_node_ids, edge_list, annotation_data);
|
||||
|
||||
const auto number_of_node_based_nodes = coordinates.size();
|
||||
if (edge_list.empty())
|
||||
@ -80,7 +75,8 @@ void NodeBasedGraphFactory::LoadDataFromFile(const boost::filesystem::path &inpu
|
||||
|
||||
void NodeBasedGraphFactory::Compress(ScriptingEnvironment &scripting_environment,
|
||||
std::vector<TurnRestriction> &turn_restrictions,
|
||||
std::vector<UnresolvedManeuverOverride> &maneuver_overrides)
|
||||
std::vector<UnresolvedManeuverOverride> &maneuver_overrides,
|
||||
const TrafficSignals &traffic_signals)
|
||||
{
|
||||
GraphCompressor graph_compressor;
|
||||
graph_compressor.Compress(barriers,
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
#include "extractor/profile_properties.hpp"
|
||||
|
||||
#include "util/conditional_restrictions.hpp"
|
||||
#include "util/integer_range.hpp"
|
||||
#include "util/log.hpp"
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
@ -9,7 +10,6 @@
|
||||
#include <boost/ref.hpp>
|
||||
|
||||
#include <osmium/osm.hpp>
|
||||
#include <osmium/tags/regex_filter.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@ -20,9 +20,9 @@ namespace extractor
|
||||
|
||||
RestrictionParser::RestrictionParser(bool use_turn_restrictions_,
|
||||
bool parse_conditionals_,
|
||||
std::vector<std::string> &restrictions_)
|
||||
const std::vector<std::string> &restrictions_)
|
||||
: use_turn_restrictions(use_turn_restrictions_), parse_conditionals(parse_conditionals_),
|
||||
restrictions(restrictions_)
|
||||
restrictions(restrictions_.begin(), restrictions_.end()), filter(false)
|
||||
{
|
||||
if (use_turn_restrictions)
|
||||
{
|
||||
@ -40,11 +40,28 @@ RestrictionParser::RestrictionParser(bool use_turn_restrictions_,
|
||||
util::Log() << "Found no turn restriction tags";
|
||||
}
|
||||
}
|
||||
|
||||
filter.add(true, "restriction");
|
||||
if (parse_conditionals)
|
||||
{
|
||||
filter.add(true, "restriction:conditional");
|
||||
for (const auto &namespaced : restrictions_)
|
||||
{
|
||||
filter.add(true, "restriction:" + namespaced + ":conditional");
|
||||
}
|
||||
}
|
||||
|
||||
// Not only use restriction= but also e.g. restriction:motorcar=
|
||||
// Include restriction:{mode}:conditional if flagged
|
||||
for (const auto &namespaced : restrictions_)
|
||||
{
|
||||
filter.add(true, "restriction:" + namespaced);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to parse a relation as a turn restriction. This can fail for a number of
|
||||
* reasons. The return type is a boost::optional<T>.
|
||||
* reasons. The return type is a std::vector<T>.
|
||||
*
|
||||
* Some restrictions can also be ignored: See the ```get_restrictions``` function
|
||||
* in the corresponding profile. We use it for both namespacing restrictions, as in
|
||||
@ -59,31 +76,13 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const
|
||||
return {};
|
||||
}
|
||||
|
||||
osmium::tags::KeyFilter filter(false);
|
||||
filter.add(true, "restriction");
|
||||
if (parse_conditionals)
|
||||
{
|
||||
filter.add(true, "restriction:conditional");
|
||||
for (const auto &namespaced : restrictions)
|
||||
{
|
||||
filter.add(true, "restriction:" + namespaced + ":conditional");
|
||||
}
|
||||
}
|
||||
|
||||
// Not only use restriction= but also e.g. restriction:motorcar=
|
||||
// Include restriction:{mode}:conditional if flagged
|
||||
for (const auto &namespaced : restrictions)
|
||||
{
|
||||
filter.add(true, "restriction:" + namespaced);
|
||||
}
|
||||
|
||||
const osmium::TagList &tag_list = relation.tags();
|
||||
|
||||
osmium::tags::KeyFilter::iterator fi_begin(filter, tag_list.begin(), tag_list.end());
|
||||
osmium::tags::KeyFilter::iterator fi_end(filter, tag_list.end(), tag_list.end());
|
||||
|
||||
// if it's not a restriction, continue;
|
||||
if (std::distance(fi_begin, fi_end) == 0)
|
||||
if (fi_begin == fi_end)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
@ -99,18 +98,20 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const
|
||||
bool is_multi_from = false;
|
||||
bool is_multi_to = false;
|
||||
|
||||
std::vector<util::OpeningHours> condition;
|
||||
|
||||
for (; fi_begin != fi_end; ++fi_begin)
|
||||
{
|
||||
const std::string key(fi_begin->key());
|
||||
const std::string value(fi_begin->value());
|
||||
auto value = fi_begin->value();
|
||||
|
||||
// documented OSM restriction tags start either with only_* or no_*;
|
||||
// check and return on these values, and ignore no_*_on_red or unrecognized values
|
||||
if (value.find("only_") == 0)
|
||||
if (boost::algorithm::starts_with(value, "only_"))
|
||||
{
|
||||
is_only_restriction = true;
|
||||
}
|
||||
else if (value.find("no_") == 0 && !boost::algorithm::ends_with(value, "_on_red"))
|
||||
else if (boost::algorithm::starts_with(value, "no_") &&
|
||||
!boost::algorithm::ends_with(value, "_on_red"))
|
||||
{
|
||||
is_only_restriction = false;
|
||||
if (boost::algorithm::starts_with(value, "no_exit"))
|
||||
@ -126,76 +127,9 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
constexpr auto INVALID_OSM_ID = std::numeric_limits<std::uint64_t>::max();
|
||||
std::vector<OSMWayID> from_ways;
|
||||
auto via_node = INVALID_OSM_ID;
|
||||
std::vector<OSMWayID> via_ways;
|
||||
std::vector<OSMWayID> to_ways;
|
||||
bool is_node_restriction = true;
|
||||
|
||||
for (const auto &member : relation.members())
|
||||
{
|
||||
const char *role = member.role();
|
||||
if (strcmp("from", role) != 0 && strcmp("to", role) != 0 && strcmp("via", role) != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (member.type())
|
||||
{
|
||||
case osmium::item_type::node:
|
||||
{
|
||||
|
||||
// Make sure nodes appear only in the role if a via node
|
||||
if (0 == strcmp("from", role) || 0 == strcmp("to", role))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
BOOST_ASSERT(0 == strcmp("via", role));
|
||||
via_node = static_cast<std::uint64_t>(member.ref());
|
||||
is_node_restriction = true;
|
||||
// set via node id
|
||||
break;
|
||||
}
|
||||
case osmium::item_type::way:
|
||||
BOOST_ASSERT(0 == strcmp("from", role) || 0 == strcmp("to", role) ||
|
||||
0 == strcmp("via", role));
|
||||
if (0 == strcmp("from", role))
|
||||
{
|
||||
from_ways.push_back({static_cast<std::uint64_t>(member.ref())});
|
||||
}
|
||||
else if (0 == strcmp("to", role))
|
||||
{
|
||||
to_ways.push_back({static_cast<std::uint64_t>(member.ref())});
|
||||
}
|
||||
else if (0 == strcmp("via", role))
|
||||
{
|
||||
via_ways.push_back({static_cast<std::uint64_t>(member.ref())});
|
||||
is_node_restriction = false;
|
||||
}
|
||||
break;
|
||||
case osmium::item_type::relation:
|
||||
// not yet supported, but who knows what the future holds...
|
||||
break;
|
||||
default:
|
||||
// shouldn't ever happen
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<util::OpeningHours> condition;
|
||||
// parse conditional tags
|
||||
if (parse_conditionals)
|
||||
{
|
||||
osmium::tags::KeyFilter::iterator fi_begin(filter, tag_list.begin(), tag_list.end());
|
||||
osmium::tags::KeyFilter::iterator fi_end(filter, tag_list.end(), tag_list.end());
|
||||
for (; fi_begin != fi_end; ++fi_begin)
|
||||
{
|
||||
const std::string key(fi_begin->key());
|
||||
const std::string value(fi_begin->value());
|
||||
|
||||
// Parse condition and add independent value/condition pairs
|
||||
const auto &parsed = osrm::util::ParseConditionalRestrictions(value);
|
||||
|
||||
@ -214,6 +148,66 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const
|
||||
}
|
||||
}
|
||||
|
||||
constexpr auto INVALID_OSM_ID = std::numeric_limits<std::uint64_t>::max();
|
||||
std::vector<OSMWayID> from_ways;
|
||||
auto via_node = INVALID_OSM_ID;
|
||||
std::vector<OSMWayID> via_ways;
|
||||
std::vector<OSMWayID> to_ways;
|
||||
bool is_node_restriction = true;
|
||||
|
||||
for (const auto &member : relation.members())
|
||||
{
|
||||
const char *role = member.role();
|
||||
const bool is_from_role = strcmp("from", role) == 0;
|
||||
const bool is_to_role = strcmp("to", role) == 0;
|
||||
const bool is_via_role = strcmp("via", role) == 0;
|
||||
|
||||
if (!is_from_role && !is_to_role && !is_via_role)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (member.type())
|
||||
{
|
||||
case osmium::item_type::node:
|
||||
{
|
||||
|
||||
// Make sure nodes appear only in the role if a via node
|
||||
if (is_from_role || is_to_role)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
BOOST_ASSERT(is_via_role);
|
||||
via_node = static_cast<std::uint64_t>(member.ref());
|
||||
is_node_restriction = true;
|
||||
// set via node id
|
||||
break;
|
||||
}
|
||||
case osmium::item_type::way:
|
||||
BOOST_ASSERT(is_from_role || is_to_role || is_via_role);
|
||||
if (is_from_role)
|
||||
{
|
||||
from_ways.push_back({static_cast<std::uint64_t>(member.ref())});
|
||||
}
|
||||
else if (is_to_role)
|
||||
{
|
||||
to_ways.push_back({static_cast<std::uint64_t>(member.ref())});
|
||||
}
|
||||
else if (is_via_role)
|
||||
{
|
||||
via_ways.push_back({static_cast<std::uint64_t>(member.ref())});
|
||||
is_node_restriction = false;
|
||||
}
|
||||
break;
|
||||
case osmium::item_type::relation:
|
||||
// not yet supported, but who knows what the future holds...
|
||||
break;
|
||||
default:
|
||||
// shouldn't ever happen
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<InputTurnRestriction> restriction_containers;
|
||||
if (!from_ways.empty() && (via_node != INVALID_OSM_ID || !via_ways.empty()) && !to_ways.empty())
|
||||
{
|
||||
@ -270,17 +264,25 @@ bool RestrictionParser::ShouldIgnoreRestriction(const std::string &except_tag_st
|
||||
return false;
|
||||
}
|
||||
|
||||
// Be warned, this is quadratic work here, but we assume that
|
||||
// only a few exceptions are actually defined.
|
||||
const std::regex delimiter_re("[;][ ]*");
|
||||
std::sregex_token_iterator except_tags_begin(
|
||||
except_tag_string.begin(), except_tag_string.end(), delimiter_re, -1);
|
||||
std::sregex_token_iterator except_tags_end;
|
||||
|
||||
return std::any_of(except_tags_begin, except_tags_end, [&](const std::string ¤t_string) {
|
||||
return std::end(restrictions) !=
|
||||
std::find(std::begin(restrictions), std::end(restrictions), current_string);
|
||||
});
|
||||
// split `except_tag_string` by semicolon and check if any of items is in `restrictions`
|
||||
std::string current_string;
|
||||
for (auto index : util::irange<size_t>(0, except_tag_string.size()))
|
||||
{
|
||||
const auto ch = except_tag_string[index];
|
||||
if (ch != ';')
|
||||
{
|
||||
current_string += ch;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (restrictions.find(current_string) != restrictions.end())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
current_string.clear();
|
||||
}
|
||||
}
|
||||
return restrictions.find(current_string) != restrictions.end();
|
||||
}
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
|
||||
@ -285,9 +285,39 @@ void Sol2ScriptingEnvironment::InitContext(LuaScriptingContext &context)
|
||||
return get_location_tag(context, node.location(), key);
|
||||
});
|
||||
|
||||
context.state.new_usertype<ExtractionNode>("ResultNode",
|
||||
context.state.new_enum("traffic_lights",
|
||||
"none",
|
||||
extractor::TrafficLightClass::NONE,
|
||||
"direction_all",
|
||||
extractor::TrafficLightClass::DIRECTION_ALL,
|
||||
"direction_forward",
|
||||
extractor::TrafficLightClass::DIRECTION_FORWARD,
|
||||
"direction_reverse",
|
||||
extractor::TrafficLightClass::DIRECTION_REVERSE);
|
||||
|
||||
context.state.new_usertype<ExtractionNode>(
|
||||
"ResultNode",
|
||||
"traffic_lights",
|
||||
&ExtractionNode::traffic_lights,
|
||||
sol::property([](const ExtractionNode &node) { return node.traffic_lights; },
|
||||
[](ExtractionNode &node, const sol::object &obj) {
|
||||
if (obj.is<bool>())
|
||||
{
|
||||
// The old approach of assigning a boolean traffic light
|
||||
// state to the node is converted to the class enum
|
||||
// TODO: Make a breaking API change and remove this option.
|
||||
bool val = obj.as<bool>();
|
||||
node.traffic_lights = (val) ? TrafficLightClass::DIRECTION_ALL
|
||||
: TrafficLightClass::NONE;
|
||||
return;
|
||||
}
|
||||
|
||||
BOOST_ASSERT(obj.is<TrafficLightClass::Direction>());
|
||||
{
|
||||
TrafficLightClass::Direction val =
|
||||
obj.as<TrafficLightClass::Direction>();
|
||||
node.traffic_lights = val;
|
||||
}
|
||||
}),
|
||||
"barrier",
|
||||
&ExtractionNode::barrier);
|
||||
|
||||
|
||||
@ -7,13 +7,11 @@
|
||||
#include "util/log.hpp"
|
||||
|
||||
#include "util/bearing.hpp"
|
||||
#include "util/coordinate_calculation.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
|
||||
using EdgeData = osrm::util::NodeBasedDynamicGraph::EdgeData;
|
||||
using osrm::guidance::getTurnDirection;
|
||||
using osrm::util::angularDeviation;
|
||||
|
||||
namespace osrm
|
||||
|
||||
@ -11,7 +11,6 @@
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
using osrm::guidance::getTurnDirection;
|
||||
using osrm::util::angularDeviation;
|
||||
|
||||
namespace osrm
|
||||
|
||||
@ -15,8 +15,6 @@
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
using osrm::guidance::getTurnDirection;
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace guidance
|
||||
|
||||
@ -6,8 +6,6 @@
|
||||
#include "util/coordinate_calculation.hpp"
|
||||
#include <set>
|
||||
|
||||
using osrm::guidance::getTurnDirection;
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace guidance
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user