Compare commits

..

1 Commits

Author SHA1 Message Date
Patrick Niklaus 670cd8813c Bump OSRM version 2018-04-09 13:13:44 +00:00
871 changed files with 6862 additions and 67007 deletions
+1 -1
View File
@@ -13,9 +13,9 @@ notifications:
branches:
only:
- master
- "5.18"
# enable building tags
- /^v\d+\.\d+(\.\d+)?(-\S*)?$/
- "5.17"
cache:
yarn: true
-19
View File
@@ -1,20 +1,3 @@
# 5.18.0
- Changes from 5.17.0:
- Features:
- ADDED: `table` plugin now optionally returns `distance` matrix as part of response [#4990](https://github.com/Project-OSRM/osrm-backend/pull/4990)
- ADDED: New optional parameter `annotations` for `table` that accepts `distance`, `duration`, or both `distance,duration` as values [#4990](https://github.com/Project-OSRM/osrm-backend/pull/4990)
- Infrastructure:
- ADDED: Updated libosmium and added protozero and vtzero libraries [#5037](https://github.com/Project-OSRM/osrm-backend/pull/5037)
- CHANGED: Use vtzero library in tile plugin [#4686](https://github.com/Project-OSRM/osrm-backend/pull/4686)
- Profile:
- ADDED: Bicycle profile now returns classes for ferry and tunnel routes. [#5054](https://github.com/Project-OSRM/osrm-backend/pull/5054)
- ADDED: Bicycle profile allows to exclude ferry routes (default to not enabled) [#5054](https://github.com/Project-OSRM/osrm-backend/pull/5054)
# 5.17.1
- Changes from 5.17.0:
- Bugfixes:
- FIXED: Do not combine a segregated edge with a roundabout [#5039](https://github.com/Project-OSRM/osrm-backend/issues/5039)
# 5.17.0
- Changes from 5.16.0:
- Bugfixes:
@@ -23,11 +6,9 @@
- FIXED: Remove the last short annotation segment in `trimShortSegments` [#4946](https://github.com/Project-OSRM/osrm-backend/pull/4946)
- FIXED: Properly calculate annotations for speeds, durations and distances when waypoints are used with mapmatching [#4949](https://github.com/Project-OSRM/osrm-backend/pull/4949)
- FIXED: Don't apply unimplemented SH and PH conditions in OpeningHours and add inversed date ranges [#4992](https://github.com/Project-OSRM/osrm-backend/issues/4992)
- FIXED: integer overflow in `DynamicGraph::Renumber` [#5021](https://github.com/Project-OSRM/osrm-backend/pull/5021)
- Profile:
- CHANGED: Handle oneways in get_forward_backward_by_key [#4929](https://github.com/Project-OSRM/osrm-backend/pull/4929)
- FIXED: Do not route against oneway road if there is a cycleway in the wrong direction; also review bike profile [#4943](https://github.com/Project-OSRM/osrm-backend/issues/4943)
- CHANGED: Make cyclability weighting of the bike profile prefer safer routes more strongly [#5015](https://github.com/Project-OSRM/osrm-backend/issues/5015)
- Guidance:
- CHANGED: Don't use obviousness for links bifurcations [#4929](https://github.com/Project-OSRM/osrm-backend/pull/4929)
- FIXED: Adjust Straight direction modifiers of side roads in driveway handler [#4929](https://github.com/Project-OSRM/osrm-backend/pull/4929)
+13 -24
View File
@@ -167,7 +167,7 @@ add_executable(osrm-customize src/tools/customize.cpp)
add_executable(osrm-contract src/tools/contract.cpp)
add_executable(osrm-routed src/tools/routed.cpp $<TARGET_OBJECTS:SERVER> $<TARGET_OBJECTS:UTIL>)
add_executable(osrm-datastore src/tools/store.cpp $<TARGET_OBJECTS:MICROTAR> $<TARGET_OBJECTS:UTIL>)
add_library(osrm src/osrm/osrm.cpp $<TARGET_OBJECTS:ENGINE> $<TARGET_OBJECTS:STORAGE> $<TARGET_OBJECTS:MICROTAR> $<TARGET_OBJECTS:UTIL>)
add_library(osrm src/osrm/osrm.cpp $<TARGET_OBJECTS:ENGINE> $<TARGET_OBJECTS:STORAGE> $<TARGET_OBJECTS:MICROTAR> $<TARGET_OBJECTS:UTIL> )
add_library(osrm_contract src/osrm/contractor.cpp $<TARGET_OBJECTS:CONTRACTOR> $<TARGET_OBJECTS:UTIL>)
add_library(osrm_extract src/osrm/extractor.cpp $<TARGET_OBJECTS:EXTRACTOR> $<TARGET_OBJECTS:MICROTAR> $<TARGET_OBJECTS:UTIL>)
add_library(osrm_guidance $<TARGET_OBJECTS:GUIDANCE> $<TARGET_OBJECTS:UTIL>)
@@ -416,30 +416,11 @@ if(UNIX AND NOT APPLE)
set(MAYBE_RT_LIBRARY -lrt)
endif()
# Disallow deprecated protozero APIs
add_definitions(-DPROTOZERO_STRICT_API)
find_package(Threads REQUIRED)
# Third-party libraries
set(RAPIDJSON_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/rapidjson/include")
include_directories(SYSTEM ${RAPIDJSON_INCLUDE_DIR})
set(MICROTAR_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/microtar/src")
include_directories(SYSTEM ${MICROTAR_INCLUDE_DIR})
set(MBXGEOM_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/geometry.hpp-0.9.2/include")
include_directories(SYSTEM ${MBXGEOM_INCLUDE_DIR})
set(CHEAPRULER_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/cheap-ruler-cpp-2.5.4/include")
include_directories(SYSTEM ${CHEAPRULER_INCLUDE_DIR})
add_library(MICROTAR OBJECT "${CMAKE_CURRENT_SOURCE_DIR}/third_party/microtar/src/microtar.c")
set_property(TARGET MICROTAR PROPERTY POSITION_INDEPENDENT_CODE ON)
set(PROTOZERO_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/protozero/include")
include_directories(SYSTEM ${PROTOZERO_INCLUDE_DIR})
set(VTZERO_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/vtzero/include")
include_directories(SYSTEM ${VTZERO_INCLUDE_DIR})
# if mason is enabled no find_package calls are made
# to ensure that we are only compiling and linking against
# fully portable mason packages
@@ -573,6 +554,14 @@ else()
include_directories(SYSTEM ${OSMIUM_INCLUDE_DIR})
endif()
set(RAPIDJSON_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/rapidjson/include")
include_directories(SYSTEM ${RAPIDJSON_INCLUDE_DIR})
set(MICROTAR_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/microtar/src")
include_directories(SYSTEM ${MICROTAR_INCLUDE_DIR})
add_library(MICROTAR OBJECT "${CMAKE_CURRENT_SOURCE_DIR}/third_party/microtar/src/microtar.c")
set_property(TARGET MICROTAR PROPERTY POSITION_INDEPENDENT_CODE ON)
# prefix compilation with ccache by default if available and on clang or gcc
if(ENABLE_CCACHE AND (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU"))
find_program(CCACHE_FOUND ccache)
@@ -873,4 +862,4 @@ if (ENABLE_NODE_BINDINGS)
endforeach()
add_library(check-headers STATIC EXCLUDE_FROM_ALL ${sources})
set_target_properties(check-headers PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${check_headers_dir})
endif()
endif()
+1 -1
View File
@@ -9,7 +9,7 @@ High performance routing engine written in C++14 designed to run on OpenStreetMa
The following services are available via HTTP API, C++ library interface and NodeJs wrapper:
- Nearest - Snaps coordinates to the street network and returns the nearest matches
- Route - Finds the fastest route between coordinates
- Table - Computes the duration or distances of the fastest route between all pairs of supplied coordinates
- Table - Computes the duration of the fastest route between all pairs of supplied coordinates
- Match - Snaps noisy GPS traces to the road network in the most plausible way
- Trip - Solves the Traveling Salesman Problem using a greedy heuristic
- Tile - Generates Mapbox Vector Tiles with internal routing metadata
+1 -1
View File
@@ -3,5 +3,5 @@ module.exports = {
verify: '--strict --tags ~@stress --tags ~@todo --tags ~@mld-only -f progress --require features/support --require features/step_definitions',
todo: '--strict --tags @todo --require features/support --require features/step_definitions',
all: '--strict --require features/support --require features/step_definitions',
mld: '--strict --tags ~@stress --tags ~@todo --tags ~@ch --require features/support --require features/step_definitions -f progress'
mld: '--strict --tags ~@stress --tags ~@todo --require features/support --require features/step_definitions -f progress'
};
+8 -117
View File
@@ -222,13 +222,13 @@ curl 'http://router.project-osrm.org/route/v1/driving/13.388860,52.517037;13.397
### Table service
Computes the duration of the fastest route between all pairs of supplied coordinates. Returns the durations or distances or both between the coordinate pairs. Note that the distances are not the shortest distance between two coordinates, but rather the distances of the fastest routes. Duration is in seconds and distances is in meters.
Computes the duration of the fastest route between all pairs of supplied coordinates.
```endpoint
GET /table/v1/{profile}/{coordinates}?{sources}=[{elem}...];&{destinations}=[{elem}...]&annotations={duration|distance|duration,distance}
GET /table/v1/{profile}/{coordinates}?{sources}=[{elem}...];&destinations=[{elem}...]
```
**Options**
**Coordinates**
In addition to the [general options](#general-options) the following options are supported for this service:
@@ -236,8 +236,6 @@ In addition to the [general options](#general-options) the following options are
|------------|--------------------------------------------------|---------------------------------------------|
|sources |`{index};{index}[;{index} ...]` or `all` (default)|Use location with given index as source. |
|destinations|`{index};{index}[;{index} ...]` or `all` (default)|Use location with given index as destination.|
|annotations |`duration` (default), `distance`, or `duration,distance`|Return the requested table or tables in response. Note that computing the `distances` table is currently only implemented for CH. If `annotations=distance` or `annotations=duration,distance` is requested when running a MLD router, a `NotImplemented` error will be returned.
|
Unlike other array encoded options, the length of `sources` and `destinations` can be **smaller or equal**
to number of input locations;
@@ -255,23 +253,14 @@ sources=0;5;7&destinations=5;1;4;2;3;6
#### Example Request
```curl
# Returns a 3x3 duration matrix:
# Returns a 3x3 matrix:
curl 'http://router.project-osrm.org/table/v1/driving/13.388860,52.517037;13.397634,52.529407;13.428555,52.523219'
# Returns a 1x3 duration matrix
# Returns a 1x3 matrix
curl 'http://router.project-osrm.org/table/v1/driving/13.388860,52.517037;13.397634,52.529407;13.428555,52.523219?sources=0'
# Returns a asymmetric 3x2 duration matrix with from the polyline encoded locations `qikdcB}~dpXkkHz`:
# Returns a asymmetric 3x2 matrix with from the polyline encoded locations `qikdcB}~dpXkkHz`:
curl 'http://router.project-osrm.org/table/v1/driving/polyline(egs_Iq_aqAppHzbHulFzeMe`EuvKpnCglA)?sources=0;1;3&destinations=2;4'
# Returns a 3x3 duration matrix:
curl 'http://router.project-osrm.org/table/v1/driving/13.388860,52.517037;13.397634,52.529407;13.428555,52.523219?annotations=duration'
# Returns a 3x3 distance matrix for CH:
curl 'http://router.project-osrm.org/table/v1/driving/13.388860,52.517037;13.397634,52.529407;13.428555,52.523219?annotations=distance'
# Returns a 3x3 duration matrix and a 3x3 distance matrix for CH:
curl 'http://router.project-osrm.org/table/v1/driving/13.388860,52.517037;13.397634,52.529407;13.428555,52.523219?annotations=distance,duration'
```
**Response**
@@ -279,115 +268,17 @@ curl 'http://router.project-osrm.org/table/v1/driving/13.388860,52.517037;13.397
- `code` if the request was successful `Ok` otherwise see the service dependent and general status codes.
- `durations` array of arrays that stores the matrix in row-major order. `durations[i][j]` gives the travel time from
the i-th waypoint to the j-th waypoint. Values are given in seconds. Can be `null` if no route between `i` and `j` can be found.
- `distances` array of arrays that stores the matrix in row-major order. `distances[i][j]` gives the travel distance from
the i-th waypoint to the j-th waypoint. Values are given in meters. Can be `null` if no route between `i` and `j` can be found. Note that computing the `distances` table is currently only implemented for CH. If `annotations=distance` or `annotations=duration,distance` is requested when running a MLD router, a `NotImplemented` error will be returned.
- `sources` array of `Waypoint` objects describing all sources in order
- `destinations` array of `Waypoint` objects describing all destinations in order
In case of error the following `code`s are supported in addition to the general ones:
| Type | Description |
|------------------|-----------------|
| Type | Description |
|-------------------|-----------------|
| `NoTable` | No route found. |
| `NotImplemented` | This request is not supported |
All other properties might be undefined.
#### Example Response
```json
{
"sources": [
{
"location": [
13.3888,
52.517033
],
"hint": "PAMAgEVJAoAUAAAAIAAAAAcAAAAAAAAArss0Qa7LNEHiVIRA4lSEQAoAAAAQAAAABAAAAAAAAADMAAAAAEzMAKlYIQM8TMwArVghAwEA3wps52D3",
"name": "Friedrichstraße"
},
{
"location": [
13.397631,
52.529432
],
"hint": "WIQBgL6mAoAEAAAABgAAAAAAAAA7AAAAhU6PQHvHj0IAAAAAQbyYQgQAAAAGAAAAAAAAADsAAADMAAAAf27MABiJIQOCbswA_4ghAwAAXwVs52D3",
"name": "Torstraße"
},
{
"location": [
13.428554,
52.523239
],
"hint": "7UcAgP___38fAAAAUQAAACYAAABTAAAAhSQKQrXq5kKRbiZCWJo_Qx8AAABRAAAAJgAAAFMAAADMAAAASufMAOdwIQNL58wA03AhAwMAvxBs52D3",
"name": "Platz der Vereinten Nationen"
}
],
"durations": [
[
0,
192.6,
382.8
],
[
199,
0,
283.9
],
[
344.7,
222.3,
0
]
],
"destinations": [
{
"location": [
13.3888,
52.517033
],
"hint": "PAMAgEVJAoAUAAAAIAAAAAcAAAAAAAAArss0Qa7LNEHiVIRA4lSEQAoAAAAQAAAABAAAAAAAAADMAAAAAEzMAKlYIQM8TMwArVghAwEA3wps52D3",
"name": "Friedrichstraße"
},
{
"location": [
13.397631,
52.529432
],
"hint": "WIQBgL6mAoAEAAAABgAAAAAAAAA7AAAAhU6PQHvHj0IAAAAAQbyYQgQAAAAGAAAAAAAAADsAAADMAAAAf27MABiJIQOCbswA_4ghAwAAXwVs52D3",
"name": "Torstraße"
},
{
"location": [
13.428554,
52.523239
],
"hint": "7UcAgP___38fAAAAUQAAACYAAABTAAAAhSQKQrXq5kKRbiZCWJo_Qx8AAABRAAAAJgAAAFMAAADMAAAASufMAOdwIQNL58wA03AhAwMAvxBs52D3",
"name": "Platz der Vereinten Nationen"
}
],
"code": "Ok",
"distances": [
[
0,
1886.89,
3791.3
],
[
1824,
0,
2838.09
],
[
3275.36,
2361.73,
0
]
]
}
```
### Match service
Map matching matches/snaps given GPS points to the road network in the most plausible way.
+2 -2
View File
@@ -110,8 +110,8 @@ Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refer
### table
Computes duration table for the given locations. Allows for both symmetric and asymmetric
tables. Optionally returns distance table.
Computes duration tables for the given locations. Allows for both symmetric and asymmetric
tables.
**Parameters**
+3 -3
View File
@@ -68,7 +68,7 @@ If you want to prioritize certain streets, increase the rate on these.
## Elements
### api_version
A profile should set `api_version` at the top of your profile. This is done to ensure that older profiles are still supported when the api changes. If `api_version` is not defined, 0 will be assumed. The current api version is 4.
A profile should set `api_version` at the top of your profile. This is done to ensure that older profiles are still supported when the api changes. If `api_version` is not defined, 0 will be assumed. The current api version is 2.
### Library files
The folder [profiles/lib/](../profiles/lib/) contains LUA library files for handling many common processing tasks.
@@ -224,8 +224,8 @@ source_highway_turn_classification | Read | Integer |
source_access_turn_classification | Read | Integer | Classification based on access tag defined by user during setup. (default when not set: 0, allowed classification values are: 0-15))
source_speed | Read | Integer | Speed on this source road in km/h
source_priority_class | Read | Enum | The type of road priority class of the source. Defined in `include/extractor/guidance/road_classification.hpp`
target_restricted | Read | Boolean | Is the target a restricted access road? (See definition in `process_way`)
target_mode | Read | Enum | Travel mode after the turn. Defined in `include/extractor/travel_mode.hpp`
target_restricted | Read | Boolean | Is it from a restricted access road? (See definition in `process_way`)
target_mode | Read | Enum | Travel mode before the turn. Defined in `include/extractor/travel_mode.hpp`
target_is_motorway | Read | Boolean | Is the target road a motorway?
target_is_link | Read | Boolean | Is the target road a link?
target_number_of_lanes | Read | Integer | How many lanes does the target road have? (default when not tagged: 0)
+3 -3
View File
@@ -43,9 +43,9 @@ We may introduce forward-compatible changes: query parameters and response prope
1. Check out the appropriate release branch `x.y`
2. Make sure `CHANGELOG.md` is up to date.
3. Make sure the `package.json` on branch `x.y` has been committed.
4. Make sure all tests are passing (e.g. Travis CI gives you a :green_apple:)
5. Use an annotated tag to mark the release: `git tag vx.y.z -a` Body of the tag description should be the changelog entries. Commit should be one in which the `package.json` version matches the version you want to release.
3. Make sure the `package.json` is up to date.
4. Make sure all tests are passing (e.g. Travis CI gives you a :thumbs_up:)
5. Use an annotated tag to mark the release: `git tag vx.y.z -a` Body of the tag description should be the changelog entries.
6. Use `npm run docs` to generate the API documentation. Copy `build/docs/*` to `https://github.com/Project-OSRM/project-osrm.github.com` in the `docs/vN.N.N/api` directory
7. Push tags and commits: `git push; git push --tags`
8. On https://github.com/Project-OSRM/osrm-backend/releases press `Draft a new release`,
-92
View File
@@ -1,92 +0,0 @@
@routing @bicycle @mode
Feature: Bicycle - Mode flag
Background:
Given the profile "bicycle"
Scenario: Bicycle - We tag ferries with a class
Given the node map
"""
a b
c d
"""
And the ways
| nodes | highway | route |
| ab | primary | |
| bc | | ferry |
| cd | primary | |
When I route I should get
| from | to | route | turns | classes |
| a | d | ab,bc,cd,cd | depart,notification right,notification left,arrive | [()],[(ferry)],[()],[()] |
| d | a | cd,bc,ab,ab | depart,notification right,notification left,arrive | [()],[(ferry)],[()],[()] |
| c | a | bc,ab,ab | depart,notification left,arrive | [(ferry)],[()],[()] |
| d | b | cd,bc,bc | depart,notification right,arrive | [()],[(ferry)],[()] |
| a | c | ab,bc,bc | depart,notification right,arrive | [()],[(ferry)],[()] |
| b | d | bc,cd,cd | depart,notification left,arrive | [(ferry)],[()],[()] |
Scenario: Bicycle - We tag tunnel with a class
Background:
Given a grid size of 200 meters
Given the node map
"""
a b
c d
"""
And the ways
| nodes | tunnel |
| ab | no |
| bc | yes |
| cd | |
When I route I should get
| from | to | route | turns | classes |
| a | d | ab,bc,cd,cd | depart,new name right,new name left,arrive | [()],[(tunnel)],[()],[()] |
Scenario: Bicycle - We tag classes without intersections
Background:
Given a grid size of 200 meters
Given the node map
"""
a b c d
"""
And the ways
| nodes | name | tunnel |
| ab | road | |
| bc | road | yes |
| cd | road | |
When I route I should get
| from | to | route | turns | classes |
| a | d | road,road | depart,arrive | [(),(tunnel),()],[()] |
Scenario: Bicycle - From roundabout on ferry
Given the node map
"""
c
/ \
a---b d---f--h
\ /
e
|
g
"""
And the ways
| nodes | oneway | highway | junction | route |
| ab | yes | service | | |
| cb | yes | service | roundabout | |
| dc | yes | service | roundabout | |
| be | yes | service | roundabout | |
| ed | yes | service | roundabout | |
| eg | yes | service | | |
| df | | | | ferry |
| fh | yes | service | | |
When I route I should get
| from | to | route | turns | classes |
| a | h | ab,df,df,fh,fh | depart,roundabout-exit-2,exit roundabout slight right,notification straight,arrive | [()],[(),()],[(ferry)],[()],[()] |
-55
View File
@@ -1,55 +0,0 @@
@routing @bicycle @exclude
Feature: Bicycle - Exclude flags
Background:
Given the profile file "bicycle" initialized with
"""
profile.excludable = Sequence { Set { 'ferry' } }
"""
Given the node map
"""
a....b~~~~~c...f
: :
d.....e
"""
And the ways
| nodes | highway | route | duration | # |
| ab | service | | | always drivable |
| bc | | ferry | 00:00:01 | not drivable for exclude=ferry, but fast. |
| bd | service | | | always drivable |
| de | service | | | always drivable |
| ec | service | | | always drivable |
| cf | service | | | always drivable |
Scenario: Bicycle - exclude nothing
When I route I should get
| from | to | route |
| a | f | ab,bc,cf,cf |
When I match I should get
| trace | matchings | duration |
| abcf | abcf | 109 |
When I request a travel time matrix I should get
| | a | f |
| a | 0 | 109 |
| f | 109 | 0 |
Scenario: Bicycle - exclude ferry
Given the query options
| exclude | ferry |
When I route I should get
| from | to | route |
| a | f | ab,bd,de,ec,cf,cf |
When I match I should get
| trace | matchings | duration |
| abcf | abcf | 301.2 |
When I request a travel time matrix I should get
| | a | f |
| a | 0 | 301 +- 1 |
| f | 301.2 +- 1 | 0 |
+22 -22
View File
@@ -11,11 +11,11 @@ Feature: Bicycle - Adds penalties to unsafe roads
Then routability should be
| highway | cycleway | forw | backw | forw_rate | backw_rate |
| motorway | | | | | |
| primary | | 15 km/h | 15 km/h | 2.1 | 2.1 |
| secondary | | 15 km/h | 15 km/h | 2.7 | 2.7 |
| primary | | 15 km/h | 15 km/h | 2.9 | 2.9 |
| secondary | | 15 km/h | 15 km/h | 3.1 | 3.1 |
| tertiary | | 15 km/h | 15 km/h | 3.3 | 3.3 |
| primary_link | | 15 km/h | 15 km/h | 2.1 | 2.1 |
| secondary_link | | 15 km/h | 15 km/h | 2.7 | 2.7 |
| primary_link | | 15 km/h | 15 km/h | 2.9 | 2.9 |
| secondary_link | | 15 km/h | 15 km/h | 3.1 | 3.1 |
| tertiary_link | | 15 km/h | 15 km/h | 3.3 | 3.3 |
| residential | | 15 km/h | 15 km/h | 4.2 | 4.2 |
| cycleway | | 15 km/h | 15 km/h | 4.2 | 4.2 |
@@ -51,49 +51,49 @@ Feature: Bicycle - Adds penalties to unsafe roads
Then routability should be
| highway | cycleway:right | cycleway:left | forw | backw | forw_rate | backw_rate |
| motorway | track | | 15 km/h | | 4.2 | |
| primary | track | | 15 km/h | 15 km/h | 4.2 | 2.1 |
| secondary | track | | 15 km/h | 15 km/h | 4.2 | 2.7 |
| primary | track | | 15 km/h | 15 km/h | 4.2 | 2.9 |
| secondary | track | | 15 km/h | 15 km/h | 4.2 | 3.1 |
| tertiary | track | | 15 km/h | 15 km/h | 4.2 | 3.3 |
| primary_link | track | | 15 km/h | 15 km/h | 4.2 | 2.1 |
| secondary_link | track | | 15 km/h | 15 km/h | 4.2 | 2.7 |
| primary_link | track | | 15 km/h | 15 km/h | 4.2 | 2.9 |
| secondary_link | track | | 15 km/h | 15 km/h | 4.2 | 3.1 |
| tertiary_link | track | | 15 km/h | 15 km/h | 4.2 | 3.3 |
| residential | track | | 15 km/h | 15 km/h | 4.2 | 4.2 |
| cycleway | track | | 15 km/h | 15 km/h | 4.2 | 4.2 |
| footway | track | | 15 km/h | 4 km/h +-1 | 4.2 | 1.1 |
| motorway | | track | 15 km/h | | 4.2 | |
| primary | | track | 15 km/h | 15 km/h | 2.1 | 4.2 |
| secondary | | track | 15 km/h | 15 km/h | 2.7 | 4.2 |
| primary | | track | 15 km/h | 15 km/h | 2.9 | 4.2 |
| secondary | | track | 15 km/h | 15 km/h | 3.1 | 4.2 |
| tertiary | | track | 15 km/h | 15 km/h | 3.3 | 4.2 |
| primary_link | | track | 15 km/h | 15 km/h | 2.1 | 4.2 |
| secondary_link | | track | 15 km/h | 15 km/h | 2.7 | 4.2 |
| primary_link | | track | 15 km/h | 15 km/h | 2.9 | 4.2 |
| secondary_link | | track | 15 km/h | 15 km/h | 3.1 | 4.2 |
| tertiary_link | | track | 15 km/h | 15 km/h | 3.3 | 4.2 |
| residential | | track | 15 km/h | 15 km/h | 4.2 | 4.2 |
| cycleway | | track | 15 km/h | 15 km/h | 4.2 | 4.2 |
| footway | | track | 4 km/h +-1 | 15 km/h | 1.1 | 4.2 |
| motorway | lane | | 15 km/h | | 4.2 | |
| primary | lane | | 15 km/h | 15 km/h | 4.2 | 2.1 |
| secondary | lane | | 15 km/h | 15 km/h | 4.2 | 2.7 |
| primary | lane | | 15 km/h | 15 km/h | 4.2 | 2.9 |
| secondary | lane | | 15 km/h | 15 km/h | 4.2 | 3.1 |
| tertiary | lane | | 15 km/h | 15 km/h | 4.2 | 3.3 |
| primary_link | lane | | 15 km/h | 15 km/h | 4.2 | 2.1 |
| secondary_link | lane | | 15 km/h | 15 km/h | 4.2 | 2.7 |
| primary_link | lane | | 15 km/h | 15 km/h | 4.2 | 2.9 |
| secondary_link | lane | | 15 km/h | 15 km/h | 4.2 | 3.1 |
| tertiary_link | lane | | 15 km/h | 15 km/h | 4.2 | 3.3 |
| residential | lane | | 15 km/h +-1 | 15 km/h +-1 | 4.2 | 4.2 |
| cycleway | lane | | 15 km/h | 15 km/h | 4.2 | 4.2 |
| footway | lane | | 15 km/h | 4 km/h +-1 | 4.2 | 1.1 |
| motorway | | lane | 15 km/h | | 4.2 | |
| primary | | lane | 15 km/h | 15 km/h | 2.1 | 4.2 |
| secondary | | lane | 15 km/h +-1 | 15 km/h +-1 | 2.7 | 4.2 |
| primary | | lane | 15 km/h | 15 km/h | 2.9 | 4.2 |
| secondary | | lane | 15 km/h +-1 | 15 km/h +-1 | 3.1 | 4.2 |
| tertiary | | lane | 15 km/h | 15 km/h | 3.3 | 4.2 |
| primary_link | | lane | 15 km/h | 15 km/h | 2.1 | 4.2 |
| secondary_link | | lane | 15 km/h | 15 km/h | 2.7 | 4.2 |
| primary_link | | lane | 15 km/h | 15 km/h | 2.9 | 4.2 |
| secondary_link | | lane | 15 km/h | 15 km/h | 3.1 | 4.2 |
| tertiary_link | | lane | 15 km/h | 15 km/h | 3.3 | 4.2 |
| residential | | lane | 15 km/h | 15 km/h | 4.2 | 4.2 |
| cycleway | | lane | 15 km/h | 15 km/h | 4.2 | 4.2 |
| footway | | lane | 4 km/h +-1 | 15 km/h | 1.1 | 4.2 |
| motorway | shared_lane | | 15 km/h | | 4.2 | |
| primary | shared_lane | | 15 km/h | 15 km/h | 4.2 | 2.1 |
| primary | shared_lane | | 15 km/h | 15 km/h | 4.2 | 2.9 |
| motorway | | shared_lane | 15 km/h | | 4.2 | |
| primary | | shared_lane | 15 km/h | 15 km/h | 2.1 | 4.2 |
| primary | | shared_lane | 15 km/h | 15 km/h | 2.9 | 4.2 |
Scenario: Bike - Don't apply penalties for all kind of cycleways
@@ -1209,33 +1209,3 @@ Feature: Simple Turns
| a | c | knob,knob | depart,arrive |
| d | e | soph,soph | depart,arrive |
| d | a | soph,knob,knob | depart,turn left,arrive |
# https://www.openstreetmap.org/node/30797565
Scenario: No turn instruction when turning from unnamed onto unnamed
Given the node map
"""
a
|
|
|
|
b----------------c
|
|
|
|
|
|
d
"""
And the ways
| nodes | highway | name | ref |
| ab | trunk_link | | |
| db | secondary | | L 460 |
| bc | secondary | | |
When I route I should get
| from | to | route | turns |
| d | c | ,, | depart,turn right,arrive |
+60 -74
View File
@@ -1,88 +1,74 @@
var util = require('util');
module.exports = function () {
const durationsRegex = new RegExp(/^I request a travel time matrix I should get$/);
const distancesRegex = new RegExp(/^I request a travel distance matrix I should get$/);
this.When(/^I request a travel time matrix I should get$/, (table, callback) => {
var NO_ROUTE = 2147483647; // MAX_INT
const DURATIONS_NO_ROUTE = 2147483647; // MAX_INT
const DISTANCES_NO_ROUTE = 3.40282e+38; // MAX_FLOAT
var tableRows = table.raw();
this.When(durationsRegex, function(table, callback) {tableParse.call(this, table, DURATIONS_NO_ROUTE, 'durations', callback);}.bind(this));
this.When(distancesRegex, function(table, callback) {tableParse.call(this, table, DISTANCES_NO_ROUTE, 'distances', callback);}.bind(this));
};
if (tableRows[0][0] !== '') throw new Error('*** Top-left cell of matrix table must be empty');
const durationsParse = function(v) { return isNaN(parseInt(v)); };
const distancesParse = function(v) { return isNaN(parseFloat(v)); };
var waypoints = [],
columnHeaders = tableRows[0].slice(1),
rowHeaders = tableRows.map((h) => h[0]).slice(1),
symmetric = columnHeaders.length == rowHeaders.length && columnHeaders.every((ele, i) => ele === rowHeaders[i]);
function tableParse(table, noRoute, annotation, callback) {
const parse = annotation == 'distances' ? distancesParse : durationsParse;
const params = this.queryParams;
params.annotations = annotation == 'distances' ? 'distance' : 'duration';
var tableRows = table.raw();
if (tableRows[0][0] !== '') throw new Error('*** Top-left cell of matrix table must be empty');
var waypoints = [],
columnHeaders = tableRows[0].slice(1),
rowHeaders = tableRows.map((h) => h[0]).slice(1),
symmetric = columnHeaders.length == rowHeaders.length && columnHeaders.every((ele, i) => ele === rowHeaders[i]);
if (symmetric) {
columnHeaders.forEach((nodeName) => {
var node = this.findNodeByName(nodeName);
if (!node) throw new Error(util.format('*** unknown node "%s"', nodeName));
waypoints.push({ coord: node, type: 'loc' });
});
} else {
columnHeaders.forEach((nodeName) => {
var node = this.findNodeByName(nodeName);
if (!node) throw new Error(util.format('*** unknown node "%s"', nodeName));
waypoints.push({ coord: node, type: 'dst' });
});
rowHeaders.forEach((nodeName) => {
var node = this.findNodeByName(nodeName);
if (!node) throw new Error(util.format('*** unknown node "%s"', nodeName));
waypoints.push({ coord: node, type: 'src' });
});
}
var actual = [];
actual.push(table.headers);
this.reprocessAndLoadData((e) => {
if (e) return callback(e);
// compute matrix
this.requestTable(waypoints, params, (err, response) => {
if (err) return callback(err);
if (!response.body.length) return callback(new Error('Invalid response body'));
var json = JSON.parse(response.body);
var result = json[annotation].map(row => {
var hashes = {};
row.forEach((v, i) => { hashes[tableRows[0][i+1]] = parse(v) ? '' : v; });
return hashes;
if (symmetric) {
columnHeaders.forEach((nodeName) => {
var node = this.findNodeByName(nodeName);
if (!node) throw new Error(util.format('*** unknown node "%s"', nodeName));
waypoints.push({ coord: node, type: 'loc' });
});
} else {
columnHeaders.forEach((nodeName) => {
var node = this.findNodeByName(nodeName);
if (!node) throw new Error(util.format('*** unknown node "%s"', nodeName));
waypoints.push({ coord: node, type: 'dst' });
});
rowHeaders.forEach((nodeName) => {
var node = this.findNodeByName(nodeName);
if (!node) throw new Error(util.format('*** unknown node "%s"', nodeName));
waypoints.push({ coord: node, type: 'src' });
});
}
var testRow = (row, ri, cb) => {
for (var k in result[ri]) {
if (this.FuzzyMatch.match(result[ri][k], row[k])) {
result[ri][k] = row[k];
} else if (row[k] === '' && result[ri][k] === noRoute) {
result[ri][k] = '';
} else {
result[ri][k] = result[ri][k].toString();
var actual = [];
actual.push(table.headers);
this.reprocessAndLoadData((e) => {
if (e) return callback(e);
// compute matrix
var params = this.queryParams;
this.requestTable(waypoints, params, (err, response) => {
if (err) return callback(err);
if (!response.body.length) return callback(new Error('Invalid response body'));
var json = JSON.parse(response.body);
var result = json['durations'].map(row => {
var hashes = {};
row.forEach((v, i) => { hashes[tableRows[0][i+1]] = isNaN(parseInt(v)) ? '' : v; });
return hashes;
});
var testRow = (row, ri, cb) => {
for (var k in result[ri]) {
if (this.FuzzyMatch.match(result[ri][k], row[k])) {
result[ri][k] = row[k];
} else if (row[k] === '' && result[ri][k] === NO_ROUTE) {
result[ri][k] = '';
} else {
result[ri][k] = result[ri][k].toString();
}
}
}
result[ri][''] = row[''];
cb(null, result[ri]);
};
result[ri][''] = row[''];
cb(null, result[ri]);
};
this.processRowsAndDiff(table, testRow, callback);
this.processRowsAndDiff(table, testRow, callback);
});
});
});
}
};
+1 -7
View File
@@ -21,8 +21,7 @@ module.exports = {
matchRe = want.match(/^\/(.*)\/$/),
// we use this for matching before/after bearing
matchBearingListAbs = want.match(/^((\d+)->(\d+))(,(\d+)->(\d+))*\s+\+\-(.+)$/),
matchIntersectionListAbs = want.match(/^(((((true|false):\d+)\s{0,1})+,{0,1})+;{0,1})+\s+\+\-(.+)$/),
matchRangeNumbers = want.match(/\d+\+\-\d+/);
matchIntersectionListAbs = want.match(/^(((((true|false):\d+)\s{0,1})+,{0,1})+;{0,1})+\s+\+\-(.+)$/);
function inRange(margin, got, want) {
var fromR = parseFloat(want) - margin,
@@ -106,11 +105,6 @@ module.exports = {
return inRange(margin, got, matchAbs[1]);
} else if (matchRe) { // regex: /a,b,.*/
return got.match(matchRe[1]);
} else if (matchRangeNumbers) {
let real_want_and_margin = want.split('+-'),
margin = parseFloat(real_want_and_margin[1].trim()),
real_want = parseFloat(real_want_and_margin[0].trim());
return inRange(margin, got, real_want);
} else {
return false;
}
+1 -1
View File
@@ -51,7 +51,7 @@ module.exports = function () {
.defer(rimraf, this.scenarioLogFile)
.awaitAll(callback);
// uncomment to get path to logfile
// console.log(' Writing logging output to ' + this.scenarioLogFile);
// console.log(" Writing logging output to " + this.scenarioLogFile)
});
this.After((scenario, callback) => {
-19
View File
@@ -226,22 +226,3 @@ Feature: Distance calculation
| x | v | xv,xv | 424m +-1 |
| x | w | xw,xw | 360m +-1 |
| x | y | xy,xy | 316m +-1 |
# Check rounding errors
Scenario: Distances Long distances
Given a grid size of 1000 meters
Given the node map
"""
a b c d
"""
And the ways
| nodes |
| abcd |
When I route I should get
| from | to | distance |
| a | b | 1000m +-3 |
| a | c | 2000m +-3 |
| a | d | 3000m +-3 |
+177 -335
View File
@@ -1,12 +1,14 @@
@matrix @testbot
Feature: Basic Distance Matrix
# note that results of travel distance are in metres
# note that results are travel time, specified in 1/10th of seconds
# since testbot uses a default speed of 100m/10s, the result matches
# the number of meters as long as the way type is the default 'primary'
Background:
Given the profile "testbot"
And the partition extra arguments "--small-component-size 1 --max-cell-sizes 2,4,8,16"
Scenario: Testbot - Travel distance matrix of minimal network
Scenario: Testbot - Travel time matrix of minimal network
Given the node map
"""
a b
@@ -16,99 +18,12 @@ Feature: Basic Distance Matrix
| nodes |
| ab |
When I request a travel distance matrix I should get
| | a | b |
| a | 0 | 100+-1 |
| b | 100+-1 | 0 |
When I request a travel time matrix I should get
| | a | b |
| a | 0 | 10 |
| b | 10 | 0 |
Scenario: Testbot - Travel distance matrix of minimal network with toll exclude
Given the query options
| exclude | toll |
Given the node map
"""
a b
c d
"""
And the ways
| nodes | highway | toll | # |
| ab | motorway | | not drivable for exclude=motorway |
| cd | primary | | always drivable |
| ac | primary | yes | not drivable for exclude=toll and exclude=motorway,toll |
| bd | motorway | yes | not drivable for exclude=toll and exclude=motorway,toll |
When I request a travel distance matrix I should get
| | a | b | c | d |
| a | 0 | 100+-1 | | |
| b | 100+-1 | 0 | | |
| c | | | 0 | 100+-1 |
| d | | | 100+-1 | 0 |
Scenario: Testbot - Travel distance matrix of minimal network with motorway exclude
Given the query options
| exclude | motorway |
Given the node map
"""
a b
c d
"""
And the ways
| nodes | highway | # |
| ab | motorway | not drivable for exclude=motorway |
| cd | residential | |
| ac | residential | |
| bd | residential | |
When I request a travel distance matrix I should get
| | a | b | c | d |
| a | 0 | 300+-2 | 100+-2 | 200+-2 |
Scenario: Testbot - Travel distance matrix of minimal network disconnected motorway exclude
Given the query options
| exclude | motorway |
And the extract extra arguments "--small-component-size 4"
Given the node map
"""
ab efgh
cd
"""
And the ways
| nodes | highway | # |
| be | motorway | not drivable for exclude=motorway |
| abcd | residential | |
| efgh | residential | |
When I request a travel distance matrix I should get
| | a | b | e |
| a | 0 | 50+-1 | |
Scenario: Testbot - Travel distance matrix of minimal network with motorway and toll excludes
Given the query options
| exclude | motorway,toll |
Given the node map
"""
a b e f
c d g h
"""
And the ways
| nodes | highway | toll | # |
| be | motorway | | not drivable for exclude=motorway |
| dg | primary | yes | not drivable for exclude=toll |
| abcd | residential | | |
| efgh | residential | | |
When I request a travel distance matrix I should get
| | a | b | e | g |
| a | 0 | 100+-1 | | |
Scenario: Testbot - Travel distance matrix with different way speeds
Scenario: Testbot - Travel time matrix with different way speeds
Given the node map
"""
a b c d
@@ -120,25 +35,40 @@ Feature: Basic Distance Matrix
| bc | secondary |
| cd | tertiary |
When I request a travel distance matrix I should get
| | a | b | c | d |
| a | 0 | 100+-1 | 200+-1 | 300+-1 |
| b | 100+-1 | 0 | 100+-1 | 200+-1 |
| c | 200+-1 | 100+-1 | 0 | 100+-1 |
| d | 300+-1 | 200+-1 | 100+-1 | 0 |
When I request a travel time matrix I should get
| | a | b | c | d |
| a | 0 | 10 | 30 | 60 |
| b | 10 | 0 | 20 | 50 |
| c | 30 | 20 | 0 | 30 |
| d | 60 | 50 | 30 | 0 |
When I request a travel distance matrix I should get
| | a | b | c | d |
| a | 0 | 100+-1 | 200+-1 | 300+-1 |
When I request a travel time matrix I should get
| | a | b | c | d |
| a | 0 | 10 | 30 | 60 |
When I request a travel distance matrix I should get
| | a |
| a | 0 |
| b | 100+-1 |
| c | 200+-1 |
| d | 300+-1 |
When I request a travel time matrix I should get
| | a |
| a | 0 |
| b | 10 |
| c | 30 |
| d | 60 |
Scenario: Testbot - Travel distance matrix of small grid
Scenario: Testbot - Travel time matrix with fuzzy match
Given the node map
"""
a b
"""
And the ways
| nodes |
| ab |
When I request a travel time matrix I should get
| | a | b |
| a | 0 | 10 |
| b | 10 | 0 |
Scenario: Testbot - Travel time matrix of small grid
Given the node map
"""
a b c
@@ -153,14 +83,14 @@ Feature: Basic Distance Matrix
| be |
| cf |
When I request a travel distance matrix I should get
| | a | b | e | f |
| a | 0 | 100+-1 | 200+-1 | 300+-1 |
| b | 100+-1 | 0 | 100+-1 | 200+-1 |
| e | 200+-1 | 100+-1 | 0 | 100+-1 |
| f | 300+-1 | 200+-1 | 100+-1 | 0 |
When I request a travel time matrix I should get
| | a | b | e | f |
| a | 0 | 10 | 20 | 30 |
| b | 10 | 0 | 10 | 20 |
| e | 20 | 10 | 0 | 10 |
| f | 30 | 20 | 10 | 0 |
Scenario: Testbot - Travel distance matrix of network with unroutable parts
Scenario: Testbot - Travel time matrix of network with unroutable parts
Given the node map
"""
a b
@@ -170,12 +100,12 @@ Feature: Basic Distance Matrix
| nodes | oneway |
| ab | yes |
When I request a travel distance matrix I should get
| | a | b |
| a | 0 | 100+-1 |
| b | | 0 |
When I request a travel time matrix I should get
| | a | b |
| a | 0 | 10 |
| b | | 0 |
Scenario: Testbot - Travel distance matrix of network with oneways
Scenario: Testbot - Travel time matrix of network with oneways
Given the node map
"""
x a b y
@@ -188,14 +118,14 @@ Feature: Basic Distance Matrix
| xa | |
| by | |
When I request a travel distance matrix I should get
| | x | y | d | e |
| x | 0 | 300+-2 | 400+-2 | 300+-2 |
| y | 500+-2 | 0 | 300+-2 | 200+-2 |
| d | 200+-2 | 300+-2 | 0 | 300+-2 |
| e | 300+-2 | 400+-2 | 100+-2 | 0 |
When I request a travel time matrix I should get
| | x | y | d | e |
| x | 0 | 30 | 40 | 30 |
| y | 50 | 0 | 30 | 20 |
| d | 20 | 30 | 0 | 30 |
| e | 30 | 40 | 10 | 0 |
Scenario: Testbot - Rectangular travel distance matrix
Scenario: Testbot - Rectangular travel time matrix
Given the node map
"""
a b c
@@ -210,57 +140,51 @@ Feature: Basic Distance Matrix
| be |
| cf |
When I route I should get
| from | to | distance |
| e | a | 200m +- 1 |
| e | b | 100m +- 1 |
| f | a | 300m +- 1 |
| f | b | 200m +- 1 |
When I request a travel time matrix I should get
| | a | b | e | f |
| a | 0 | 10 | 20 | 30 |
When I request a travel distance matrix I should get
| | a | b | e | f |
| a | 0 | 100+-1 | 200+-1 | 300+-1 |
When I request a travel time matrix I should get
| | a |
| a | 0 |
| b | 10 |
| e | 20 |
| f | 30 |
When I request a travel distance matrix I should get
| | a |
| a | 0 |
| b | 100+-1 |
| e | 200+-1 |
| f | 300+-1 |
When I request a travel time matrix I should get
| | a | b | e | f |
| a | 0 | 10 | 20 | 30 |
| b | 10 | 0 | 10 | 20 |
When I request a travel distance matrix I should get
| | a | b | e | f |
| a | 0 | 100+-1 | 200+-1 | 300+-1 |
| b | 100+-1 | 0 | 100+-1 | 200+-1 |
When I request a travel time matrix I should get
| | a | b |
| a | 0 | 10 |
| b | 10 | 0 |
| e | 20 | 10 |
| f | 30 | 20 |
When I request a travel distance matrix I should get
| | a | b |
| a | 0 | 100+-1 |
| b | 100+-1 | 0 |
| e | 200+-1 | 100+-1 |
| f | 300+-1 | 200+-1 |
When I request a travel time matrix I should get
| | a | b | e | f |
| a | 0 | 10 | 20 | 30 |
| b | 10 | 0 | 10 | 20 |
| e | 20 | 10 | 0 | 10 |
When I request a travel distance matrix I should get
| | a | b | e | f |
| a | 0 | 100+-1 | 200+-1 | 300+-1 |
| b | 100+-1 | 0 | 100+-1 | 200+-1 |
| e | 200+-1 | 100+-1 | 0 | 100+-1 |
When I request a travel time matrix I should get
| | a | b | e |
| a | 0 | 10 | 20 |
| b | 10 | 0 | 10 |
| e | 20 | 10 | 0 |
| f | 30 | 20 | 10 |
When I request a travel distance matrix I should get
| | a | b | e |
| a | 0 | 100+-1 | 200+-1 |
| b | 100+-1 | 0 | 100+-1 |
| e | 200+-1 | 100+-1 | 0 |
| f | 300+-1 | 200+-1 | 100+-1 |
When I request a travel time matrix I should get
| | a | b | e | f |
| a | 0 | 10 | 20 | 30 |
| b | 10 | 0 | 10 | 20 |
| e | 20 | 10 | 0 | 10 |
| f | 30 | 20 | 10 | 0 |
When I request a travel distance matrix I should get
| | a | b | e | f |
| a | 0 | 100+-1 | 200+-1 | 300+-1 |
| b | 100+-1 | 0 | 100+-1 | 200+-1 |
| e | 200+-1 | 100+-1 | 0 | 100+-1 |
| f | 300+-1 | 200+-1 | 100+-1 | 0 |
Scenario: Testbot - Travel distance 3x2 matrix
Scenario: Testbot - Travel time 3x2 matrix
Given the node map
"""
a b c
@@ -275,11 +199,10 @@ Feature: Basic Distance Matrix
| be |
| cf |
When I request a travel distance matrix I should get
| | b | e | f |
| a | 100+-1 | 200+-1 | 300+-1 |
| b | 0 | 100+-1 | 200+-1 |
When I request a travel time matrix I should get
| | b | e | f |
| a | 10 | 20 | 30 |
| b | 0 | 10 | 20 |
Scenario: Testbot - All coordinates are from same small component
Given a grid size of 300 meters
@@ -298,10 +221,10 @@ Feature: Basic Distance Matrix
| da |
| fg |
When I request a travel distance matrix I should get
| | f | g |
| f | 0 | 300+-2 |
| g | 300+-2 | 0 |
When I request a travel time matrix I should get
| | f | g |
| f | 0 | 30 |
| g | 30 | 0 |
Scenario: Testbot - Coordinates are from different small component and snap to big CC
Given a grid size of 300 meters
@@ -321,25 +244,14 @@ Feature: Basic Distance Matrix
| fg |
| hi |
When I route I should get
| from | to | distance |
| f | g | 300m |
| f | i | 300m |
| g | f | 300m |
| g | h | 300m |
| h | g | 300m |
| h | i | 300m |
| i | f | 300m |
| i | h | 300m |
When I request a travel time matrix I should get
| | f | g | h | i |
| f | 0 | 30 | 0 | 30 |
| g | 30 | 0 | 30 | 0 |
| h | 0 | 30 | 0 | 30 |
| i | 30 | 0 | 30 | 0 |
When I request a travel distance matrix I should get
| | f | g | h | i |
| f | 0 | 300+-2 | 0 | 300+-2 |
| g | 300+-2 | 0 | 300+-2 | 0 |
| h | 0 | 300+-2 | 0 | 300+-2 |
| i | 300+-2 | 0 | 300+-2 | 0 |
Scenario: Testbot - Travel distance matrix with loops
Scenario: Testbot - Travel time matrix with loops
Given the node map
"""
a 1 2 b
@@ -353,15 +265,14 @@ Feature: Basic Distance Matrix
| cd | yes |
| da | yes |
When I request a travel distance matrix I should get
| | 1 | 2 | 3 | 4 |
| 1 | 0 | 100+-1 | 400+-1 | 500+-1 |
| 2 | 700+-1 | 0 | 300+-1 | 400+-1 |
| 3 | 400+-1 | 500+-1 | 0 | 100+-1 |
| 4 | 300+-1 | 400+-1 | 700+-1 | 0 |
When I request a travel time matrix I should get
| | 1 | 2 | 3 | 4 |
| 1 | 0 | 10 +-1 | 40 +-1 | 50 +-1 |
| 2 | 70 +-1 | 0 | 30 +-1 | 40 +-1 |
| 3 | 40 +-1 | 50 +-1 | 0 | 10 +-1 |
| 4 | 30 +-1 | 40 +-1 | 70 +-1 | 0 |
Scenario: Testbot - Travel distance matrix based on segment durations
Scenario: Testbot - Travel time matrix based on segment durations
Given the profile file
"""
local functions = require('testbot')
@@ -390,19 +301,20 @@ Feature: Basic Distance Matrix
"""
And the ways
| nodes |
| abcd |
| ce |
| nodes |
| abcd |
| ce |
When I request a travel distance matrix I should get
| | a | b | c | d | e |
| a | 0 | 100+-2 | 200+-2 | 300+-2 | 400+-2 |
| b | 100+-2 | 0 | 100+-2 | 200+-2 | 300+-2 |
| c | 200+-2 | 100+-2 | 0 | 100+-2 | 200+-2 |
| d | 300+-2 | 200+-2 | 100+-2 | 0 | 300+-2 |
| e | 400+-2 | 300+-2 | 200+-2 | 300+-2 | 0 |
When I request a travel time matrix I should get
| | a | b | c | d | e |
| a | 0 | 11 | 22 | 33 | 33 |
| b | 11 | 0 | 11 | 22 | 22 |
| c | 22 | 11 | 0 | 11 | 11 |
| d | 33 | 22 | 11 | 0 | 22 |
| e | 33 | 22 | 11 | 22 | 0 |
Scenario: Testbot - Travel distance matrix for alternative loop paths
Scenario: Testbot - Travel time matrix for alternative loop paths
Given the profile file
"""
local functions = require('testbot')
@@ -438,132 +350,62 @@ Feature: Basic Distance Matrix
| dc | yes |
| ca | yes |
When I request a travel distance matrix I should get
| | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| 1 | 0 | 1100+-5 | 300+-5 | 200+-5 | 600+-5 | 500+-5 | 900+-5 | 800+-5 |
| 2 | 100+-5 | 0 | 400+-5 | 300+-5 | 700+-5 | 600+-5 | 1000+-5 | 900+-5 |
| 3 | 900+-5 | 800+-5 | 0 | 1100+-5 | 300+-5 | 200+-5 | 600+-5 | 500+-5 |
| 4 | 1000+-5 | 900+-5 | 100+-5 | 0 | 400+-5 | 300+-5 | 700+-5 | 600+-5 |
| 5 | 600+-5 | 500+-5 | 900+-5 | 800+-5 | 0 | 1100+-5 | 300+-5 | 200+-5 |
| 6 | 700+-5 | 600+-5 | 1000+-5 | 900+-5 | 100+-5 | 0 | 400+-5 | 300+-5 |
| 7 | 300+-5 | 200+-5 | 600+-5 | 500+-5 | 900+-5 | 800+-5 | 0 | 1100+-5 |
| 8 | 400+-5 | 300+-5 | 700+-5 | 600+-5 | 1000+-5 | 900+-5 | 100+-5 | 0 |
When I request a travel time matrix I should get
| | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| 1 | 0 | 11 | 3 | 2 | 6 | 5 | 8.9 | 7.9 |
| 2 | 1 | 0 | 4 | 3 | 7 | 6 | 9.9 | 8.9 |
| 3 | 9 | 8 | 0 | 11 | 3 | 2 | 5.9 | 4.9 |
| 4 | 10 | 9 | 1 | 0 | 4 | 3 | 6.9 | 5.9 |
| 5 | 6 | 5 | 9 | 8 | 0 | 11 | 2.9 | 1.9 |
| 6 | 7 | 6 | 10 | 9 | 1 | 0 | 3.9 | 2.9 |
| 7 | 3.1 | 2.1 | 6.1 | 5.1 | 9.1 | 8.1 | 0 | 11 |
| 8 | 4.1 | 3.1 | 7.1 | 6.1 | 10.1 | 9.1 | 1 | 0 |
When I request a travel distance matrix I should get
| | 1 |
| 1 | 0 |
| 2 | 100+-5 |
| 3 | 900+-5 |
| 4 | 1000+-5 |
| 5 | 600+-5 |
| 6 | 700+-5 |
| 7 | 300+-5 |
| 8 | 400+-5 |
Scenario: Testbot - Travel distance matrix with ties
Given the node map
Scenario: Testbot - Travel time matrix with ties
Given the profile file
"""
local functions = require('testbot')
functions.process_segment = function(profile, segment)
segment.weight = 1
segment.duration = 1
end
functions.process_turn = function(profile, turn)
if turn.angle >= 0 then
turn.duration = 16
else
turn.duration = 4
end
turn.weight = 0
end
return functions
"""
And the node map
"""
a b
a b
c d
"""
And the ways
| nodes |
| ab |
| ac |
| bd |
| dc |
| nodes |
| ab |
| ac |
| bd |
| dc |
When I route I should get
| from | to | route | distance | time | weight |
| a | c | ac,ac | 200m | 20s | 20 |
| from | to | route | distance | time | weight |
| a | c | ac,ac | 200m | 5s | 5 |
When I route I should get
| from | to | route | distance |
| a | b | ab,ab | 450m |
| a | c | ac,ac | 200m |
| a | d | ac,dc,dc | 500m +- 1 |
When I request a travel time matrix I should get
| | a | b | c | d |
| a | 0 | 1 | 5 | 10 |
When I request a travel distance matrix I should get
| | a | b | c | d |
| a | 0 | 450+-2 | 200+-2 | 500+-2 |
When I request a travel distance matrix I should get
| | a |
| a | 0 |
| b | 450+-2 |
| c | 200+-2 |
| d | 500+-2 |
When I request a travel distance matrix I should get
| | a | c |
| a | 0 | 200+-2 |
| c | 200+-2 | 0 |
# Check rounding errors
Scenario: Testbot - Long distances in tables
Given a grid size of 1000 meters
Given the node map
"""
a b c d
"""
And the ways
| nodes |
| abcd |
When I request a travel distance matrix I should get
| | a | b | c | d |
| a | 0 | 1000+-3 | 2000+-3 | 3000+-3 |
Scenario: Testbot - OneToMany vs ManyToOne
Given the node map
"""
a b
c
"""
And the ways
| nodes | oneway |
| ab | yes |
| ac | |
| bc | |
When I request a travel distance matrix I should get
| | a | b |
| b | 240.4 | 0 |
When I request a travel distance matrix I should get
| | a |
| a | 0 |
| b | 240.4 |
Scenario: Testbot - Varying distances between nodes
Given the node map
"""
a b c d
e
f
"""
And the ways
| nodes | oneway |
| feabcd | yes |
| ec | |
| fd | |
When I request a travel distance matrix I should get
| | a | b | c | d | e | f |
| a | 0 | 100+-1 | 300+-1 | 650+-1 | 1930+-1 | 1533+-1 |
| b | 760+-1 | 0 | 200+-1 | 550+-1 | 1830+-1 | 1433+-1 |
| c | 560+-2 | 660+-2 | 0 | 350+-1 | 1630+-1 | 1233+-1 |
| d | 1480+-2 | 1580+-1 | 1780+-1 | 0 | 1280+-1 | 883+-1 |
| e | 200+-2 | 300+-2 | 500+-1 | 710+-1 | 0 | 1593+-1 |
| f | 597+-1 | 696+-1 | 896+-1 | 1108+-1 | 400+-3 | 0 |
When I request a travel time matrix I should get
| | a |
| a | 0 |
| b | 1 |
| c | 15 |
| d | 10 |
-512
View File
@@ -1,512 +0,0 @@
@matrix @testbot
Feature: Basic Duration Matrix
# note that results of travel time are in seconds
Background:
Given the profile "testbot"
And the partition extra arguments "--small-component-size 1 --max-cell-sizes 2,4,8,16"
Scenario: Testbot - Travel time matrix of minimal network
Given the node map
"""
a b
"""
And the ways
| nodes |
| ab |
When I request a travel time matrix I should get
| | a | b |
| a | 0 | 10 |
| b | 10 | 0 |
@ch
Scenario: Testbot - Travel time matrix of minimal network with toll exclude
Given the query options
| exclude | toll |
Given the node map
"""
a b
c d
"""
And the ways
| nodes | highway | toll | # |
| ab | motorway | | not drivable for exclude=motorway |
| cd | primary | | always drivable |
| ac | motorway | yes | not drivable for exclude=toll and exclude=motorway,toll |
| bd | motorway | yes | not drivable for exclude=toll and exclude=motorway,toll |
When I request a travel time matrix I should get
| | a | b | c | d |
| a | 0 | 15 | | |
| b | 15 | 0 | | |
| c | | | 0 | 10 |
| d | | | 10 | 0 |
@ch
Scenario: Testbot - Travel time matrix of minimal network with motorway exclude
Given the query options
| exclude | motorway |
Given the node map
"""
a b
c d
"""
And the ways
| nodes | highway | # |
| ab | motorway | not drivable for exclude=motorway |
| cd | residential | |
| ac | residential | |
| bd | residential | |
When I request a travel time matrix I should get
| | a | b | c | d |
| a | 0 | 45 | 15 | 30 |
@ch
Scenario: Testbot - Travel time matrix of minimal network disconnected motorway exclude
Given the query options
| exclude | motorway |
Given the node map
"""
ab efgh
cd
"""
And the ways
| nodes | highway | # |
| be | motorway | not drivable for exclude=motorway |
| abcd | residential | |
| efgh | residential | |
When I request a travel time matrix I should get
| | a | b | e |
| a | 0 | 7.5 | |
@ch
Scenario: Testbot - Travel time matrix of minimal network with motorway and toll excludes
Given the query options
| exclude | motorway,toll |
Given the node map
"""
a b e f
c d g h
"""
And the ways
| nodes | highway | toll | # |
| be | motorway | | not drivable for exclude=motorway |
| dg | primary | yes | not drivable for exclude=toll |
| abcd | residential | | |
| efgh | residential | | |
When I request a travel time matrix I should get
| | a | b | e | g |
| a | 0 | 15 | | |
Scenario: Testbot - Travel time matrix with different way speeds
Given the node map
"""
a b c d
"""
And the ways
| nodes | highway |
| ab | primary |
| bc | secondary |
| cd | tertiary |
When I request a travel time matrix I should get
| | a | b | c | d |
| a | 0 | 10 | 30 | 60 |
| b | 10 | 0 | 20 | 50 |
| c | 30 | 20 | 0 | 30 |
| d | 60 | 50 | 30 | 0 |
When I request a travel time matrix I should get
| | a | b | c | d |
| a | 0 | 10 | 30 | 60 |
When I request a travel time matrix I should get
| | a |
| a | 0 |
| b | 10 |
| c | 30 |
| d | 60 |
Scenario: Testbot - Travel time matrix of small grid
Given the node map
"""
a b c
d e f
"""
And the ways
| nodes |
| abc |
| def |
| ad |
| be |
| cf |
When I request a travel time matrix I should get
| | a | b | e | f |
| a | 0 | 10 | 20 | 30 |
| b | 10 | 0 | 10 | 20 |
| e | 20 | 10 | 0 | 10 |
| f | 30 | 20 | 10 | 0 |
Scenario: Testbot - Travel time matrix of network with unroutable parts
Given the node map
"""
a b
"""
And the ways
| nodes | oneway |
| ab | yes |
When I request a travel time matrix I should get
| | a | b |
| a | 0 | 10 |
| b | | 0 |
Scenario: Testbot - Travel time matrix of network with oneways
Given the node map
"""
x a b y
d e
"""
And the ways
| nodes | oneway |
| abeda | yes |
| xa | |
| by | |
When I request a travel time matrix I should get
| | x | y | d | e |
| x | 0 | 30 | 40 | 30 |
| y | 50 | 0 | 30 | 20 |
| d | 20 | 30 | 0 | 30 |
| e | 30 | 40 | 10 | 0 |
Scenario: Testbot - Rectangular travel time matrix
Given the node map
"""
a b c
d e f
"""
And the ways
| nodes |
| abc |
| def |
| ad |
| be |
| cf |
When I request a travel time matrix I should get
| | a | b | e | f |
| a | 0 | 10 | 20 | 30 |
When I request a travel time matrix I should get
| | a |
| a | 0 |
| b | 10 |
| e | 20 |
| f | 30 |
When I request a travel time matrix I should get
| | a | b | e | f |
| a | 0 | 10 | 20 | 30 |
| b | 10 | 0 | 10 | 20 |
When I request a travel time matrix I should get
| | a | b |
| a | 0 | 10 |
| b | 10 | 0 |
| e | 20 | 10 |
| f | 30 | 20 |
When I request a travel time matrix I should get
| | a | b | e | f |
| a | 0 | 10 | 20 | 30 |
| b | 10 | 0 | 10 | 20 |
| e | 20 | 10 | 0 | 10 |
When I request a travel time matrix I should get
| | a | b | e |
| a | 0 | 10 | 20 |
| b | 10 | 0 | 10 |
| e | 20 | 10 | 0 |
| f | 30 | 20 | 10 |
When I request a travel time matrix I should get
| | a | b | e | f |
| a | 0 | 10 | 20 | 30 |
| b | 10 | 0 | 10 | 20 |
| e | 20 | 10 | 0 | 10 |
| f | 30 | 20 | 10 | 0 |
Scenario: Testbot - Travel time 3x2 matrix
Given the node map
"""
a b c
d e f
"""
And the ways
| nodes |
| abc |
| def |
| ad |
| be |
| cf |
When I request a travel time matrix I should get
| | b | e | f |
| a | 10 | 20 | 30 |
| b | 0 | 10 | 20 |
Scenario: Testbot - All coordinates are from same small component
Given a grid size of 300 meters
Given the extract extra arguments "--small-component-size 4"
Given the node map
"""
a b f
d e g
"""
And the ways
| nodes |
| ab |
| be |
| ed |
| da |
| fg |
When I request a travel time matrix I should get
| | f | g |
| f | 0 | 30 |
| g | 30 | 0 |
Scenario: Testbot - Coordinates are from different small component and snap to big CC
Given a grid size of 300 meters
Given the extract extra arguments "--small-component-size 4"
Given the node map
"""
a b f h
d e g i
"""
And the ways
| nodes |
| ab |
| be |
| ed |
| da |
| fg |
| hi |
When I request a travel time matrix I should get
| | f | g | h | i |
| f | 0 | 30 | 0 | 30 |
| g | 30 | 0 | 30 | 0 |
| h | 0 | 30 | 0 | 30 |
| i | 30 | 0 | 30 | 0 |
Scenario: Testbot - Travel time matrix with loops
Given the node map
"""
a 1 2 b
d 4 3 c
"""
And the ways
| nodes | oneway |
| ab | yes |
| bc | yes |
| cd | yes |
| da | yes |
When I request a travel time matrix I should get
| | 1 | 2 | 3 | 4 |
| 1 | 0 | 10 +-1 | 40 +-1 | 50 +-1 |
| 2 | 70 +-1 | 0 | 30 +-1 | 40 +-1 |
| 3 | 40 +-1 | 50 +-1 | 0 | 10 +-1 |
| 4 | 30 +-1 | 40 +-1 | 70 +-1 | 0 |
Scenario: Testbot - Travel time matrix based on segment durations
Given the profile file
"""
local functions = require('testbot')
functions.setup_testbot = functions.setup
functions.setup = function()
local profile = functions.setup_testbot()
profile.traffic_signal_penalty = 0
profile.u_turn_penalty = 0
return profile
end
functions.process_segment = function(profile, segment)
segment.weight = 2
segment.duration = 11
end
return functions
"""
And the node map
"""
a-b-c-d
.
e
"""
And the ways
| nodes |
| abcd |
| ce |
When I request a travel time matrix I should get
| | a | b | c | d | e |
| a | 0 | 11 | 22 | 33 | 33 |
| b | 11 | 0 | 11 | 22 | 22 |
| c | 22 | 11 | 0 | 11 | 11 |
| d | 33 | 22 | 11 | 0 | 22 |
| e | 33 | 22 | 11 | 22 | 0 |
Scenario: Testbot - Travel time matrix for alternative loop paths
Given the profile file
"""
local functions = require('testbot')
functions.setup_testbot = functions.setup
functions.setup = function()
local profile = functions.setup_testbot()
profile.traffic_signal_penalty = 0
profile.u_turn_penalty = 0
profile.weight_precision = 3
return profile
end
functions.process_segment = function(profile, segment)
segment.weight = 777
segment.duration = 3
end
return functions
"""
And the node map
"""
a 2 1 b
7 4
8 3
c 5 6 d
"""
And the ways
| nodes | oneway |
| ab | yes |
| bd | yes |
| dc | yes |
| ca | yes |
When I request a travel time matrix I should get
| | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| 1 | 0 | 11 | 3 | 2 | 6 | 5 | 8.9 | 7.9 |
| 2 | 1 | 0 | 4 | 3 | 7 | 6 | 9.9 | 8.9 |
| 3 | 9 | 8 | 0 | 11 | 3 | 2 | 5.9 | 4.9 |
| 4 | 10 | 9 | 1 | 0 | 4 | 3 | 6.9 | 5.9 |
| 5 | 6 | 5 | 9 | 8 | 0 | 11 | 2.9 | 1.9 |
| 6 | 7 | 6 | 10 | 9 | 1 | 0 | 3.9 | 2.9 |
| 7 | 3.1 | 2.1 | 6.1 | 5.1 | 9.1 | 8.1 | 0 | 11 |
| 8 | 4.1 | 3.1 | 7.1 | 6.1 | 10.1 | 9.1 | 1 | 0 |
Scenario: Testbot - Travel time matrix with ties
Given the profile file
"""
local functions = require('testbot')
functions.process_segment = function(profile, segment)
segment.weight = 1
segment.duration = 1
end
functions.process_turn = function(profile, turn)
if turn.angle >= 0 then
turn.duration = 16
else
turn.duration = 4
end
turn.weight = 0
end
return functions
"""
And the node map
"""
a b
c d
"""
And the ways
| nodes |
| ab |
| ac |
| bd |
| dc |
When I route I should get
| from | to | route | distance | time | weight |
| a | c | ac,ac | 200m | 5s | 5 |
When I request a travel time matrix I should get
| | a | b | c | d |
| a | 0 | 1 | 5 | 10 |
When I request a travel time matrix I should get
| | a |
| a | 0 |
| b | 1 |
| c | 15 |
| d | 10 |
Scenario: Testbot - OneToMany vs ManyToOne
Given the node map
"""
a b
c
"""
And the ways
| nodes | oneway |
| ab | yes |
| ac | |
| bc | |
When I request a travel time matrix I should get
| | a | b |
| b | 24.1 | 0 |
When I request a travel time matrix I should get
| | a |
| a | 0 |
| b | 24.1 |
+2 -61
View File
@@ -39,64 +39,5 @@ Feature: Fixed bugs, kept to check for regressions
| de | yes |
When I route I should get
| from | to | route |
| 1 | 2 | bcd,bcd |
#############################
# This test models the OSM map at the location for
# https://github.com/Project-OSRM/osrm-backend/issues/5039
#############################
Scenario: Mixed Entry and Exit and segregated
Given the profile file "car" initialized with
"""
profile.properties.left_hand_driving = true
"""
Given the node locations
| node | lon | lat |
| a | 171.12889297029 | -42.58425289548 |
| b | 171.1299357 | -42.5849295 |
| c | 171.1295427 | -42.5849385 |
| d | 171.1297356 | -42.5852029 |
| e | 171.1296909 | -42.5851986 |
| f | 171.1295097 | -42.585007 |
| g | 171.1298225 | -42.5851928 |
| h | 171.1300262 | -42.5859122 |
| i | 171.1292651 | -42.584698 |
| j | 171.1297209 | -42.5848569 |
| k | 171.1297188 | -42.5854056 |
| l | 171.1298326 | -42.5857266 |
| m | 171.1298871 | -42.5848922 |
| n | 171.1296505 | -42.585189 |
| o | 171.1295206 | -42.5850862 |
| p | 171.1296106 | -42.5848862 |
| q | 171.1299784 | -42.5850191 |
| r | 171.1298867 | -42.5851671 |
| s | 171.1306955 | -42.5845812 |
| t | 171.129525 | -42.584807 |
| u | 171.1299705 | -42.584984 |
| v | 171.1299067 | -42.5849073 |
| w | 171.1302061 | -42.5848173 |
| x | 171.12975 | -42.5855753 |
| y | 171.129969 | -42.585079 |
| 1 | 171.131511926651| -42.584306746421966 |
| 2 | 171.128743886947| -42.58414875714669 |
And the ways
| nodes | highway | maxspeed | name | ref | surface | junction | oneway |
| ws | primary | 100 | Taramakau Highway | SH 6 | asphalt | | |
| kxlh | trunk | | Otira Highway | SH 73 | | | |
| ai | primary | 100 | Kumara Junction Highway | SH 6 | asphalt | | |
| qyrgdenof | primary | 100 | Kumara Junction | | | roundabout | yes |
| ke | trunk | | Otira Highway | SH 73 | | | yes |
| itj | primary | 100 | Kumara Junction Highway | SH 6 | | | yes |
| gk | trunk | | Otira Highway | SH 73 | | | yes |
| fi | primary | 100 | Kumara Junction Highway | SH 6 | | | yes |
| wq | primary | 100 | Taramakau Highway | SH 6 | | | yes |
| vw | primary | 100 | Taramakau Highway | SH 6 | | | yes |
| vbuq | primary | 100 | Kumara Junction | | | roundabout | yes |
| jmv | primary | 100 | Kumara Junction | | | roundabout | yes |
| fcpj | primary | 100 | Kumara Junction | | | roundabout | yes |
When I route I should get
| waypoints | route | turns |
| 1,2 | Taramakau Highway,Kumara Junction Highway,Kumara Junction Highway,Kumara Junction Highway | depart,Kumara Junction-exit-2,exit rotary slight left,arrive |
| from | to | route |
| 1 | 2 | bcd,bcd |
@@ -106,40 +106,6 @@ Feature: Multi level routing
| l | 144.7 | 60 |
| o | 124.7 | 0 |
When I request a travel distance matrix I should get
| | a | f | l | o |
| a | 0+-2 | 2287+-2 | 1443+-2 | 1243+-2 |
| f | 2284+-2 | 0+-2 | 1241+-2 | 1443+-2 |
| l | 1443+-2 | 1244+-2 | 0+-2 | 600+-2 |
| o | 1243+-2 | 1444+-2 | 600+-2 | 0+-2 |
When I request a travel distance matrix I should get
| | a | f | l | o |
| a | 0 | 2287.2+-2 | 1443+-2 | 1243+-2 |
When I request a travel distance matrix I should get
| | a |
| a | 0 |
| f | 2284.5+-2 |
| l | 1443.1 |
| o | 1243 |
When I request a travel distance matrix I should get
| | a | f | l | o |
| a | 0 | 2287+-2 | 1443+-2 | 1243+-2 |
| o | 1243 | 1444+-2 | 600+-2 | 0+-2 |
When I request a travel distance matrix I should get
| | a | o |
| a | 0+-2 | 1243+-2 |
| f | 2284+-2 | 1443+-2 |
| l | 1443+-2 | 600+-2 |
| o | 1243+-2 | 0+-2 |
Scenario: Testbot - Multi level routing: horizontal road
Given the node map
"""
+1 -1
View File
@@ -54,7 +54,7 @@ Feature: Traffic - speeds
| a | d | ad,ad | 27 km/h | 1275.7,0 | 1 |
| d | c | dc,dc | 36 km/h | 956.8,0 | 0 |
| g | b | fb,fb | 36 km/h | 164.7,0 | 0 |
| a | g | ad,df,fb,fb | 30 km/h | 1295.7,487.5,304.7,0 | 1:0:0 |
| a | g | ad,df,fb,fb | 30 km/h | 1275.7,487.5,304.7,0 | 1:0:0 |
Scenario: Weighting based on speed file weights, ETA based on file durations
+1 -2
View File
@@ -21,8 +21,7 @@ struct CustomizationConfig final : storage::IOConfig
".osrm.partition",
".osrm.cells",
".osrm.ebg_nodes",
".osrm.properties",
".osrm.enw"},
".osrm.properties"},
{},
{".osrm.cell_metrics", ".osrm.mldgr"}),
requested_num_threads(0)
+16 -97
View File
@@ -16,109 +16,28 @@ namespace osrm
namespace customizer
{
struct EdgeBasedGraphEdgeData
using EdgeBasedGraphEdgeData = partitioner::EdgeBasedGraphEdgeData;
struct MultiLevelEdgeBasedGraph
: public partitioner::MultiLevelGraph<EdgeBasedGraphEdgeData, storage::Ownership::Container>
{
NodeID turn_id; // ID of the edge based node (node based edge)
using Base =
partitioner::MultiLevelGraph<EdgeBasedGraphEdgeData, storage::Ownership::Container>;
using Base::Base;
};
template <typename EdgeDataT, storage::Ownership Ownership> class MultiLevelGraph;
namespace serialization
struct MultiLevelEdgeBasedGraphView
: public partitioner::MultiLevelGraph<EdgeBasedGraphEdgeData, storage::Ownership::View>
{
template <typename EdgeDataT, storage::Ownership Ownership>
void read(storage::tar::FileReader &reader,
const std::string &name,
MultiLevelGraph<EdgeDataT, Ownership> &graph);
template <typename EdgeDataT, storage::Ownership Ownership>
void write(storage::tar::FileWriter &writer,
const std::string &name,
const MultiLevelGraph<EdgeDataT, Ownership> &graph);
}
template <typename EdgeDataT, storage::Ownership Ownership>
class MultiLevelGraph : public partitioner::MultiLevelGraph<EdgeDataT, Ownership>
{
private:
using SuperT = partitioner::MultiLevelGraph<EdgeDataT, Ownership>;
using PartitionerGraphT = partitioner::MultiLevelGraph<partitioner::EdgeBasedGraphEdgeData,
storage::Ownership::Container>;
template <typename T> using Vector = util::ViewOrVector<T, Ownership>;
public:
using NodeArrayEntry = typename SuperT::NodeArrayEntry;
using EdgeArrayEntry = typename SuperT::EdgeArrayEntry;
using EdgeOffset = typename SuperT::EdgeOffset;
MultiLevelGraph() = default;
MultiLevelGraph(MultiLevelGraph &&) = default;
MultiLevelGraph(const MultiLevelGraph &) = default;
MultiLevelGraph &operator=(MultiLevelGraph &&) = default;
MultiLevelGraph &operator=(const MultiLevelGraph &) = default;
MultiLevelGraph(PartitionerGraphT &&graph,
Vector<EdgeWeight> node_weights_,
Vector<EdgeDuration> node_durations_)
: node_weights(std::move(node_weights_)), node_durations(std::move(node_durations_))
{
util::ViewOrVector<PartitionerGraphT::EdgeArrayEntry, storage::Ownership::Container>
original_edge_array;
std::tie(SuperT::node_array,
original_edge_array,
SuperT::node_to_edge_offset,
SuperT::connectivity_checksum) = std::move(graph).data();
SuperT::edge_array.reserve(original_edge_array.size());
for (const auto &edge : original_edge_array)
{
SuperT::edge_array.push_back({edge.target, {edge.data.turn_id}});
is_forward_edge.push_back(edge.data.forward);
is_backward_edge.push_back(edge.data.backward);
}
}
MultiLevelGraph(Vector<NodeArrayEntry> node_array_,
Vector<EdgeArrayEntry> edge_array_,
Vector<EdgeOffset> node_to_edge_offset_,
Vector<EdgeWeight> node_weights_,
Vector<EdgeDuration> node_durations_,
Vector<bool> is_forward_edge_,
Vector<bool> is_backward_edge_)
: SuperT(std::move(node_array_), std::move(edge_array_), std::move(node_to_edge_offset_)),
node_weights(std::move(node_weights_)), node_durations(std::move(node_durations_)),
is_forward_edge(is_forward_edge_), is_backward_edge(is_backward_edge_)
{
}
EdgeWeight GetNodeWeight(NodeID node) const { return node_weights[node]; }
EdgeWeight GetNodeDuration(NodeID node) const { return node_durations[node]; }
bool IsForwardEdge(EdgeID edge) const { return is_forward_edge[edge]; }
bool IsBackwardEdge(EdgeID edge) const { return is_backward_edge[edge]; }
friend void
serialization::read<EdgeDataT, Ownership>(storage::tar::FileReader &reader,
const std::string &name,
MultiLevelGraph<EdgeDataT, Ownership> &graph);
friend void
serialization::write<EdgeDataT, Ownership>(storage::tar::FileWriter &writer,
const std::string &name,
const MultiLevelGraph<EdgeDataT, Ownership> &graph);
protected:
Vector<EdgeWeight> node_weights;
Vector<EdgeDuration> node_durations;
Vector<bool> is_forward_edge;
Vector<bool> is_backward_edge;
using Base = partitioner::MultiLevelGraph<EdgeBasedGraphEdgeData, storage::Ownership::View>;
using Base::Base;
};
using MultiLevelEdgeBasedGraph =
MultiLevelGraph<EdgeBasedGraphEdgeData, storage::Ownership::Container>;
using MultiLevelEdgeBasedGraphView =
MultiLevelGraph<EdgeBasedGraphEdgeData, storage::Ownership::View>;
struct StaticEdgeBasedGraphEdge : MultiLevelEdgeBasedGraph::InputEdge
{
using Base = MultiLevelEdgeBasedGraph::InputEdge;
using Base::Base;
};
}
}
-33
View File
@@ -73,39 +73,6 @@ writeCellMetrics(const boost::filesystem::path &path,
}
}
}
// reads .osrm.mldgr file
template <typename MultiLevelGraphT>
inline void readGraph(const boost::filesystem::path &path,
MultiLevelGraphT &graph,
std::uint32_t &connectivity_checksum)
{
static_assert(std::is_same<customizer::MultiLevelEdgeBasedGraphView, MultiLevelGraphT>::value ||
std::is_same<customizer::MultiLevelEdgeBasedGraph, MultiLevelGraphT>::value,
"");
storage::tar::FileReader reader{path, storage::tar::FileReader::VerifyFingerprint};
reader.ReadInto("/mld/connectivity_checksum", connectivity_checksum);
serialization::read(reader, "/mld/multilevelgraph", graph);
}
// writes .osrm.mldgr file
template <typename MultiLevelGraphT>
inline void writeGraph(const boost::filesystem::path &path,
const MultiLevelGraphT &graph,
const std::uint32_t connectivity_checksum)
{
static_assert(std::is_same<customizer::MultiLevelEdgeBasedGraphView, MultiLevelGraphT>::value ||
std::is_same<customizer::MultiLevelEdgeBasedGraph, MultiLevelGraphT>::value,
"");
storage::tar::FileWriter writer{path, storage::tar::FileWriter::GenerateFingerprint};
writer.WriteElementCount64("/mld/connectivity_checksum", 1);
writer.WriteFrom("/mld/connectivity_checksum", connectivity_checksum);
serialization::write(writer, "/mld/multilevelgraph", graph);
}
}
}
}
-30
View File
@@ -1,8 +1,6 @@
#ifndef OSRM_CUSTOMIZER_SERIALIZATION_HPP
#define OSRM_CUSTOMIZER_SERIALIZATION_HPP
#include "customizer/edge_based_graph.hpp"
#include "partitioner/cell_storage.hpp"
#include "storage/serialization.hpp"
@@ -33,34 +31,6 @@ inline void write(storage::tar::FileWriter &writer,
storage::serialization::write(writer, name + "/weights", metric.weights);
storage::serialization::write(writer, name + "/durations", metric.durations);
}
template <typename EdgeDataT, storage::Ownership Ownership>
inline void read(storage::tar::FileReader &reader,
const std::string &name,
MultiLevelGraph<EdgeDataT, Ownership> &graph)
{
storage::serialization::read(reader, name + "/node_array", graph.node_array);
storage::serialization::read(reader, name + "/node_weights", graph.node_weights);
storage::serialization::read(reader, name + "/node_durations", graph.node_durations);
storage::serialization::read(reader, name + "/edge_array", graph.edge_array);
storage::serialization::read(reader, name + "/is_forward_edge", graph.is_forward_edge);
storage::serialization::read(reader, name + "/is_backward_edge", graph.is_backward_edge);
storage::serialization::read(reader, name + "/node_to_edge_offset", graph.node_to_edge_offset);
}
template <typename EdgeDataT, storage::Ownership Ownership>
inline void write(storage::tar::FileWriter &writer,
const std::string &name,
const MultiLevelGraph<EdgeDataT, Ownership> &graph)
{
storage::serialization::write(writer, name + "/node_array", graph.node_array);
storage::serialization::write(writer, name + "/node_weights", graph.node_weights);
storage::serialization::write(writer, name + "/node_durations", graph.node_durations);
storage::serialization::write(writer, name + "/edge_array", graph.edge_array);
storage::serialization::write(writer, name + "/is_forward_edge", graph.is_forward_edge);
storage::serialization::write(writer, name + "/is_backward_edge", graph.is_backward_edge);
storage::serialization::write(writer, name + "/node_to_edge_offset", graph.node_to_edge_offset);
}
}
}
}
-9
View File
@@ -50,9 +50,6 @@ template <typename AlgorithmT> struct HasMapMatching final : std::false_type
template <typename AlgorithmT> struct HasManyToManySearch final : std::false_type
{
};
template <typename AlgorithmT> struct SupportsDistanceAnnotationType final : std::false_type
{
};
template <typename AlgorithmT> struct HasGetTileTurns final : std::false_type
{
};
@@ -76,9 +73,6 @@ template <> struct HasMapMatching<ch::Algorithm> final : std::true_type
template <> struct HasManyToManySearch<ch::Algorithm> final : std::true_type
{
};
template <> struct SupportsDistanceAnnotationType<ch::Algorithm> final : std::true_type
{
};
template <> struct HasGetTileTurns<ch::Algorithm> final : std::true_type
{
};
@@ -102,9 +96,6 @@ template <> struct HasMapMatching<mld::Algorithm> final : std::true_type
template <> struct HasManyToManySearch<mld::Algorithm> final : std::true_type
{
};
template <> struct SupportsDistanceAnnotationType<mld::Algorithm> final : std::false_type
{
};
template <> struct HasGetTileTurns<mld::Algorithm> final : std::true_type
{
};
+8 -48
View File
@@ -36,10 +36,9 @@ class TableAPI final : public BaseAPI
{
}
virtual void
MakeResponse(const std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>> &tables,
const std::vector<PhantomNode> &phantoms,
util::json::Object &response) const
virtual void MakeResponse(const std::vector<EdgeWeight> &durations,
const std::vector<PhantomNode> &phantoms,
util::json::Object &response) const
{
auto number_of_sources = parameters.sources.size();
auto number_of_destinations = parameters.destinations.size();
@@ -65,18 +64,8 @@ class TableAPI final : public BaseAPI
response.values["destinations"] = MakeWaypoints(phantoms, parameters.destinations);
}
if (parameters.annotations & TableParameters::AnnotationsType::Duration)
{
response.values["durations"] =
MakeDurationTable(tables.first, number_of_sources, number_of_destinations);
}
if (parameters.annotations & TableParameters::AnnotationsType::Distance)
{
response.values["distances"] =
MakeDistanceTable(tables.second, number_of_sources, number_of_destinations);
}
response.values["durations"] =
MakeTable(durations, number_of_sources, number_of_destinations);
response.values["code"] = "Ok";
}
@@ -108,9 +97,9 @@ class TableAPI final : public BaseAPI
return json_waypoints;
}
virtual util::json::Array MakeDurationTable(const std::vector<EdgeWeight> &values,
std::size_t number_of_rows,
std::size_t number_of_columns) const
virtual util::json::Array MakeTable(const std::vector<EdgeWeight> &values,
std::size_t number_of_rows,
std::size_t number_of_columns) const
{
util::json::Array json_table;
for (const auto row : util::irange<std::size_t>(0UL, number_of_rows))
@@ -127,7 +116,6 @@ class TableAPI final : public BaseAPI
{
return util::json::Value(util::json::Null());
}
// division by 10 because the duration is in deciseconds (10s)
return util::json::Value(util::json::Number(duration / 10.));
});
json_table.values.push_back(std::move(json_row));
@@ -135,34 +123,6 @@ class TableAPI final : public BaseAPI
return json_table;
}
virtual util::json::Array MakeDistanceTable(const std::vector<EdgeDistance> &values,
std::size_t number_of_rows,
std::size_t number_of_columns) const
{
util::json::Array json_table;
for (const auto row : util::irange<std::size_t>(0UL, number_of_rows))
{
util::json::Array json_row;
auto row_begin_iterator = values.begin() + (row * number_of_columns);
auto row_end_iterator = values.begin() + ((row + 1) * number_of_columns);
json_row.values.resize(number_of_columns);
std::transform(row_begin_iterator,
row_end_iterator,
json_row.values.begin(),
[](const EdgeDistance distance) {
if (distance == INVALID_EDGE_DISTANCE)
{
return util::json::Value(util::json::Null());
}
// round to single decimal place
return util::json::Value(
util::json::Number(std::round(distance * 10) / 10.));
});
json_table.values.push_back(std::move(json_row));
}
return json_table;
}
const TableParameters &parameters;
};
+1 -41
View File
@@ -60,16 +60,6 @@ struct TableParameters : public BaseParameters
std::vector<std::size_t> sources;
std::vector<std::size_t> destinations;
enum class AnnotationsType
{
None = 0,
Duration = 0x01,
Distance = 0x02,
All = Duration | Distance
};
AnnotationsType annotations = AnnotationsType::Duration;
TableParameters() = default;
template <typename... Args>
TableParameters(std::vector<std::size_t> sources_,
@@ -80,16 +70,6 @@ struct TableParameters : public BaseParameters
{
}
template <typename... Args>
TableParameters(std::vector<std::size_t> sources_,
std::vector<std::size_t> destinations_,
const AnnotationsType annotations_,
Args... args_)
: BaseParameters{std::forward<Args>(args_)...}, sources{std::move(sources_)},
destinations{std::move(destinations_)}, annotations{annotations_}
{
}
bool IsValid() const
{
if (!BaseParameters::IsValid())
@@ -99,7 +79,7 @@ struct TableParameters : public BaseParameters
if (coordinates.size() < 2)
return false;
// 1/ The user is able to specify duplicates in srcs and dsts, in that case it's their fault
// 1/ The user is able to specify duplicates in srcs and dsts, in that case it's her fault
// 2/ len(srcs) and len(dsts) smaller or equal to len(locations)
if (sources.size() > coordinates.size())
@@ -120,26 +100,6 @@ struct TableParameters : public BaseParameters
return true;
}
};
inline bool operator&(TableParameters::AnnotationsType lhs, TableParameters::AnnotationsType rhs)
{
return static_cast<bool>(
static_cast<std::underlying_type_t<TableParameters::AnnotationsType>>(lhs) &
static_cast<std::underlying_type_t<TableParameters::AnnotationsType>>(rhs));
}
inline TableParameters::AnnotationsType operator|(TableParameters::AnnotationsType lhs,
TableParameters::AnnotationsType rhs)
{
return (TableParameters::AnnotationsType)(
static_cast<std::underlying_type_t<TableParameters::AnnotationsType>>(lhs) |
static_cast<std::underlying_type_t<TableParameters::AnnotationsType>>(rhs));
}
inline TableParameters::AnnotationsType &operator|=(TableParameters::AnnotationsType &lhs,
TableParameters::AnnotationsType rhs)
{
return lhs = lhs | rhs;
}
}
}
}
@@ -2,7 +2,6 @@
#define OSRM_ENGINE_DATAFACADE_ALGORITHM_DATAFACADE_HPP
#include "contractor/query_edge.hpp"
#include "customizer/edge_based_graph.hpp"
#include "extractor/edge_based_edge.hpp"
#include "engine/algorithm.hpp"
@@ -60,7 +59,7 @@ template <> class AlgorithmDataFacade<CH>
template <> class AlgorithmDataFacade<MLD>
{
public:
using EdgeData = customizer::EdgeBasedGraphEdgeData;
using EdgeData = extractor::EdgeBasedEdge::EdgeData;
using EdgeRange = util::range<EdgeID>;
// search graph access
@@ -72,20 +71,12 @@ template <> class AlgorithmDataFacade<MLD>
virtual unsigned GetOutDegree(const NodeID n) const = 0;
virtual EdgeRange GetAdjacentEdgeRange(const NodeID node) const = 0;
virtual EdgeWeight GetNodeWeight(const NodeID node) const = 0;
virtual EdgeWeight GetNodeDuration(const NodeID node) const = 0; // TODO: to be removed
virtual bool IsForwardEdge(EdgeID edge) const = 0;
virtual bool IsBackwardEdge(EdgeID edge) const = 0;
virtual NodeID GetTarget(const EdgeID e) const = 0;
virtual const EdgeData &GetEdgeData(const EdgeID e) const = 0;
virtual EdgeRange GetAdjacentEdgeRange(const NodeID node) const = 0;
virtual const partitioner::MultiLevelPartitionView &GetMultiLevelPartition() const = 0;
virtual const partitioner::CellStorageView &GetCellStorage() const = 0;
@@ -133,6 +133,7 @@ class ContiguousInternalMemoryDataFacadeBase : public BaseDataFacade
using RTreeNode = SharedRTree::TreeNode;
extractor::ClassData exclude_mask;
std::string m_timestamp;
extractor::ProfileProperties *m_profile_properties;
extractor::Datasources *m_datasources;
@@ -282,13 +283,13 @@ class ContiguousInternalMemoryDataFacadeBase : public BaseDataFacade
return segment_data.GetReverseDatasources(id);
}
TurnPenalty GetWeightPenaltyForEdgeID(const EdgeID id) const override final
TurnPenalty GetWeightPenaltyForEdgeID(const unsigned id) const override final
{
BOOST_ASSERT(m_turn_weight_penalties.size() > id);
return m_turn_weight_penalties[id];
}
TurnPenalty GetDurationPenaltyForEdgeID(const EdgeID id) const override final
TurnPenalty GetDurationPenaltyForEdgeID(const unsigned id) const override final
{
BOOST_ASSERT(m_turn_duration_penalties.size() > id);
return m_turn_duration_penalties[id];
@@ -621,6 +622,7 @@ class ContiguousInternalMemoryDataFacade<CH>
const std::size_t exclude_index)
: ContiguousInternalMemoryDataFacadeBase(allocator, metric_name, exclude_index),
ContiguousInternalMemoryAlgorithmDataFacade<CH>(allocator, metric_name, exclude_index)
{
}
};
@@ -682,31 +684,6 @@ template <> class ContiguousInternalMemoryAlgorithmDataFacade<MLD> : public Algo
return query_graph.GetOutDegree(n);
}
EdgeRange GetAdjacentEdgeRange(const NodeID node) const override final
{
return query_graph.GetAdjacentEdgeRange(node);
}
EdgeWeight GetNodeWeight(const NodeID node) const override final
{
return query_graph.GetNodeWeight(node);
}
EdgeDuration GetNodeDuration(const NodeID node) const override final
{
return query_graph.GetNodeDuration(node);
}
bool IsForwardEdge(const NodeID node) const override final
{
return query_graph.IsForwardEdge(node);
}
bool IsBackwardEdge(const NodeID node) const override final
{
return query_graph.IsBackwardEdge(node);
}
NodeID GetTarget(const EdgeID e) const override final { return query_graph.GetTarget(e); }
const EdgeData &GetEdgeData(const EdgeID e) const override final
@@ -714,6 +691,11 @@ template <> class ContiguousInternalMemoryAlgorithmDataFacade<MLD> : public Algo
return query_graph.GetEdgeData(e);
}
EdgeRange GetAdjacentEdgeRange(const NodeID node) const override final
{
return query_graph.GetAdjacentEdgeRange(node);
}
EdgeRange GetBorderEdgeRange(const LevelID level, const NodeID node) const override final
{
return query_graph.GetBorderEdgeRange(level, node);
@@ -738,6 +720,7 @@ class ContiguousInternalMemoryDataFacade<MLD> final
const std::size_t exclude_index)
: ContiguousInternalMemoryDataFacadeBase(allocator, metric_name, exclude_index),
ContiguousInternalMemoryAlgorithmDataFacade<MLD>(allocator, metric_name, exclude_index)
{
}
};
@@ -33,6 +33,7 @@
#include <boost/range/adaptor/reversed.hpp>
#include <boost/range/any_range.hpp>
#include <cstddef>
#include <string>
@@ -86,9 +87,9 @@ class BaseDataFacade
virtual NodeForwardRange GetUncompressedForwardGeometry(const EdgeID id) const = 0;
virtual NodeReverseRange GetUncompressedReverseGeometry(const EdgeID id) const = 0;
virtual TurnPenalty GetWeightPenaltyForEdgeID(const EdgeID id) const = 0;
virtual TurnPenalty GetWeightPenaltyForEdgeID(const unsigned id) const = 0;
virtual TurnPenalty GetDurationPenaltyForEdgeID(const EdgeID id) const = 0;
virtual TurnPenalty GetDurationPenaltyForEdgeID(const unsigned id) const = 0;
// Gets the weight values for each segment in an uncompressed geometry.
// Should always be 1 shorter than GetUncompressedGeometry
+2 -35
View File
@@ -13,7 +13,6 @@
#include <algorithm>
#include <cmath>
#include <iterator>
#include <memory>
#include <vector>
@@ -448,8 +447,6 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
const auto forward_durations = datafacade.GetUncompressedForwardDurations(geometry_id);
const auto reverse_durations = datafacade.GetUncompressedReverseDurations(geometry_id);
const auto forward_geometry = datafacade.GetUncompressedForwardGeometry(geometry_id);
const auto forward_weight_offset =
std::accumulate(forward_weights.begin(),
forward_weights.begin() + data.fwd_segment_position,
@@ -460,25 +457,12 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
forward_durations.begin() + data.fwd_segment_position,
EdgeDuration{0});
EdgeDistance forward_distance_offset = 0;
for (auto current = forward_geometry.begin();
current < forward_geometry.begin() + data.fwd_segment_position;
++current)
{
forward_distance_offset += util::coordinate_calculation::fccApproximateDistance(
datafacade.GetCoordinateOfNode(*current),
datafacade.GetCoordinateOfNode(*std::next(current)));
}
EdgeWeight forward_weight = forward_weights[data.fwd_segment_position];
EdgeDuration forward_duration = forward_durations[data.fwd_segment_position];
BOOST_ASSERT(data.fwd_segment_position <
std::distance(forward_durations.begin(), forward_durations.end()));
EdgeWeight forward_weight = forward_weights[data.fwd_segment_position];
EdgeDuration forward_duration = forward_durations[data.fwd_segment_position];
EdgeDistance forward_distance = util::coordinate_calculation::fccApproximateDistance(
datafacade.GetCoordinateOfNode(forward_geometry(data.fwd_segment_position)),
point_on_segment);
const auto reverse_weight_offset =
std::accumulate(reverse_weights.begin(),
reverse_weights.end() - data.fwd_segment_position - 1,
@@ -489,23 +473,10 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
reverse_durations.end() - data.fwd_segment_position - 1,
EdgeDuration{0});
EdgeDistance reverse_distance_offset = 0;
for (auto current = forward_geometry.begin();
current < forward_geometry.end() - data.fwd_segment_position - 2;
++current)
{
reverse_distance_offset += util::coordinate_calculation::fccApproximateDistance(
datafacade.GetCoordinateOfNode(*current),
datafacade.GetCoordinateOfNode(*std::next(current)));
}
EdgeWeight reverse_weight =
reverse_weights[reverse_weights.size() - data.fwd_segment_position - 1];
EdgeDuration reverse_duration =
reverse_durations[reverse_durations.size() - data.fwd_segment_position - 1];
EdgeDistance reverse_distance = util::coordinate_calculation::fccApproximateDistance(
point_on_segment,
datafacade.GetCoordinateOfNode(forward_geometry(data.fwd_segment_position + 1)));
ratio = std::min(1.0, std::max(0.0, ratio));
if (data.forward_segment_id.id != SPECIAL_SEGMENTID)
@@ -539,10 +510,6 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
reverse_weight,
forward_weight_offset,
reverse_weight_offset,
forward_distance,
reverse_distance,
forward_distance_offset,
reverse_distance_offset,
forward_duration,
reverse_duration,
forward_duration_offset,
+2 -2
View File
@@ -63,8 +63,8 @@ struct Hint
friend std::ostream &operator<<(std::ostream &, const Hint &);
};
static_assert(sizeof(Hint) == 80 + 4, "Hint is bigger than expected");
constexpr std::size_t ENCODED_HINT_SIZE = 112;
static_assert(sizeof(Hint) == 64 + 4, "Hint is bigger than expected");
constexpr std::size_t ENCODED_HINT_SIZE = 92;
static_assert(ENCODED_HINT_SIZE / 4 * 3 >= sizeof(Hint),
"ENCODED_HINT_SIZE does not match size of Hint");
}
+3 -42
View File
@@ -47,13 +47,10 @@ struct PhantomNode
: forward_segment_id{SPECIAL_SEGMENTID, false},
reverse_segment_id{SPECIAL_SEGMENTID, false}, forward_weight(INVALID_EDGE_WEIGHT),
reverse_weight(INVALID_EDGE_WEIGHT), forward_weight_offset(0), reverse_weight_offset(0),
forward_distance(INVALID_EDGE_DISTANCE), reverse_distance(INVALID_EDGE_DISTANCE),
forward_distance_offset(0), reverse_distance_offset(0),
forward_duration(MAXIMAL_EDGE_DURATION), reverse_duration(MAXIMAL_EDGE_DURATION),
forward_duration_offset(0), reverse_duration_offset(0), fwd_segment_position(0),
is_valid_forward_source{false}, is_valid_forward_target{false},
is_valid_reverse_source{false}, is_valid_reverse_target{false}, bearing(0)
{
}
@@ -81,30 +78,6 @@ struct PhantomNode
return reverse_duration + reverse_duration_offset;
}
// DO THIS FOR DISTANCE
EdgeDistance GetForwardDistance() const
{
// ..... <-- forward_distance
// .... <-- offset
// ......... <-- desired distance
// x <-- this is PhantomNode.location
// 0----1----2----3----4 <-- EdgeBasedGraph Node segments
BOOST_ASSERT(forward_segment_id.enabled);
return forward_distance + forward_distance_offset;
}
EdgeDistance GetReverseDistance() const
{
// .......... <-- reverse_distance
// ... <-- offset
// ............. <-- desired distance
// x <-- this is PhantomNode.location
// 0----1----2----3----4 <-- EdgeBasedGraph Node segments
BOOST_ASSERT(reverse_segment_id.enabled);
return reverse_distance + reverse_distance_offset;
}
bool IsBidirected() const { return forward_segment_id.enabled && reverse_segment_id.enabled; }
bool IsValid(const unsigned number_of_nodes) const
@@ -115,8 +88,6 @@ struct PhantomNode
(reverse_weight != INVALID_EDGE_WEIGHT)) &&
((forward_duration != MAXIMAL_EDGE_DURATION) ||
(reverse_duration != MAXIMAL_EDGE_DURATION)) &&
((forward_distance != INVALID_EDGE_DISTANCE) ||
(reverse_distance != INVALID_EDGE_DISTANCE)) &&
(component.id != INVALID_COMPONENTID);
}
@@ -159,10 +130,6 @@ struct PhantomNode
EdgeWeight reverse_weight,
EdgeWeight forward_weight_offset,
EdgeWeight reverse_weight_offset,
EdgeDistance forward_distance,
EdgeDistance reverse_distance,
EdgeDistance forward_distance_offset,
EdgeDistance reverse_distance_offset,
EdgeWeight forward_duration,
EdgeWeight reverse_duration,
EdgeWeight forward_duration_offset,
@@ -177,9 +144,7 @@ struct PhantomNode
: forward_segment_id{other.forward_segment_id},
reverse_segment_id{other.reverse_segment_id}, forward_weight{forward_weight},
reverse_weight{reverse_weight}, forward_weight_offset{forward_weight_offset},
reverse_weight_offset{reverse_weight_offset}, forward_distance{forward_distance},
reverse_distance{reverse_distance}, forward_distance_offset{forward_distance_offset},
reverse_distance_offset{reverse_distance_offset}, forward_duration{forward_duration},
reverse_weight_offset{reverse_weight_offset}, forward_duration{forward_duration},
reverse_duration{reverse_duration}, forward_duration_offset{forward_duration_offset},
reverse_duration_offset{reverse_duration_offset},
component{component.id, component.is_tiny}, location{location},
@@ -197,17 +162,13 @@ struct PhantomNode
EdgeWeight reverse_weight;
EdgeWeight forward_weight_offset; // TODO: try to remove -> requires path unpacking changes
EdgeWeight reverse_weight_offset; // TODO: try to remove -> requires path unpacking changes
EdgeDistance forward_distance;
EdgeDistance reverse_distance;
EdgeDistance forward_distance_offset; // TODO: try to remove -> requires path unpacking changes
EdgeDistance reverse_distance_offset; // TODO: try to remove -> requires path unpacking changes
EdgeWeight forward_duration;
EdgeWeight reverse_duration;
EdgeWeight forward_duration_offset; // TODO: try to remove -> requires path unpacking changes
EdgeWeight reverse_duration_offset; // TODO: try to remove -> requires path unpacking changes
ComponentID component;
util::Coordinate location; // this is the coordinate of x
util::Coordinate location;
util::Coordinate input_location;
unsigned short fwd_segment_position;
// is phantom node valid to be used as source or target
@@ -219,7 +180,7 @@ struct PhantomNode
unsigned short bearing : 12;
};
static_assert(sizeof(PhantomNode) == 80, "PhantomNode has more padding then expected");
static_assert(sizeof(PhantomNode) == 64, "PhantomNode has more padding then expected");
using PhantomNodePair = std::pair<PhantomNode, PhantomNode>;
+10 -27
View File
@@ -30,12 +30,10 @@ class RoutingAlgorithmsInterface
virtual InternalRouteResult
DirectShortestPathSearch(const PhantomNodes &phantom_node_pair) const = 0;
virtual std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
virtual std::vector<EdgeDuration>
ManyToManySearch(const std::vector<PhantomNode> &phantom_nodes,
const std::vector<std::size_t> &source_indices,
const std::vector<std::size_t> &target_indices,
const bool calculate_distance,
const bool calculate_duration) const = 0;
const std::vector<std::size_t> &target_indices) const = 0;
virtual routing_algorithms::SubMatchingList
MapMatching(const routing_algorithms::CandidateLists &candidates_list,
@@ -55,7 +53,6 @@ class RoutingAlgorithmsInterface
virtual bool HasDirectShortestPathSearch() const = 0;
virtual bool HasMapMatching() const = 0;
virtual bool HasManyToManySearch() const = 0;
virtual bool SupportsDistanceAnnotationType() const = 0;
virtual bool HasGetTileTurns() const = 0;
virtual bool HasExcludeFlags() const = 0;
virtual bool IsValid() const = 0;
@@ -84,12 +81,10 @@ template <typename Algorithm> class RoutingAlgorithms final : public RoutingAlgo
InternalRouteResult
DirectShortestPathSearch(const PhantomNodes &phantom_nodes) const final override;
virtual std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
std::vector<EdgeDuration>
ManyToManySearch(const std::vector<PhantomNode> &phantom_nodes,
const std::vector<std::size_t> &source_indices,
const std::vector<std::size_t> &target_indices,
const bool calculate_distance,
const bool calculate_duration) const final override;
const std::vector<std::size_t> &target_indices) const final override;
routing_algorithms::SubMatchingList
MapMatching(const routing_algorithms::CandidateLists &candidates_list,
@@ -129,11 +124,6 @@ template <typename Algorithm> class RoutingAlgorithms final : public RoutingAlgo
return routing_algorithms::HasManyToManySearch<Algorithm>::value;
}
bool SupportsDistanceAnnotationType() const final override
{
return routing_algorithms::SupportsDistanceAnnotationType<Algorithm>::value;
}
bool HasGetTileTurns() const final override
{
return routing_algorithms::HasGetTileTurns<Algorithm>::value;
@@ -194,12 +184,10 @@ inline routing_algorithms::SubMatchingList RoutingAlgorithms<Algorithm>::MapMatc
}
template <typename Algorithm>
std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
RoutingAlgorithms<Algorithm>::ManyToManySearch(const std::vector<PhantomNode> &phantom_nodes,
const std::vector<std::size_t> &_source_indices,
const std::vector<std::size_t> &_target_indices,
const bool calculate_distance,
const bool calculate_duration) const
std::vector<EdgeDuration> RoutingAlgorithms<Algorithm>::ManyToManySearch(
const std::vector<PhantomNode> &phantom_nodes,
const std::vector<std::size_t> &_source_indices,
const std::vector<std::size_t> &_target_indices) const
{
BOOST_ASSERT(!phantom_nodes.empty());
@@ -217,13 +205,8 @@ RoutingAlgorithms<Algorithm>::ManyToManySearch(const std::vector<PhantomNode> &p
std::iota(target_indices.begin(), target_indices.end(), 0);
}
return routing_algorithms::manyToManySearch(heaps,
*facade,
phantom_nodes,
std::move(source_indices),
std::move(target_indices),
calculate_distance,
calculate_duration);
return routing_algorithms::manyToManySearch(
heaps, *facade, phantom_nodes, std::move(source_indices), std::move(target_indices));
}
template <typename Algorithm>
@@ -15,43 +15,23 @@ namespace engine
{
namespace routing_algorithms
{
namespace
{
struct NodeBucket
{
NodeID middle_node;
NodeID parent_node;
unsigned column_index : 31; // a column in the weight/duration matrix
unsigned from_clique_arc : 1;
unsigned column_index; // a column in the weight/duration matrix
EdgeWeight weight;
EdgeDuration duration;
NodeBucket(NodeID middle_node,
NodeID parent_node,
bool from_clique_arc,
unsigned column_index,
EdgeWeight weight,
EdgeDuration duration)
: middle_node(middle_node), parent_node(parent_node), column_index(column_index),
from_clique_arc(from_clique_arc), weight(weight), duration(duration)
{
}
NodeBucket(NodeID middle_node,
NodeID parent_node,
unsigned column_index,
EdgeWeight weight,
EdgeDuration duration)
: middle_node(middle_node), parent_node(parent_node), column_index(column_index),
from_clique_arc(false), weight(weight), duration(duration)
NodeBucket(NodeID middle_node, unsigned column_index, EdgeWeight weight, EdgeDuration duration)
: middle_node(middle_node), column_index(column_index), weight(weight), duration(duration)
{
}
// partial order comparison
bool operator<(const NodeBucket &rhs) const
{
return std::tie(middle_node, column_index) < std::tie(rhs.middle_node, rhs.column_index);
}
bool operator<(const NodeBucket &rhs) const { return middle_node < rhs.middle_node; }
// functor for equal_range
struct Compare
@@ -66,36 +46,15 @@ struct NodeBucket
return lhs < rhs.middle_node;
}
};
// functor for equal_range
struct ColumnCompare
{
unsigned column_idx;
ColumnCompare(unsigned column_idx) : column_idx(column_idx){};
bool operator()(const NodeBucket &lhs, const NodeID &rhs) const // lowerbound
{
return std::tie(lhs.middle_node, lhs.column_index) < std::tie(rhs, column_idx);
}
bool operator()(const NodeID &lhs, const NodeBucket &rhs) const // upperbound
{
return std::tie(lhs, column_idx) < std::tie(rhs.middle_node, rhs.column_index);
}
};
};
} // namespace
}
template <typename Algorithm>
std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
manyToManySearch(SearchEngineData<Algorithm> &engine_working_data,
const DataFacade<Algorithm> &facade,
const std::vector<PhantomNode> &phantom_nodes,
const std::vector<std::size_t> &source_indices,
const std::vector<std::size_t> &target_indices,
const bool calculate_distance,
const bool calculate_duration);
std::vector<EdgeDuration> manyToManySearch(SearchEngineData<Algorithm> &engine_working_data,
const DataFacade<Algorithm> &facade,
const std::vector<PhantomNode> &phantom_nodes,
const std::vector<std::size_t> &source_indices,
const std::vector<std::size_t> &target_indices);
} // namespace routing_algorithms
} // namespace engine
@@ -181,7 +181,6 @@ void annotatePath(const FacadeT &facade,
BOOST_ASSERT(datasource_vector.size() > 0);
BOOST_ASSERT(weight_vector.size() + 1 == id_vector.size());
BOOST_ASSERT(duration_vector.size() + 1 == id_vector.size());
const bool is_first_segment = unpacked_path.empty();
const std::size_t start_index =
@@ -406,22 +405,6 @@ InternalRouteResult extractRoute(const DataFacade<AlgorithmT> &facade,
return raw_route_data;
}
template <typename FacadeT> EdgeDistance computeEdgeDistance(const FacadeT &facade, NodeID node_id)
{
const auto geometry_index = facade.GetGeometryIndex(node_id);
EdgeDistance total_distance = 0.0;
auto geometry_range = facade.GetUncompressedForwardGeometry(geometry_index.id);
for (auto current = geometry_range.begin(); current < geometry_range.end() - 1; ++current)
{
total_distance += util::coordinate_calculation::fccApproximateDistance(
facade.GetCoordinateOfNode(*current), facade.GetCoordinateOfNode(*std::next(current)));
}
return total_distance;
}
} // namespace routing_algorithms
} // namespace engine
} // namespace osrm
@@ -288,106 +288,6 @@ void unpackPath(const DataFacade<Algorithm> &facade,
}
}
template <typename BidirectionalIterator>
EdgeDistance calculateEBGNodeAnnotations(const DataFacade<Algorithm> &facade,
BidirectionalIterator packed_path_begin,
BidirectionalIterator packed_path_end)
{
// Make sure we have at least something to unpack
if (packed_path_begin == packed_path_end ||
std::distance(packed_path_begin, packed_path_end) <= 1)
return 0;
std::stack<std::tuple<NodeID, NodeID, bool>> recursion_stack;
std::stack<EdgeDistance> distance_stack;
// We have to push the path in reverse order onto the stack because it's LIFO.
for (auto current = std::prev(packed_path_end); current > packed_path_begin;
current = std::prev(current))
{
recursion_stack.emplace(*std::prev(current), *current, false);
}
std::tuple<NodeID, NodeID, bool> edge;
while (!recursion_stack.empty())
{
edge = recursion_stack.top();
recursion_stack.pop();
// Have we processed the edge before? tells us if we have values in the durations stack that
// we can add up
if (!std::get<2>(edge))
{ // haven't processed edge before, so process it in the body!
std::get<2>(edge) = true; // mark that this edge will now be processed
// Look for an edge on the forward CH graph (.forward)
EdgeID smaller_edge_id =
facade.FindSmallestEdge(std::get<0>(edge), std::get<1>(edge), [](const auto &data) {
return data.forward;
});
// If we didn't find one there, the we might be looking at a part of the path that
// was found using the backward search. Here, we flip the node order (.second,
// .first) and only consider edges with the `.backward` flag.
if (SPECIAL_EDGEID == smaller_edge_id)
{
smaller_edge_id =
facade.FindSmallestEdge(std::get<1>(edge),
std::get<0>(edge),
[](const auto &data) { return data.backward; });
}
// If we didn't find anything *still*, then something is broken and someone has
// called this function with bad values.
BOOST_ASSERT_MSG(smaller_edge_id != SPECIAL_EDGEID, "Invalid smaller edge ID");
const auto &data = facade.GetEdgeData(smaller_edge_id);
BOOST_ASSERT_MSG(data.weight != std::numeric_limits<EdgeWeight>::max(),
"edge weight invalid");
// If the edge is a shortcut, we need to add the two halfs to the stack.
if (data.shortcut)
{ // unpack
const NodeID middle_node_id = data.turn_id;
// Note the order here - we're adding these to a stack, so we
// want the first->middle to get visited before middle->second
recursion_stack.emplace(edge);
recursion_stack.emplace(middle_node_id, std::get<1>(edge), false);
recursion_stack.emplace(std::get<0>(edge), middle_node_id, false);
}
else
{
// compute the duration here and put it onto the duration stack using method
// similar to annotatePath but smaller
EdgeDistance distance = computeEdgeDistance(facade, std::get<0>(edge));
distance_stack.emplace(distance);
}
}
else
{ // the edge has already been processed. this means that there are enough values in the
// distances stack
BOOST_ASSERT_MSG(distance_stack.size() >= 2,
"There are not enough (at least 2) values on the distance stack");
EdgeDistance distance1 = distance_stack.top();
distance_stack.pop();
EdgeDistance distance2 = distance_stack.top();
distance_stack.pop();
EdgeDistance distance = distance1 + distance2;
distance_stack.emplace(distance);
}
}
EdgeDistance total_distance = 0;
while (!distance_stack.empty())
{
total_distance += distance_stack.top();
distance_stack.pop();
}
return total_distance;
}
template <typename RandomIter, typename FacadeT>
void unpackPath(const FacadeT &facade,
RandomIter packed_path_begin,
@@ -440,11 +340,6 @@ void retrievePackedPathFromSingleHeap(const SearchEngineData<Algorithm>::QueryHe
const NodeID middle_node_id,
std::vector<NodeID> &packed_path);
void retrievePackedPathFromSingleManyToManyHeap(
const SearchEngineData<Algorithm>::ManyToManyQueryHeap &search_heap,
const NodeID middle_node_id,
std::vector<NodeID> &packed_path);
// assumes that heaps are already setup correctly.
// ATTENTION: This only works if no additional offset is supplied next to the Phantom Node
// Offsets.
@@ -54,7 +54,7 @@ inline bool checkParentCellRestriction(CellID, const PhantomNodes &) { return tr
// Restricted search (Args is LevelID, CellID):
// * use the fixed level for queries
// * check if the node cell is the same as the specified parent
// * check if the node cell is the same as the specified parent onr
template <typename MultiLevelPartition>
inline LevelID getNodeQueryLevel(const MultiLevelPartition &, NodeID, LevelID level, CellID)
{
@@ -65,61 +65,6 @@ inline bool checkParentCellRestriction(CellID cell, LevelID, CellID parent)
{
return cell == parent;
}
// Unrestricted search with a single phantom node (Args is const PhantomNode &):
// * use partition.GetQueryLevel to find the node query level
// * allow to traverse all cells
template <typename MultiLevelPartition>
inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition,
const NodeID node,
const PhantomNode &phantom_node)
{
auto highest_diffrent_level = [&partition, node](const SegmentID &phantom_node) {
if (phantom_node.enabled)
return partition.GetHighestDifferentLevel(phantom_node.id, node);
return INVALID_LEVEL_ID;
};
const auto node_level = std::min(highest_diffrent_level(phantom_node.forward_segment_id),
highest_diffrent_level(phantom_node.reverse_segment_id));
return node_level;
}
// Unrestricted search with a single phantom node and a vector of phantom nodes:
// * use partition.GetQueryLevel to find the node query level
// * allow to traverse all cells
template <typename MultiLevelPartition>
inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition,
NodeID node,
const std::vector<PhantomNode> &phantom_nodes,
const std::size_t phantom_index,
const std::vector<std::size_t> &phantom_indices)
{
auto min_level = [&partition, node](const PhantomNode &phantom_node) {
const auto &forward_segment = phantom_node.forward_segment_id;
const auto forward_level =
forward_segment.enabled ? partition.GetHighestDifferentLevel(node, forward_segment.id)
: INVALID_LEVEL_ID;
const auto &reverse_segment = phantom_node.reverse_segment_id;
const auto reverse_level =
reverse_segment.enabled ? partition.GetHighestDifferentLevel(node, reverse_segment.id)
: INVALID_LEVEL_ID;
return std::min(forward_level, reverse_level);
};
// Get minimum level over all phantoms of the highest different level with respect to node
// This is equivalent to min_{∀ source, target} partition.GetQueryLevel(source, node, target)
auto result = min_level(phantom_nodes[phantom_index]);
for (const auto &index : phantom_indices)
{
result = std::min(result, min_level(phantom_nodes[index]));
}
return result;
}
}
// Heaps only record for each node its predecessor ("parent") on the shortest path.
@@ -129,46 +74,6 @@ inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition,
using PackedEdge = std::tuple</*from*/ NodeID, /*to*/ NodeID, /*from_clique_arc*/ bool>;
using PackedPath = std::vector<PackedEdge>;
template <bool DIRECTION, typename OutIter>
inline void retrievePackedPathFromSingleManyToManyHeap(
const SearchEngineData<Algorithm>::ManyToManyQueryHeap &heap, const NodeID middle, OutIter out)
{
NodeID current = middle;
NodeID parent = heap.GetData(current).parent;
while (current != parent)
{
const auto &data = heap.GetData(current);
if (DIRECTION == FORWARD_DIRECTION)
{
*out = std::make_tuple(parent, current, data.from_clique_arc);
++out;
}
else if (DIRECTION == REVERSE_DIRECTION)
{
*out = std::make_tuple(current, parent, data.from_clique_arc);
++out;
}
current = parent;
parent = heap.GetData(parent).parent;
}
}
template <bool DIRECTION>
inline PackedPath retrievePackedPathFromSingleManyToManyHeap(
const SearchEngineData<Algorithm>::ManyToManyQueryHeap &heap, const NodeID middle)
{
PackedPath packed_path;
retrievePackedPathFromSingleManyToManyHeap<DIRECTION>(
heap, middle, std::back_inserter(packed_path));
return packed_path;
}
template <bool DIRECTION, typename OutIter>
inline void retrievePackedPathFromSingleHeap(const SearchEngineData<Algorithm>::QueryHeap &heap,
const NodeID middle,
@@ -301,22 +206,15 @@ void relaxOutgoingEdges(const DataFacade<Algorithm> &facade,
for (const auto edge : facade.GetBorderEdgeRange(level, node))
{
const auto &edge_data = facade.GetEdgeData(edge);
if ((DIRECTION == FORWARD_DIRECTION) ? facade.IsForwardEdge(edge)
: facade.IsBackwardEdge(edge))
if (DIRECTION == FORWARD_DIRECTION ? edge_data.forward : edge_data.backward)
{
const NodeID to = facade.GetTarget(edge);
if (!facade.ExcludeNode(to) &&
checkParentCellRestriction(partition.GetCell(level + 1, to), args...))
{
const auto node_weight =
facade.GetNodeWeight(DIRECTION == FORWARD_DIRECTION ? node : to);
const auto turn_penalty = facade.GetWeightPenaltyForEdgeID(edge_data.turn_id);
// TODO: BOOST_ASSERT(edge_data.weight == node_weight + turn_penalty);
const EdgeWeight to_weight = weight + node_weight + turn_penalty;
BOOST_ASSERT_MSG(edge_data.weight > 0, "edge_weight invalid");
const EdgeWeight to_weight = weight + edge_data.weight;
if (!forward_heap.WasInserted(to))
{
@@ -509,90 +407,6 @@ UnpackedPath search(SearchEngineData<Algorithm> &engine_working_data,
return std::make_tuple(weight, std::move(unpacked_nodes), std::move(unpacked_edges));
}
// With (s, middle, t) we trace back the paths middle -> s and middle -> t.
// This gives us a packed path (node ids) from the base graph around s and t,
// and overlay node ids otherwise. We then have to unpack the overlay clique
// edges by recursively descending unpacking the path down to the base graph.
using UnpackedNodes = std::vector<NodeID>;
using UnpackedEdges = std::vector<EdgeID>;
using UnpackedPath = std::tuple<EdgeWeight, UnpackedNodes, UnpackedEdges>;
template <typename Algorithm, typename... Args>
UnpackedPath
unpackPathAndCalculateDistance(SearchEngineData<Algorithm> &engine_working_data,
const DataFacade<Algorithm> &facade,
typename SearchEngineData<Algorithm>::QueryHeap &forward_heap,
typename SearchEngineData<Algorithm>::QueryHeap &reverse_heap,
const bool force_loop_forward,
const bool force_loop_reverse,
EdgeWeight weight_upper_bound,
PackedPath packed_path,
NodeID middle,
Args... args)
{
EdgeWeight weight = weight_upper_bound;
const auto &partition = facade.GetMultiLevelPartition();
const NodeID source_node = !packed_path.empty() ? std::get<0>(packed_path.front()) : middle;
// Unpack path
std::vector<NodeID> unpacked_nodes;
std::vector<EdgeID> unpacked_edges;
unpacked_nodes.reserve(packed_path.size());
unpacked_edges.reserve(packed_path.size());
unpacked_nodes.push_back(source_node);
for (auto const &packed_edge : packed_path)
{
NodeID source, target;
bool overlay_edge;
std::tie(source, target, overlay_edge) = packed_edge;
if (!overlay_edge)
{ // a base graph edge
unpacked_nodes.push_back(target);
unpacked_edges.push_back(facade.FindEdge(source, target));
}
else
{ // an overlay graph edge
LevelID level = getNodeQueryLevel(partition, source, args...);
CellID parent_cell_id = partition.GetCell(level, source);
BOOST_ASSERT(parent_cell_id == partition.GetCell(level, target));
LevelID sublevel = level - 1;
// Here heaps can be reused, let's go deeper!
forward_heap.Clear();
reverse_heap.Clear();
forward_heap.Insert(source, 0, {source});
reverse_heap.Insert(target, 0, {target});
// TODO: when structured bindings will be allowed change to
// auto [subpath_weight, subpath_source, subpath_target, subpath] = ...
EdgeWeight subpath_weight;
std::vector<NodeID> subpath_nodes;
std::vector<EdgeID> subpath_edges;
std::tie(subpath_weight, subpath_nodes, subpath_edges) = search(engine_working_data,
facade,
forward_heap,
reverse_heap,
force_loop_forward,
force_loop_reverse,
weight_upper_bound,
sublevel,
parent_cell_id);
BOOST_ASSERT(!subpath_edges.empty());
BOOST_ASSERT(subpath_nodes.size() > 1);
BOOST_ASSERT(subpath_nodes.front() == source);
BOOST_ASSERT(subpath_nodes.back() == target);
unpacked_nodes.insert(
unpacked_nodes.end(), std::next(subpath_nodes.begin()), subpath_nodes.end());
unpacked_edges.insert(unpacked_edges.end(), subpath_edges.begin(), subpath_edges.end());
}
}
return std::make_tuple(weight, std::move(unpacked_nodes), std::move(unpacked_edges));
}
// Alias to be compatible with the CH-based search
template <typename Algorithm>
inline void search(SearchEngineData<Algorithm> &engine_working_data,
@@ -91,7 +91,6 @@ class EdgeBasedGraphFactory
void GetEdgeBasedNodeSegments(std::vector<EdgeBasedNodeSegment> &nodes);
void GetStartPointMarkers(std::vector<bool> &node_is_startpoint);
void GetEdgeBasedNodeWeights(std::vector<EdgeWeight> &output_node_weights);
void GetEdgeBasedNodeDurations(std::vector<EdgeWeight> &output_node_durations);
std::uint32_t GetConnectivityChecksum() const;
std::uint64_t GetNumberOfEdgeBasedNodes() const;
@@ -118,7 +117,6 @@ class EdgeBasedGraphFactory
//! node weights that indicate the length of the segment (node based) represented by the
//! edge-based node
std::vector<EdgeWeight> m_edge_based_node_weights;
std::vector<EdgeDuration> m_edge_based_node_durations;
//! list of edge based nodes (compressed segments)
std::vector<EdgeBasedNodeSegment> m_edge_based_node_segments;
-1
View File
@@ -87,7 +87,6 @@ class Extractor
std::vector<EdgeBasedNodeSegment> &edge_based_node_segments,
std::vector<bool> &node_is_startpoint,
std::vector<EdgeWeight> &edge_based_node_weights,
std::vector<EdgeDuration> &edge_based_node_durations,
util::DeallocatingVector<EdgeBasedEdge> &edge_based_edge_list,
std::uint32_t &connectivity_checksum);
+3 -17
View File
@@ -462,28 +462,14 @@ void readEdgeBasedNodeWeights(const boost::filesystem::path &path, NodeWeigtsVec
storage::serialization::read(reader, "/extractor/edge_based_node_weights", weights);
}
template <typename NodeWeigtsVectorT, typename NodeDurationsVectorT>
void readEdgeBasedNodeWeightsDurations(const boost::filesystem::path &path,
NodeWeigtsVectorT &weights,
NodeDurationsVectorT &durations)
{
const auto fingerprint = storage::tar::FileReader::VerifyFingerprint;
storage::tar::FileReader reader{path, fingerprint};
storage::serialization::read(reader, "/extractor/edge_based_node_weights", weights);
storage::serialization::read(reader, "/extractor/edge_based_node_durations", durations);
}
template <typename NodeWeigtsVectorT, typename NodeDurationsVectorT>
void writeEdgeBasedNodeWeightsDurations(const boost::filesystem::path &path,
const NodeWeigtsVectorT &weights,
const NodeDurationsVectorT &durations)
template <typename NodeWeigtsVectorT>
void writeEdgeBasedNodeWeights(const boost::filesystem::path &path,
const NodeWeigtsVectorT &weights)
{
const auto fingerprint = storage::tar::FileWriter::GenerateFingerprint;
storage::tar::FileWriter writer{path, fingerprint};
storage::serialization::write(writer, "/extractor/edge_based_node_weights", weights);
storage::serialization::write(writer, "/extractor/edge_based_node_durations", durations);
}
template <typename RTreeT>
-40
View File
@@ -1064,46 +1064,6 @@ argumentsToTableParameter(const Nan::FunctionCallbackInfo<v8::Value> &args,
}
}
if (obj->Has(Nan::New("annotations").ToLocalChecked()))
{
v8::Local<v8::Value> annotations = obj->Get(Nan::New("annotations").ToLocalChecked());
if (annotations.IsEmpty())
return table_parameters_ptr();
if (!annotations->IsArray())
{
Nan::ThrowError(
"Annotations must an array containing 'duration' or 'distance', or both");
return table_parameters_ptr();
}
params->annotations = osrm::TableParameters::AnnotationsType::None;
v8::Local<v8::Array> annotations_array = v8::Local<v8::Array>::Cast(annotations);
for (std::size_t i = 0; i < annotations_array->Length(); ++i)
{
const Nan::Utf8String annotations_utf8str(annotations_array->Get(i));
std::string annotations_str{*annotations_utf8str,
*annotations_utf8str + annotations_utf8str.length()};
if (annotations_str == "duration")
{
params->annotations =
params->annotations | osrm::TableParameters::AnnotationsType::Duration;
}
else if (annotations_str == "distance")
{
params->annotations =
params->annotations | osrm::TableParameters::AnnotationsType::Distance;
}
else
{
Nan::ThrowError("this 'annotations' param is not supported");
return table_parameters_ptr();
}
}
}
return params;
}
+35 -29
View File
@@ -1,6 +1,8 @@
#ifndef OSRM_PARTITIONER_SERILIZATION_HPP
#define OSRM_PARTITIONER_SERILIZATION_HPP
#include "customizer/edge_based_graph.hpp"
#include "partitioner/serialization.hpp"
#include "storage/io.hpp"
@@ -12,6 +14,39 @@ namespace partitioner
namespace files
{
// reads .osrm.mldgr file
template <typename MultiLevelGraphT>
inline void readGraph(const boost::filesystem::path &path,
MultiLevelGraphT &graph,
std::uint32_t &connectivity_checksum)
{
static_assert(std::is_same<customizer::MultiLevelEdgeBasedGraphView, MultiLevelGraphT>::value ||
std::is_same<customizer::MultiLevelEdgeBasedGraph, MultiLevelGraphT>::value,
"");
storage::tar::FileReader reader{path, storage::tar::FileReader::VerifyFingerprint};
reader.ReadInto("/mld/connectivity_checksum", connectivity_checksum);
serialization::read(reader, "/mld/multilevelgraph", graph);
}
// writes .osrm.mldgr file
template <typename MultiLevelGraphT>
inline void writeGraph(const boost::filesystem::path &path,
const MultiLevelGraphT &graph,
const std::uint32_t connectivity_checksum)
{
static_assert(std::is_same<customizer::MultiLevelEdgeBasedGraphView, MultiLevelGraphT>::value ||
std::is_same<customizer::MultiLevelEdgeBasedGraph, MultiLevelGraphT>::value,
"");
storage::tar::FileWriter writer{path, storage::tar::FileWriter::GenerateFingerprint};
writer.WriteElementCount64("/mld/connectivity_checksum", 1);
writer.WriteFrom("/mld/connectivity_checksum", connectivity_checksum);
serialization::write(writer, "/mld/multilevelgraph", graph);
}
// read .osrm.partition file
template <typename MultiLevelPartitionT>
inline void readPartition(const boost::filesystem::path &path, MultiLevelPartitionT &mlp)
@@ -67,35 +102,6 @@ inline void writeCells(const boost::filesystem::path &path, CellStorageT &storag
serialization::write(writer, "/mld/cellstorage", storage);
}
// reads .osrm.mldgr file
template <typename MultiLevelGraphT>
inline void readGraph(const boost::filesystem::path &path,
MultiLevelGraphT &graph,
std::uint32_t &connectivity_checksum)
{
static_assert(std::is_same<partitioner::MultiLevelEdgeBasedGraph, MultiLevelGraphT>::value, "");
storage::tar::FileReader reader{path, storage::tar::FileReader::VerifyFingerprint};
reader.ReadInto("/mld/connectivity_checksum", connectivity_checksum);
serialization::read(reader, "/mld/multilevelgraph", graph);
}
// writes .osrm.mldgr file
template <typename MultiLevelGraphT>
inline void writeGraph(const boost::filesystem::path &path,
const MultiLevelGraphT &graph,
const std::uint32_t connectivity_checksum)
{
static_assert(std::is_same<partitioner::MultiLevelEdgeBasedGraph, MultiLevelGraphT>::value, "");
storage::tar::FileWriter writer{path, storage::tar::FileWriter::GenerateFingerprint};
writer.WriteElementCount64("/mld/connectivity_checksum", 1);
writer.WriteFrom("/mld/connectivity_checksum", connectivity_checksum);
serialization::write(writer, "/mld/multilevelgraph", graph);
}
}
}
}
-15
View File
@@ -1,7 +1,6 @@
#ifndef OSRM_PARTITIONER_MULTI_LEVEL_GRAPH_HPP
#define OSRM_PARTITIONER_MULTI_LEVEL_GRAPH_HPP
#include "partitioner/edge_based_graph.hpp"
#include "partitioner/multi_level_partition.hpp"
#include "storage/shared_memory_ownership.hpp"
@@ -43,8 +42,6 @@ class MultiLevelGraph : public util::StaticGraph<EdgeDataT, Ownership>
template <typename T> using Vector = util::ViewOrVector<T, Ownership>;
public:
using SuperT::SuperT;
// We limit each node to have 255 edges
// this is very generous, we could probably pack this
using EdgeOffset = std::uint8_t;
@@ -149,14 +146,6 @@ class MultiLevelGraph : public util::StaticGraph<EdgeDataT, Ownership>
return max_border_node_id;
}
auto data() && // rvalue ref-qualifier is a safety-belt
{
return std::make_tuple(std::move(SuperT::node_array),
std::move(SuperT::edge_array),
std::move(node_to_edge_offset),
connectivity_checksum);
}
private:
template <typename ContainerT>
auto GetHighestBorderLevel(const MultiLevelPartition &mlp, const ContainerT &edges) const
@@ -229,13 +218,9 @@ class MultiLevelGraph : public util::StaticGraph<EdgeDataT, Ownership>
const std::string &name,
const MultiLevelGraph<EdgeDataT, Ownership> &graph);
protected:
Vector<EdgeOffset> node_to_edge_offset;
std::uint32_t connectivity_checksum;
};
using MultiLevelEdgeBasedGraph =
MultiLevelGraph<EdgeBasedGraphEdgeData, storage::Ownership::Container>;
}
}
+1 -1
View File
@@ -16,7 +16,7 @@ namespace partitioner
struct PartitionerConfig final : storage::IOConfig
{
PartitionerConfig()
: IOConfig({".osrm", ".osrm.fileIndex", ".osrm.ebg_nodes", ".osrm.enw"},
: IOConfig({".osrm", ".osrm.fileIndex", ".osrm.ebg_nodes"},
{".osrm.hsgr", ".osrm.cnbg"},
{".osrm.ebg",
".osrm.cnbg",
+20
View File
@@ -19,6 +19,26 @@ namespace partitioner
namespace serialization
{
template <typename EdgeDataT, storage::Ownership Ownership>
inline void read(storage::tar::FileReader &reader,
const std::string &name,
MultiLevelGraph<EdgeDataT, Ownership> &graph)
{
storage::serialization::read(reader, name + "/node_array", graph.node_array);
storage::serialization::read(reader, name + "/edge_array", graph.edge_array);
storage::serialization::read(reader, name + "/node_to_edge_offset", graph.node_to_edge_offset);
}
template <typename EdgeDataT, storage::Ownership Ownership>
inline void write(storage::tar::FileWriter &writer,
const std::string &name,
const MultiLevelGraph<EdgeDataT, Ownership> &graph)
{
storage::serialization::write(writer, name + "/node_array", graph.node_array);
storage::serialization::write(writer, name + "/edge_array", graph.edge_array);
storage::serialization::write(writer, name + "/node_to_edge_offset", graph.node_to_edge_offset);
}
template <storage::Ownership Ownership>
inline void read(storage::tar::FileReader &reader,
const std::string &name,
+3 -23
View File
@@ -22,11 +22,11 @@ namespace qi = boost::spirit::qi;
template <typename Iterator = std::string::iterator,
typename Signature = void(engine::api::TableParameters &)>
struct TableParametersGrammar : public BaseParametersGrammar<Iterator, Signature>
struct TableParametersGrammar final : public BaseParametersGrammar<Iterator, Signature>
{
using BaseGrammar = BaseParametersGrammar<Iterator, Signature>;
TableParametersGrammar() : TableParametersGrammar(root_rule)
TableParametersGrammar() : BaseGrammar(root_rule)
{
#ifdef BOOST_HAS_LONG_LONG
if (std::is_same<std::size_t, unsigned long long>::value)
@@ -51,35 +51,15 @@ struct TableParametersGrammar : public BaseParametersGrammar<Iterator, Signature
table_rule = destinations_rule(qi::_r1) | sources_rule(qi::_r1);
root_rule = BaseGrammar::query_rule(qi::_r1) > -qi::lit(".json") >
-('?' > (table_rule(qi::_r1) | base_rule(qi::_r1)) % '&');
-('?' > (table_rule(qi::_r1) | BaseGrammar::base_rule(qi::_r1)) % '&');
}
TableParametersGrammar(qi::rule<Iterator, Signature> &root_rule_) : BaseGrammar(root_rule_)
{
using AnnotationsType = engine::api::TableParameters::AnnotationsType;
annotations.add("duration", AnnotationsType::Duration)("distance",
AnnotationsType::Distance);
annotations_list = annotations[qi::_val |= qi::_1] % ',';
base_rule = BaseGrammar::base_rule(qi::_r1) |
(qi::lit("annotations=") >
annotations_list[ph::bind(&engine::api::TableParameters::annotations,
qi::_r1) = qi::_1]);
}
protected:
qi::rule<Iterator, Signature> base_rule;
private:
qi::rule<Iterator, Signature> root_rule;
qi::rule<Iterator, Signature> table_rule;
qi::rule<Iterator, Signature> sources_rule;
qi::rule<Iterator, Signature> destinations_rule;
qi::rule<Iterator, std::size_t()> size_t_;
qi::symbols<char, engine::api::TableParameters::AnnotationsType> annotations;
qi::rule<Iterator, engine::api::TableParameters::AnnotationsType()> annotations_list;
};
}
}
+8 -14
View File
@@ -46,39 +46,33 @@ class SharedDataIndex
template <typename T> auto GetBlockPtr(const std::string &name) const
{
const auto &region = GetBlockRegion(name);
const auto index_iter = block_to_region.find(name);
const auto &region = regions[index_iter->second];
return region.layout.GetBlockPtr<T>(region.memory_ptr, name);
}
template <typename T> auto GetBlockPtr(const std::string &name)
{
const auto &region = GetBlockRegion(name);
const auto index_iter = block_to_region.find(name);
const auto &region = regions[index_iter->second];
return region.layout.GetBlockPtr<T>(region.memory_ptr, name);
}
std::size_t GetBlockEntries(const std::string &name) const
{
const auto &region = GetBlockRegion(name);
const auto index_iter = block_to_region.find(name);
const auto &region = regions[index_iter->second];
return region.layout.GetBlockEntries(name);
}
std::size_t GetBlockSize(const std::string &name) const
{
const auto &region = GetBlockRegion(name);
const auto index_iter = block_to_region.find(name);
const auto &region = regions[index_iter->second];
return region.layout.GetBlockSize(name);
}
private:
const AllocatedRegion &GetBlockRegion(const std::string &name) const
{
const auto index_iter = block_to_region.find(name);
if (index_iter == block_to_region.end())
{
throw util::exception("data block " + name + " not found " + SOURCE_REF);
}
return regions[index_iter->second];
}
std::vector<AllocatedRegion> regions;
std::unordered_map<std::string, std::uint32_t> block_to_region;
};
-13
View File
@@ -117,19 +117,6 @@ template <typename Data> struct SharedMonitor
#endif
static void remove() { bi::shared_memory_object::remove(Data::name); }
static bool exists()
{
try
{
bi::shared_memory_object shmem_open =
bi::shared_memory_object(bi::open_only, Data::name, bi::read_only);
}
catch (const bi::interprocess_exception &exception)
{
return false;
}
return true;
}
private:
#if USE_BOOST_INTERPROCESS_CONDITION
+4 -2
View File
@@ -29,8 +29,10 @@ checkMTarError(int error_code, const boost::filesystem::path &filepath, const st
case MTAR_ESUCCESS:
return;
case MTAR_EFAILURE:
throw util::RuntimeError(
filepath.string() + " : " + name, ErrorCode::FileIOError, SOURCE_REF);
throw util::RuntimeError(filepath.string() + " : " + name,
ErrorCode::FileIOError,
SOURCE_REF,
std::strerror(errno));
case MTAR_EOPENFAIL:
throw util::RuntimeError(filepath.string() + " : " + name,
ErrorCode::FileOpenError,
+2 -11
View File
@@ -330,18 +330,9 @@ inline auto make_multi_level_graph_view(const SharedDataIndex &index, const std:
index, name + "/edge_array");
auto node_to_offset = make_vector_view<customizer::MultiLevelEdgeBasedGraphView::EdgeOffset>(
index, name + "/node_to_edge_offset");
auto node_weights = make_vector_view<EdgeWeight>(index, name + "/node_weights");
auto node_durations = make_vector_view<EdgeDuration>(index, name + "/node_durations");
auto is_forward_edge = make_vector_view<bool>(index, name + "/is_forward_edge");
auto is_backward_edge = make_vector_view<bool>(index, name + "/is_backward_edge");
return customizer::MultiLevelEdgeBasedGraphView(std::move(node_list),
std::move(edge_list),
std::move(node_to_offset),
std::move(node_weights),
std::move(node_durations),
std::move(is_forward_edge),
std::move(is_backward_edge));
return customizer::MultiLevelEdgeBasedGraphView(
std::move(node_list), std::move(edge_list), std::move(node_to_offset));
}
inline auto make_maneuver_overrides_views(const SharedDataIndex &index, const std::string &name)
+3 -5
View File
@@ -17,15 +17,13 @@ class Updater
public:
Updater(UpdaterConfig config_) : config(std::move(config_)) {}
EdgeID
LoadAndUpdateEdgeExpandedGraph(std::vector<extractor::EdgeBasedEdge> &edge_based_edge_list,
std::vector<EdgeWeight> &node_weights,
std::uint32_t &connectivity_checksum) const;
using NumNodesAndEdges =
std::tuple<EdgeID, std::vector<extractor::EdgeBasedEdge>, std::uint32_t>;
NumNodesAndEdges LoadAndUpdateEdgeExpandedGraph() const;
EdgeID
LoadAndUpdateEdgeExpandedGraph(std::vector<extractor::EdgeBasedEdge> &edge_based_edge_list,
std::vector<EdgeWeight> &node_weights,
std::vector<EdgeDuration> &node_durations, // TODO: to be deleted
std::uint32_t &connectivity_checksum) const;
private:
+1 -2
View File
@@ -53,8 +53,7 @@ struct UpdaterConfig final : storage::IOConfig
".osrm.geometry",
".osrm.fileIndex",
".osrm.properties",
".osrm.restrictions",
".osrm.enw"},
".osrm.restrictions"},
{},
{".osrm.datasource_names"}),
valid_now(0)
-3
View File
@@ -43,9 +43,6 @@ inline double radToDeg(const double radian)
//! Takes the squared euclidean distance of the input coordinates. Does not return meters!
std::uint64_t squaredEuclideanDistance(const Coordinate lhs, const Coordinate rhs);
double fccApproximateDistance(const Coordinate first_coordinate,
const Coordinate second_coordinate);
double haversineDistance(const Coordinate first_coordinate, const Coordinate second_coordinate);
double greatCircleDistance(const Coordinate first_coordinate, const Coordinate second_coordinate);
+1 -1
View File
@@ -22,7 +22,7 @@ namespace util
inline std::ostream &operator<<(std::ostream &out, const Coordinate &coordinate)
{
out << std::setprecision(12) << "{" << toFloating(coordinate.lon) << ", "
<< toFloating(coordinate.lat) << "}";
<< toFloating(coordinate.lon) << "}";
return out;
}
}
+1 -8
View File
@@ -2,8 +2,6 @@
#define DYNAMICGRAPH_HPP
#include "util/deallocating_vector.hpp"
#include "util/exception.hpp"
#include "util/exception_utils.hpp"
#include "util/integer_range.hpp"
#include "util/permutation.hpp"
#include "util/typedefs.hpp"
@@ -413,12 +411,7 @@ template <typename EdgeDataT> class DynamicGraph
util::inplacePermutation(node_array.begin(), node_array.end(), old_to_new_node);
// Build up edge permutation
if (edge_list.size() >= std::numeric_limits<EdgeID>::max())
{
throw util::exception("There are too many edges, OSRM only supports 2^32" + SOURCE_REF);
}
EdgeID new_edge_index = 0;
auto new_edge_index = 0;
std::vector<EdgeID> old_to_new_edge(edge_list.size(), SPECIAL_EDGEID);
for (auto node : util::irange<NodeID>(0, number_of_nodes))
{
-2
View File
@@ -226,14 +226,12 @@ class QueryHeap
Data &GetData(NodeID node)
{
const auto index = node_index.peek_index(node);
BOOST_ASSERT((int)index >= 0 && (int)index < (int)inserted_nodes.size());
return inserted_nodes[index].data;
}
Data const &GetData(NodeID node) const
{
const auto index = node_index.peek_index(node);
BOOST_ASSERT((int)index >= 0 && (int)index < (int)inserted_nodes.size());
return inserted_nodes[index].data;
}
+1 -1
View File
@@ -312,7 +312,7 @@ class StaticGraph
});
}
protected:
// private:
NodeIterator number_of_nodes;
EdgeIterator number_of_edges;
-2
View File
@@ -75,7 +75,6 @@ using NameID = std::uint32_t;
using AnnotationID = std::uint32_t;
using EdgeWeight = std::int32_t;
using EdgeDuration = std::int32_t;
using EdgeDistance = float;
using SegmentWeight = std::uint32_t;
using SegmentDuration = std::uint32_t;
using TurnPenalty = std::int16_t; // turn penalty in 100ms units
@@ -114,7 +113,6 @@ static const SegmentDuration MAX_SEGMENT_DURATION = INVALID_SEGMENT_DURATION - 1
static const EdgeWeight INVALID_EDGE_WEIGHT = std::numeric_limits<EdgeWeight>::max();
static const EdgeDuration MAXIMAL_EDGE_DURATION = std::numeric_limits<EdgeDuration>::max();
static const TurnPenalty INVALID_TURN_PENALTY = std::numeric_limits<TurnPenalty>::max();
static const EdgeDistance INVALID_EDGE_DISTANCE = std::numeric_limits<EdgeDistance>::max();
// FIXME the bitfields we use require a reduced maximal duration, this should be kept consistent
// within the code base. For now we have to ensure that we don't case 30 bit to -1 and break any
+25
View File
@@ -9,6 +9,31 @@ namespace util
{
namespace vector_tile
{
const constexpr std::uint32_t ID_TAG = 1;
const constexpr std::uint32_t NAME_TAG = 1;
const constexpr std::uint32_t FEATURE_TAG = 2;
const constexpr std::uint32_t LAYER_TAG = 3;
const constexpr std::uint32_t GEOMETRY_TAG = 3;
const constexpr std::uint32_t KEY_TAG = 3;
const constexpr std::uint32_t VARIANT_TAG = 4;
const constexpr std::uint32_t EXTENT_TAG = 5;
const constexpr std::uint32_t VERSION_TAG = 15;
const constexpr std::uint32_t FEATURE_ATTRIBUTES_TAG = 2;
const constexpr std::uint32_t FEATURE_GEOMETRIES_TAG = 4;
const constexpr std::uint32_t GEOMETRY_TYPE_POINT = 1;
const constexpr std::uint32_t GEOMETRY_TYPE_LINE = 2;
const constexpr std::uint32_t VARIANT_TYPE_STRING = 1;
const constexpr std::uint32_t VARIANT_TYPE_FLOAT = 2;
const constexpr std::uint32_t VARIANT_TYPE_DOUBLE = 3;
const constexpr std::uint32_t VARIANT_TYPE_UINT64 = 5;
const constexpr std::uint32_t VARIANT_TYPE_SINT64 = 6;
const constexpr std::uint32_t VARIANT_TYPE_BOOL = 7;
// Vector tiles are 4096 virtual pixels on each side
const constexpr double EXTENT = 4096.0;
const constexpr double BUFFER = 128.0;
+1 -7
View File
@@ -1,6 +1,6 @@
{
"name": "osrm",
"version": "5.18.0",
"version": "5.17.0-rc.1",
"private": false,
"description": "The Open Source Routing Machine is a high performance routing engine written in C++14 designed to run on OpenStreetMap data.",
"dependencies": {
@@ -38,25 +38,19 @@
"node": ">=4.0.0"
},
"devDependencies": {
"ansi-escape-sequences": "^4.0.0",
"aws-sdk": "~2.0.31",
"babel-plugin-transform-class-properties": "^6.24.1",
"chalk": "^1.1.3",
"command-line-args": "^5.0.2",
"command-line-usage": "^5.0.4",
"csv-stringify": "^3.0.0",
"cucumber": "^1.2.1",
"d3-queue": "^2.0.3",
"docbox": "^1.0.6",
"documentation": "^4.0.0-rc.1",
"eslint": "^2.4.0",
"faucet": "^0.0.1",
"jsonpath": "^1.0.0",
"node-timeout": "0.0.4",
"polyline": "^0.2.0",
"request": "^2.69.0",
"tape": "^4.7.0",
"turf": "^3.0.14",
"xmlbuilder": "^4.2.1"
},
"bundleDependencies": [
+146 -237
View File
@@ -109,11 +109,11 @@ function setup()
-- reduce the driving speed by 30% for unsafe roads
-- only used for cyclability metric
unsafe_highway_list = {
primary = 0.5,
secondary = 0.65,
primary = 0.7,
secondary = 0.75,
tertiary = 0.8,
primary_link = 0.5,
secondary_link = 0.65,
primary_link = 0.7,
secondary_link = 0.75,
tertiary_link = 0.8,
},
@@ -193,16 +193,6 @@ function setup()
sett = 10
},
classes = Sequence {
'ferry', 'tunnel'
},
-- Which classes should be excludable
-- This increases memory usage so its disabled by default.
excludable = Sequence {
-- Set {'ferry'}
},
tracktype_speeds = {
},
@@ -260,237 +250,204 @@ end
function handle_bicycle_tags(profile,way,result,data)
-- initial routability check, filters out buildings, boundaries, etc
data.route = way:get_value_by_key("route")
data.man_made = way:get_value_by_key("man_made")
data.railway = way:get_value_by_key("railway")
data.amenity = way:get_value_by_key("amenity")
data.public_transport = way:get_value_by_key("public_transport")
data.bridge = way:get_value_by_key("bridge")
local route = way:get_value_by_key("route")
local man_made = way:get_value_by_key("man_made")
local railway = way:get_value_by_key("railway")
local amenity = way:get_value_by_key("amenity")
local public_transport = way:get_value_by_key("public_transport")
local bridge = way:get_value_by_key("bridge")
if (not data.highway or data.highway == '') and
(not data.route or data.route == '') and
(not profile.use_public_transport or not data.railway or data.railway=='') and
(not data.amenity or data.amenity=='') and
(not data.man_made or data.man_made=='') and
(not data.public_transport or data.public_transport=='') and
(not data.bridge or data.bridge=='')
(not route or route == '') and
(not profile.use_public_transport or not railway or railway=='') and
(not amenity or amenity=='') and
(not man_made or man_made=='') and
(not public_transport or public_transport=='') and
(not bridge or bridge=='')
then
return false
end
-- access
data.access = find_access_tag(way, profile.access_tags_hierarchy)
if data.access and profile.access_tag_blacklist[data.access] then
local access = find_access_tag(way, profile.access_tags_hierarchy)
if access and profile.access_tag_blacklist[access] then
return false
end
-- other tags
data.junction = way:get_value_by_key("junction")
data.maxspeed = parse_maxspeed(way:get_value_by_key ( "maxspeed") )
data.maxspeed_forward = parse_maxspeed(way:get_value_by_key( "maxspeed:forward"))
data.maxspeed_backward = parse_maxspeed(way:get_value_by_key( "maxspeed:backward"))
data.barrier = way:get_value_by_key("barrier")
data.oneway = way:get_value_by_key("oneway")
data.oneway_bicycle = way:get_value_by_key("oneway:bicycle")
data.cycleway = way:get_value_by_key("cycleway")
data.cycleway_left = way:get_value_by_key("cycleway:left")
data.cycleway_right = way:get_value_by_key("cycleway:right")
data.duration = way:get_value_by_key("duration")
data.service = way:get_value_by_key("service")
data.foot = way:get_value_by_key("foot")
data.foot_forward = way:get_value_by_key("foot:forward")
data.foot_backward = way:get_value_by_key("foot:backward")
data.bicycle = way:get_value_by_key("bicycle")
speed_handler(profile,way,result,data)
oneway_handler(profile,way,result,data)
cycleway_handler(profile,way,result,data)
bike_push_handler(profile,way,result,data)
local junction = way:get_value_by_key("junction")
local maxspeed = parse_maxspeed(way:get_value_by_key ( "maxspeed") )
local maxspeed_forward = parse_maxspeed(way:get_value_by_key( "maxspeed:forward"))
local maxspeed_backward = parse_maxspeed(way:get_value_by_key( "maxspeed:backward"))
local barrier = way:get_value_by_key("barrier")
local oneway = way:get_value_by_key("oneway")
local oneway_bicycle = way:get_value_by_key("oneway:bicycle")
local cycleway = way:get_value_by_key("cycleway")
local cycleway_left = way:get_value_by_key("cycleway:left")
local cycleway_right = way:get_value_by_key("cycleway:right")
local duration = way:get_value_by_key("duration")
local service = way:get_value_by_key("service")
local foot = way:get_value_by_key("foot")
local foot_forward = way:get_value_by_key("foot:forward")
local foot_backward = way:get_value_by_key("foot:backward")
local bicycle = way:get_value_by_key("bicycle")
-- maxspeed
limit( result, data.maxspeed, data.maxspeed_forward, data.maxspeed_backward )
-- not routable if no speed assigned
-- this avoid assertions in debug builds
if result.forward_speed <= 0 and result.duration <= 0 then
result.forward_mode = mode.inaccessible
end
if result.backward_speed <= 0 and result.duration <= 0 then
result.backward_mode = mode.inaccessible
end
safety_handler(profile,way,result,data)
end
function speed_handler(profile,way,result,data)
data.way_type_allows_pushing = false
local way_type_allows_pushing = false
-- speed
local bridge_speed = profile.bridge_speeds[data.bridge]
local bridge_speed = profile.bridge_speeds[bridge]
if (bridge_speed and bridge_speed > 0) then
data.highway = data.bridge
if data.duration and durationIsValid(data.duration) then
result.duration = math.max( parseDuration(data.duration), 1 )
data.highway = bridge
if duration and durationIsValid(duration) then
result.duration = math.max( parseDuration(duration), 1 )
end
result.forward_speed = bridge_speed
result.backward_speed = bridge_speed
data.way_type_allows_pushing = true
elseif profile.route_speeds[data.route] then
way_type_allows_pushing = true
elseif profile.route_speeds[route] then
-- ferries (doesn't cover routes tagged using relations)
result.forward_mode = mode.ferry
result.backward_mode = mode.ferry
if data.duration and durationIsValid(data.duration) then
result.duration = math.max( 1, parseDuration(data.duration) )
if duration and durationIsValid(duration) then
result.duration = math.max( 1, parseDuration(duration) )
else
result.forward_speed = profile.route_speeds[data.route]
result.backward_speed = profile.route_speeds[data.route]
result.forward_speed = profile.route_speeds[route]
result.backward_speed = profile.route_speeds[route]
end
-- railway platforms (old tagging scheme)
elseif data.railway and profile.platform_speeds[data.railway] then
result.forward_speed = profile.platform_speeds[data.railway]
result.backward_speed = profile.platform_speeds[data.railway]
data.way_type_allows_pushing = true
elseif railway and profile.platform_speeds[railway] then
result.forward_speed = profile.platform_speeds[railway]
result.backward_speed = profile.platform_speeds[railway]
way_type_allows_pushing = true
-- public_transport platforms (new tagging platform)
elseif data.public_transport and profile.platform_speeds[data.public_transport] then
result.forward_speed = profile.platform_speeds[data.public_transport]
result.backward_speed = profile.platform_speeds[data.public_transport]
data.way_type_allows_pushing = true
elseif public_transport and profile.platform_speeds[public_transport] then
result.forward_speed = profile.platform_speeds[public_transport]
result.backward_speed = profile.platform_speeds[public_transport]
way_type_allows_pushing = true
-- railways
elseif profile.use_public_transport and data.railway and profile.railway_speeds[data.railway] and profile.access_tag_whitelist[data.access] then
elseif profile.use_public_transport and railway and profile.railway_speeds[railway] and profile.access_tag_whitelist[access] then
result.forward_mode = mode.train
result.backward_mode = mode.train
result.forward_speed = profile.railway_speeds[data.railway]
result.backward_speed = profile.railway_speeds[data.railway]
elseif data.amenity and profile.amenity_speeds[data.amenity] then
result.forward_speed = profile.railway_speeds[railway]
result.backward_speed = profile.railway_speeds[railway]
elseif amenity and profile.amenity_speeds[amenity] then
-- parking areas
result.forward_speed = profile.amenity_speeds[data.amenity]
result.backward_speed = profile.amenity_speeds[data.amenity]
data.way_type_allows_pushing = true
result.forward_speed = profile.amenity_speeds[amenity]
result.backward_speed = profile.amenity_speeds[amenity]
way_type_allows_pushing = true
elseif profile.bicycle_speeds[data.highway] then
-- regular ways
result.forward_speed = profile.bicycle_speeds[data.highway]
result.backward_speed = profile.bicycle_speeds[data.highway]
data.way_type_allows_pushing = true
elseif data.access and profile.access_tag_whitelist[data.access] then
way_type_allows_pushing = true
elseif access and profile.access_tag_whitelist[access] then
-- unknown way, but valid access tag
result.forward_speed = profile.default_speed
result.backward_speed = profile.default_speed
data.way_type_allows_pushing = true
way_type_allows_pushing = true
end
end
function oneway_handler(profile,way,result,data)
-- oneway
data.implied_oneway = data.junction == "roundabout" or data.junction == "circular" or data.highway == "motorway"
data.reverse = false
local implied_oneway = junction == "roundabout" or junction == "circular" or data.highway == "motorway"
local reverse = false
if data.oneway_bicycle == "yes" or data.oneway_bicycle == "1" or data.oneway_bicycle == "true" then
if oneway_bicycle == "yes" or oneway_bicycle == "1" or oneway_bicycle == "true" then
result.backward_mode = mode.inaccessible
elseif data.oneway_bicycle == "no" or data.oneway_bicycle == "0" or data.oneway_bicycle == "false" then
elseif oneway_bicycle == "no" or oneway_bicycle == "0" or oneway_bicycle == "false" then
-- prevent other cases
elseif data.oneway_bicycle == "-1" then
elseif oneway_bicycle == "-1" then
result.forward_mode = mode.inaccessible
data.reverse = true
elseif data.oneway == "yes" or data.oneway == "1" or data.oneway == "true" then
reverse = true
elseif oneway == "yes" or oneway == "1" or oneway == "true" then
result.backward_mode = mode.inaccessible
elseif data.oneway == "no" or data.oneway == "0" or data.oneway == "false" then
elseif oneway == "no" or oneway == "0" or oneway == "false" then
-- prevent other cases
elseif data.oneway == "-1" then
elseif oneway == "-1" then
result.forward_mode = mode.inaccessible
data.reverse = true
elseif data.implied_oneway then
reverse = true
elseif implied_oneway then
result.backward_mode = mode.inaccessible
end
end
function cycleway_handler(profile,way,result,data)
-- cycleway
data.has_cycleway_forward = false
data.has_cycleway_backward = false
data.is_twoway = result.forward_mode ~= mode.inaccessible and result.backward_mode ~= mode.inaccessible and not data.implied_oneway
local has_cycleway_forward = false
local has_cycleway_backward = false
local is_oneway = result.forward_mode ~= mode.inaccessible and result.backward_mode ~= mode.inaccessible and not implied_oneway
-- cycleways on normal roads
if data.is_twoway then
if data.cycleway and profile.cycleway_tags[data.cycleway] then
data.has_cycleway_backward = true
data.has_cycleway_forward = true
if is_oneway then
if cycleway and profile.cycleway_tags[cycleway] then
has_cycleway_backward = true
has_cycleway_forward = true
end
if (data.cycleway_right and profile.cycleway_tags[data.cycleway_right]) or (data.cycleway_left and profile.opposite_cycleway_tags[data.cycleway_left]) then
data.has_cycleway_forward = true
if (cycleway_right and profile.cycleway_tags[cycleway_right]) or (cycleway_left and profile.opposite_cycleway_tags[cycleway_left]) then
has_cycleway_forward = true
end
if (data.cycleway_left and profile.cycleway_tags[data.cycleway_left]) or (data.cycleway_right and profile.opposite_cycleway_tags[data.cycleway_right]) then
data.has_cycleway_backward = true
if (cycleway_left and profile.cycleway_tags[cycleway_left]) or (cycleway_right and profile.opposite_cycleway_tags[cycleway_right]) then
has_cycleway_backward = true
end
else
local has_twoway_cycleway = (data.cycleway and profile.opposite_cycleway_tags[data.cycleway]) or (data.cycleway_right and profile.opposite_cycleway_tags[data.cycleway_right]) or (data.cycleway_left and profile.opposite_cycleway_tags[data.cycleway_left])
local has_opposite_cycleway = (data.cycleway_left and profile.opposite_cycleway_tags[data.cycleway_left]) or (data.cycleway_right and profile.opposite_cycleway_tags[data.cycleway_right])
local has_oneway_cycleway = (data.cycleway and profile.cycleway_tags[data.cycleway]) or (data.cycleway_right and profile.cycleway_tags[data.cycleway_right]) or (data.cycleway_left and profile.cycleway_tags[data.cycleway_left])
local has_twoway_cycleway = (cycleway and profile.opposite_cycleway_tags[cycleway]) or (cycleway_right and profile.opposite_cycleway_tags[cycleway_right]) or (cycleway_left and profile.opposite_cycleway_tags[cycleway_left])
local has_opposite_cycleway = (cycleway_left and profile.opposite_cycleway_tags[cycleway_left]) or (cycleway_right and profile.opposite_cycleway_tags[cycleway_right])
local has_oneway_cycleway = (cycleway and profile.cycleway_tags[cycleway]) or (cycleway_right and profile.cycleway_tags[cycleway_right]) or (cycleway_left and profile.cycleway_tags[cycleway_left])
-- set cycleway even though it is an one-way if opposite is tagged
if has_twoway_cycleway then
data.has_cycleway_backward = true
data.has_cycleway_forward = true
has_cycleway_backward = true
has_cycleway_forward = true
elseif has_opposite_cycleway then
if not data.reverse then
data.has_cycleway_backward = true
if not reverse then
has_cycleway_backward = true
else
data.has_cycleway_forward = true
has_cycleway_forward = true
end
elseif has_oneway_cycleway then
if not data.reverse then
data.has_cycleway_forward = true
if not reverse then
has_cycleway_forward = true
else
data.has_cycleway_backward = true
has_cycleway_backward = true
end
end
end
if data.has_cycleway_backward then
if has_cycleway_backward then
result.backward_mode = mode.cycling
result.backward_speed = profile.bicycle_speeds["cycleway"]
end
if data.has_cycleway_forward then
if has_cycleway_forward then
result.forward_mode = mode.cycling
result.forward_speed = profile.bicycle_speeds["cycleway"]
end
end
function bike_push_handler(profile,way,result,data)
-- pushing bikes - if no other mode found
if result.forward_mode == mode.inaccessible or result.backward_mode == mode.inaccessible or
result.forward_speed == -1 or result.backward_speed == -1 then
if data.foot ~= 'no' then
if foot ~= 'no' then
local push_forward_speed = nil
local push_backward_speed = nil
if profile.pedestrian_speeds[data.highway] then
push_forward_speed = profile.pedestrian_speeds[data.highway]
push_backward_speed = profile.pedestrian_speeds[data.highway]
elseif data.man_made and profile.man_made_speeds[data.man_made] then
push_forward_speed = profile.man_made_speeds[data.man_made]
push_backward_speed = profile.man_made_speeds[data.man_made]
elseif man_made and profile.man_made_speeds[man_made] then
push_forward_speed = profile.man_made_speeds[man_made]
push_backward_speed = profile.man_made_speeds[man_made]
else
if data.foot == 'yes' then
if foot == 'yes' then
push_forward_speed = profile.walking_speed
if not data.implied_oneway then
if not implied_oneway then
push_backward_speed = profile.walking_speed
end
elseif data.foot_forward == 'yes' then
elseif foot_forward == 'yes' then
push_forward_speed = profile.walking_speed
elseif data.foot_backward == 'yes' then
elseif foot_backward == 'yes' then
push_backward_speed = profile.walking_speed
elseif data.way_type_allows_pushing then
elseif way_type_allows_pushing then
push_forward_speed = profile.walking_speed
if not data.implied_oneway then
if not implied_oneway then
push_backward_speed = profile.walking_speed
end
end
@@ -509,77 +466,65 @@ function bike_push_handler(profile,way,result,data)
end
-- dismount
if data.bicycle == "dismount" then
if bicycle == "dismount" then
result.forward_mode = mode.pushing_bike
result.backward_mode = mode.pushing_bike
result.forward_speed = profile.walking_speed
result.backward_speed = profile.walking_speed
end
end
function safety_handler(profile,way,result,data)
-- maxspeed
limit( result, maxspeed, maxspeed_forward, maxspeed_backward )
-- not routable if no speed assigned
-- this avoid assertions in debug builds
if result.forward_speed <= 0 and result.duration <= 0 then
result.forward_mode = mode.inaccessible
end
if result.backward_speed <= 0 and result.duration <= 0 then
result.backward_mode = mode.inaccessible
end
-- convert duration into cyclability
if profile.properties.weight_name == 'cyclability' then
local safety_penalty = profile.unsafe_highway_list[data.highway] or 1.
local is_unsafe = safety_penalty < 1
local safety_penalty = profile.unsafe_highway_list[data.highway] or 1.
local is_unsafe = safety_penalty < 1
local forward_is_unsafe = is_unsafe and not has_cycleway_forward
local backward_is_unsafe = is_unsafe and not has_cycleway_backward
local is_undesireable = data.highway == "service" and profile.service_penalties[service]
local forward_penalty = 1.
local backward_penalty = 1.
if forward_is_unsafe then
forward_penalty = math.min(forward_penalty, safety_penalty)
end
if backward_is_unsafe then
backward_penalty = math.min(backward_penalty, safety_penalty)
end
-- primaries that are one ways are probably huge primaries where the lanes need to be separated
if is_unsafe and data.highway == 'primary' and not data.is_twoway then
safety_penalty = safety_penalty * 0.5
end
if is_unsafe and data.highway == 'secondary' and not data.is_twoway then
safety_penalty = safety_penalty * 0.6
end
if is_undesireable then
forward_penalty = math.min(forward_penalty, profile.service_penalties[service])
backward_penalty = math.min(backward_penalty, profile.service_penalties[service])
end
local forward_is_unsafe = is_unsafe and not data.has_cycleway_forward
local backward_is_unsafe = is_unsafe and not data.has_cycleway_backward
local is_undesireable = data.highway == "service" and profile.service_penalties[data.service]
local forward_penalty = 1.
local backward_penalty = 1.
if forward_is_unsafe then
forward_penalty = math.min(forward_penalty, safety_penalty)
end
if backward_is_unsafe then
backward_penalty = math.min(backward_penalty, safety_penalty)
end
if is_undesireable then
forward_penalty = math.min(forward_penalty, profile.service_penalties[data.service])
backward_penalty = math.min(backward_penalty, profile.service_penalties[data.service])
end
if result.forward_speed > 0 then
-- convert from km/h to m/s
result.forward_rate = result.forward_speed / 3.6 * forward_penalty
end
if result.backward_speed > 0 then
-- convert from km/h to m/s
result.backward_rate = result.backward_speed / 3.6 * backward_penalty
end
if result.duration > 0 then
result.weight = result.duration / forward_penalty
end
if data.highway == "bicycle" then
safety_bonus = safety_bonus + 0.2
if result.forward_speed > 0 then
-- convert from km/h to m/s
result.forward_rate = result.forward_speed / 3.6 * safety_bonus
result.forward_rate = result.forward_speed / 3.6 * forward_penalty
end
if result.backward_speed > 0 then
-- convert from km/h to m/s
result.backward_rate = result.backward_speed / 3.6 * safety_bonus
result.backward_rate = result.backward_speed / 3.6 * backward_penalty
end
if result.duration > 0 then
result.weight = result.duration / safety_bonus
result.weight = result.duration / forward_penalty
end
end
end
end
function process_way(profile, way, result)
-- the initial filtering of ways based on presence of tags
-- affects processing times significantly, because all ways
@@ -597,39 +542,6 @@ function process_way(profile, way, result)
local data = {
-- prefetch tags
highway = way:get_value_by_key('highway'),
route = nil,
man_made = nil,
railway = nil,
amenity = nil,
public_transport = nil,
bridge = nil,
access = nil,
junction = nil,
maxspeed = nil,
maxspeed_forward = nil,
maxspeed_backward = nil,
barrier = nil,
oneway = nil,
oneway_bicycle = nil,
cycleway = nil,
cycleway_left = nil,
cycleway_right = nil,
duration = nil,
service = nil,
foot = nil,
foot_forward = nil,
foot_backward = nil,
bicycle = nil,
way_type_allows_pushing = false,
has_cycleway_forward = false,
has_cycleway_backward = false,
is_twoway = true,
reverse = false,
implied_oneway = false
}
local handlers = Sequence {
@@ -660,9 +572,6 @@ function process_way(profile, way, result)
-- set name, ref and pronunciation
WayHandlers.names,
-- set classes
WayHandlers.classes,
-- set weight properties of the way
WayHandlers.weights
}
+8 -17
View File
@@ -306,46 +306,37 @@ end
-- add class information
function WayHandlers.classes(profile,way,result,data)
if not profile.classes then
return
end
local allowed_classes = Set {}
for k, v in pairs(profile.classes) do
allowed_classes[v] = true
end
local forward_toll, backward_toll = Tags.get_forward_backward_by_key(way, data, "toll")
local forward_route, backward_route = Tags.get_forward_backward_by_key(way, data, "route")
local tunnel = way:get_value_by_key("tunnel")
if allowed_classes["tunnel"] and tunnel and tunnel ~= "no" then
if tunnel and tunnel ~= "no" then
result.forward_classes["tunnel"] = true
result.backward_classes["tunnel"] = true
end
if allowed_classes["toll"] and forward_toll == "yes" then
if forward_toll == "yes" then
result.forward_classes["toll"] = true
end
if allowed_classes["toll"] and backward_toll == "yes" then
if backward_toll == "yes" then
result.backward_classes["toll"] = true
end
if allowed_classes["ferry"] and forward_route == "ferry" then
if forward_route == "ferry" then
result.forward_classes["ferry"] = true
end
if allowed_classes["ferry"] and backward_route == "ferry" then
if backward_route == "ferry" then
result.backward_classes["ferry"] = true
end
if allowed_classes["restricted"] and result.forward_restricted then
if result.forward_restricted then
result.forward_classes["restricted"] = true
end
if allowed_classes["restricted"] and result.backward_restricted then
if result.backward_restricted then
result.backward_classes["restricted"] = true
end
if allowed_classes["motorway"] and (data.highway == "motorway" or data.highway == "motorway_link") then
if data.highway == "motorway" or data.highway == "motorway_link" then
result.forward_classes["motorway"] = true
result.backward_classes["motorway"] = true
end
+6 -6
View File
@@ -5,7 +5,7 @@
-- Secondary road: 18km/h = 18000m/3600s = 100m/20s
-- Tertiary road: 12km/h = 12000m/3600s = 100m/30s
api_version = 4
api_version = 3
function setup()
return {
@@ -14,7 +14,7 @@ function setup()
max_speed_for_map_matching = 30/3.6, --km -> m/s
weight_name = 'duration',
process_call_tagless_node = false,
u_turn_penalty = 20,
uturn_penalty = 20,
traffic_light_penalty = 7, -- seconds
use_turn_restrictions = true
},
@@ -32,7 +32,7 @@ function setup()
primary = 36,
secondary = 18,
tertiary = 12,
steps = 6
steps = 6,
}
}
end
@@ -128,9 +128,9 @@ function process_way (profile, way, result)
end
function process_turn (profile, turn)
if turn.is_u_turn then
turn.duration = turn.duration + profile.properties.u_turn_penalty
turn.weight = turn.weight + profile.properties.u_turn_penalty
if turn.direction_modifier == direction_modifier.uturn then
turn.duration = profile.properties.uturn_penalty
turn.weight = profile.properties.uturn_penalty
end
if turn.has_traffic_light then
turn.duration = turn.duration + profile.properties.traffic_light_penalty
+1 -3
View File
@@ -9,8 +9,6 @@ lonlat = lambda x: (coord2float(x['lon']['__value']), coord2float(x['lat']['__va
def call(this, method, *args):
"""Call this.method(args)"""
if (str(this) == '<optimized out>'):
raise BaseException('"this" is optimized out')
command = '(*({})({})).{}({})'.format(this.type.target().pointer(), this.address, method, ','.join((str(x) for x in args)))
return gdb.parse_and_eval(command)
@@ -236,6 +234,7 @@ class SVGPrinter (gdb.Command):
mld_facade = facade.cast(gdb.lookup_type('osrm::engine::datafacade::ContiguousInternalMemoryAlgorithmDataFacade<osrm::engine::routing_algorithms::mld::Algorithm>'))
mld_partition = mld_facade['mld_partition']
mld_levels = call(mld_partition, 'GetNumberOfLevels')
print (mld_level, mld_levels)
if mld_level < mld_levels:
sentinel_node = call(mld_partition['partition'], 'size') - 1 # GetSentinelNode
number_of_cells = call(mld_partition, 'GetCell', mld_level, sentinel_node) # GetNumberOfCells
@@ -273,7 +272,6 @@ class SVGPrinter (gdb.Command):
for node in nodes:
geometry_id = call(facade, 'GetGeometryIndex', node)
direction = 'forward' if geometry_id['forward'] else 'reverse'
print (geometry_id, direction)
geometry = SVGPrinter.getByGeometryId(facade, geometry_id, 'Geometry')
weights = SVGPrinter.getByGeometryId(facade, geometry_id, 'Weights')
-43
View File
@@ -1,43 +0,0 @@
/*********************************
* Takes an XML `.osm` file and converts it into a cucumber scenario definition like
* Given the node locations
* | node | lon | lat |
* .....
* Given the ways
* | nodes | tag1 | tag2 | tag3 |
* .....
*
* Note that cucumber tests are limited to 26 nodes (labelled a-z), so
* you'll need to use pretty small OSM extracts to get this to work.
*****************************************/
var fs = require('fs');
var parseString = require('xml2js').parseString;
var data = fs.readFileSync('filename.osm', 'utf8');
const items = parseString(data, (err, result) => {
var idmap = {};
console.log('Given the node locations');
console.log(' | node | lon | lat |');
result.osm.node.filter(n => !n.$.action || n.$.action !== 'delete').forEach(i => {
var code = String.fromCharCode(97 + Object.keys(idmap).length)
idmap[i.$.id] = code;
console.log(` | ${code} | ${i.$.lon} | ${i.$.lat} |`);
});
var allkeys = {};
var waytags = {};
result.osm.way.filter(n => !n.$.action || n.$.action !== 'delete').forEach(w => {
if (!waytags[w.$.id]) waytags[w.$.id] = {};
w.tag.forEach(t => { allkeys[t.$.k] = t.$.v; waytags[w.$.id][t.$.k] = t.$.v; });
});
console.log('And the ways');
console.log(` | nodes | ${Object.keys(allkeys).join(' | ')} |`);
result.osm.way.filter(n => !n.$.action || n.$.action !== 'delete').forEach(w => {
console.log(` | ${w.nd.map(n => idmap[n.$.ref]).join('')} | ${Object.keys(allkeys).map(k => waytags[w.$.id][k] || '').join(' | ')} |`);
});
});
+45 -60
View File
@@ -10,12 +10,13 @@ const clu = require('command-line-usage');
const ansi = require('ansi-escape-sequences');
const turf = require('turf');
const jp = require('jsonpath');
const csv_stringify = require('csv-stringify/lib/sync');
const run_query = (query_options, filters, callback) => {
let tic = () => 0.;
http.request(query_options, function (res) {
let body = '', ttfb = tic();
if (res.statusCode != 200)
return callback(query_options.path, res.statusCode, ttfb);
res.setEncoding('utf8');
res.on('data', function (chunk) {
@@ -34,51 +35,27 @@ const run_query = (query_options, filters, callback) => {
}).end();
};
function generate_points(polygon, number, coordinates_number, max_distance) {
function generate_points(polygon, number) {
let query_points = [];
while (query_points.length < number) {
let points = [];
while(points.length < coordinates_number) {
let chunk = turf
.random('points', coordinates_number, { bbox: turf.bbox(polygon)})
.features
.map(x => x.geometry.coordinates)
.filter(pt => turf.inside(pt, polygon));
if (max_distance > 0)
{
chunk.forEach(pt => {
if (points.length == 0)
{
points.push(pt);
}
else
{
let distance = turf.distance(pt, points[points.length-1], 'meters');
if (distance < max_distance)
{
points.push(pt);
}
}
});
}
else
{
points = points.concat(chunk);
}
}
query_points.push(points);
var chunk = turf
.random('points', number, { bbox: turf.bbox(polygon)})
.features
.map(x => x.geometry.coordinates)
.filter(pt => turf.inside(pt, polygon));
query_points = query_points.concat(chunk);
}
return query_points;
return query_points.slice(0, number);
}
function generate_queries(options, query_points) {
let queries = query_points.map(points => {
return options.path.replace(/{}/g, x => points.pop().join(','));
});
function generate_queries(options, query_points, coordinates_number) {
let queries = [];
for (let chunk = 0; chunk < query_points.length; chunk += coordinates_number)
{
let points = query_points.slice(chunk, chunk + coordinates_number);
let query = options.path.replace(/{}/g, x => points.pop().join(','));
queries.push(query);
}
return queries;
}
@@ -96,22 +73,27 @@ function BoundingBox(x) {
}
const optionsList = [
{name: 'help', alias: 'h', type: Boolean, description: 'Display this usage guide.', defaultValue: false},
{name: 'server', alias: 's', type: ServerDetails, defaultValue: ServerDetails('localhost:5000'), description: 'OSRM routing server', typeLabel: '{underline hostname[:port]}'},
{name: 'path', alias: 'p', type: String, defaultValue: '/route/v1/driving/{};{}', description: 'OSRM query path with \\{\\} coordinate placeholders, default /route/v2/driving/\\{\\};\\{\\}', typeLabel: '{underline path}'},
{name: 'filter', alias: 'f', type: String, defaultValue: ['$.routes[0].weight'], multiple: true, description: 'JSONPath filters, default "$.routes[0].weight"', typeLabel: '{underline filter}'},
{name: 'bounding-box', alias: 'b', type: BoundingBox, defaultValue: BoundingBox('5.86442,47.2654,15.0508,55.1478'), multiple: true, description: 'queries bounding box, default "5.86442,47.2654,15.0508,55.1478"', typeLabel: '{underline west,south,east,north}'},
{name: 'max-sockets', alias: 'm', type: Number, defaultValue: 1, description: 'how many concurrent sockets the agent can have open per origin, default 1', typeLabel: '{underline number}'},
{name: 'number', alias: 'n', type: Number, defaultValue: 10, description: 'number of query points, default 10', typeLabel: '{underline number}'},
{name: 'max-distance', alias: 'd', type: Number, defaultValue: -1, description: 'maximal distance between coordinates', typeLabel: '{underline number}'},
{name: 'queries-files', alias: 'q', type: String, description: 'CSV file with queries in the first row', typeLabel: '{underline file}'},
];
{name: 'server', alias: 's', type: ServerDetails, defaultValue: ServerDetails('localhost:5000'),
description: 'OSRM routing server', typeLabel: '[underline]{hostname[:port]}'},
{name: 'path', alias: 'p', type: String, defaultValue: '/route/v1/driving/{};{}',
description: 'OSRM query path with {} coordinate placeholders, default /route/v1/driving/{};{}', typeLabel: '[underline]{path}'},
{name: 'filter', alias: 'f', type: String, defaultValue: ['$.routes[0].weight'], multiple: true,
description: 'JSONPath filters, default "$.routes[0].weight"', typeLabel: '[underline]{filter}'},
{name: 'bounding-box', alias: 'b', type: BoundingBox, defaultValue: BoundingBox('5.86442,47.2654,15.0508,55.1478'), multiple: true,
description: 'queries bounding box, default "5.86442,47.2654,15.0508,55.1478"', typeLabel: '[underline]{west,south,east,north}'},
{name: 'max-sockets', alias: 'm', type: Number, defaultValue: 1,
description: 'how many concurrent sockets the agent can have open per origin, default 1', typeLabel: '[underline]{number}'},
{name: 'number', alias: 'n', type: Number, defaultValue: 10,
description: 'number of query points, default 10', typeLabel: '[underline]{number}'},
{name: 'queries-files', alias: 'q', type: String,
description: 'CSV file with queries in the first row', typeLabel: '[underline]{file}'}];
const options = cla(optionsList);
if (options.help) {
const banner =`\
____ _______ __ ______ __ ___ ___ _________
/ __ \\\\/ __/ _ \\\\/ |/ / _ \\\\/ / / / |/ / |/ / __/ _ \\\\
/ /_/ /\\\\ \\\\/ , _/ /|_/ / , _/ /_/ / / / _// , _/
\\\\____/___/_/|_/_/ /_/_/|_|\\\\____/_/|_/_/|_/___/_/|_|`;
const banner =
String.raw` ____ _______ __ ___ ___ __ ___ ___ _________ ` + '\n' +
String.raw` / __ \/ __/ _ \/ |/ / / _ \/ / / / |/ / |/ / __/ _ \ ` + '\n' +
String.raw`/ /_/ /\ \/ , _/ /|_/ / / , _/ /_/ / / / _// , _/ ` + '\n' +
String.raw`\____/___/_/|_/_/ /_/ /_/|_|\____/_/|_/_/|_/___/_/|_| `;
const usage = clu([
{ content: ansi.format(banner, 'green'), raw: true },
{ header: 'Run OSRM queries and collect results'/*, content: 'Generates something [italic]{very} important.'*/ },
@@ -132,8 +114,8 @@ if (options.hasOwnProperty('queries-files')) {
} else {
const polygon = options['bounding-box'].map(x => x.poly).reduce((x,y) => turf.union(x, y));
const coordinates_number = (options.path.match(/{}/g) || []).length;
const query_points = generate_points(polygon, options.number, coordinates_number, options['max-distance']);
queries = generate_queries(options, query_points);
const query_points = generate_points(polygon, coordinates_number * options.number);
queries = generate_queries(options, query_points, coordinates_number);
}
queries = queries.map(q => { return {hostname: options.server.hostname, port: options.server.port, path: q}; });
@@ -141,8 +123,11 @@ queries = queries.map(q => { return {hostname: options.server.hostname, port: op
http.globalAgent.maxSockets = options['max-sockets'];
queries.map(query => {
run_query(query, options.filter, (query, code, ttfb, total, results) => {
let data = results ? JSON.stringify(results[0]) : '';
let record = [[query, code, ttfb, total, data]];
process.stdout.write(csv_stringify(record));
let str = `"${query}",${code}`;
if (ttfb !== undefined) str += `,${ttfb}`;
if (total !== undefined) str += `,${total}`;
if (typeof results === 'object' && results.length > 0)
str += ',' + results.map(x => isNaN(x) ? '"' + JSON.stringify(x).replace(/\n/g, ';').replace(/"/g, "'") + '"' : Number(x)).join(',');
console.log(str);
});
});
+61 -32
View File
@@ -9,50 +9,79 @@ set -o nounset
# structure will be lost.
# http://git.661346.n2.nabble.com/subtree-merges-lose-prefix-after-rebase-td7332850.html
OSMIUM_PATH="osmcode/libosmium"
OSMIUM_TAG=v2.14.0
OSMIUM_REPO="https://github.com/osmcode/libosmium.git"
OSMIUM_TAG=v2.13.1
VARIANT_PATH="mapbox/variant"
VARIANT_REPO="https://github.com/mapbox/variant.git"
VARIANT_TAG=v1.1.3
SOL_PATH="ThePhD/sol2"
SOL_REPO="https://github.com/ThePhD/sol2.git"
SOL_TAG=v2.17.5
RAPIDJSON_PATH="Tencent/rapidjson"
RAPIDJSON_REPO="https://github.com/miloyip/rapidjson.git"
RAPIDJSON_TAG=v1.1.0
MICROTAR_PATH="rxi/microtar"
MICROTAR_REPO="https://github.com/rxi/microtar"
MICROTAR_TAG=v0.1.0
PROTOZERO_PATH="mapbox/protozero"
PROTOZERO_TAG=v1.6.2
VARIANT_LATEST=$(curl "https://api.github.com/repos/mapbox/variant/releases/latest" | jq ".tag_name")
OSMIUM_LATEST=$(curl "https://api.github.com/repos/osmcode/libosmium/releases/latest" | jq ".tag_name")
SOL_LATEST=$(curl "https://api.github.com/repos/ThePhD/sol2/releases/latest" | jq ".tag_name")
RAPIDJSON_LATEST=$(curl "https://api.github.com/repos/miloyip/rapidjson/releases/latest" | jq ".tag_name")
MICROTAR_LATEST=$(curl "https://api.github.com/repos/rxi/microtar/releases/latest" | jq ".tag_name")
VTZERO_PATH="mapbox/vtzero"
VTZERO_TAG=v1.0.1
echo "Latest osmium release is $OSMIUM_LATEST, pulling in \"$OSMIUM_TAG\""
echo "Latest variant release is $VARIANT_LATEST, pulling in \"$VARIANT_TAG\""
echo "Latest sol2 release is $SOL_LATEST, pulling in \"$SOL_TAG\""
echo "Latest rapidjson release is $RAPIDJSON_LATEST, pulling in \"$RAPIDJSON_TAG\""
echo "Latest microtar release is $MICROTAR_LATEST, pulling in \"$MICROTAR_TAG\""
function update_subtree () {
name=${1^^}
path=$(tmpvar=${name}_PATH && echo ${!tmpvar})
tag=$(tmpvar=${name}_TAG && echo ${!tmpvar})
dir=$(basename $path)
repo="https://github.com/${path}.git"
latest=$(curl -s "https://api.github.com/repos/${path}/releases/latest" | jq ".tag_name")
read -p "Update osmium (y/n) " ok
if [[ $ok =~ [yY] ]]
then
if [ -d "third_party/libosmium" ]; then
git subtree pull -P third_party/libosmium/ $OSMIUM_REPO $OSMIUM_TAG --squash
else
git subtree add -P third_party/libosmium/ $OSMIUM_REPO $OSMIUM_TAG --squash
fi
fi
echo "Latest $1 release is ${latest}, pulling in \"${tag}\""
read -p "Update variant (y/n) " ok
if [[ $ok =~ [yY] ]]
then
if [ -d "third_party/variant" ]; then
git subtree pull -P third_party/variant/ $VARIANT_REPO $VARIANT_TAG --squash
else
git subtree add -P third_party/variant/ $VARIANT_REPO $VARIANT_TAG --squash
fi
fi
read -p "Update ${1} (y/n) " ok
read -p "Update sol2 (y/n) " ok
if [[ $ok =~ [yY] ]]
then
if [ -d "third_party/sol2" ]; then
git subtree pull -P third_party/sol2/sol2/ $SOL_REPO $SOL_TAG --squash
else
git subtree add -P third_party/sol2/sol2/ $SOL_REPO $SOL_TAG --squash
fi
fi
if [[ $ok =~ [yY] ]]
then
if [ -d "third_party/$dir" ]; then
git subtree pull -P third_party/$dir ${repo} ${tag} --squash
else
git subtree add -P third_party/$dir ${repo} ${tag} --squash
fi
fi
}
read -p "Update rapidjson (y/n) " ok
if [[ $ok =~ [yY] ]]
then
if [ -d "third_party/rapidjson" ]; then
git subtree pull -P third_party/rapidjson/ $RAPIDJSON_REPO $RAPIDJSON_TAG --squash
else
git subtree add -P third_party/rapidjson/ $RAPIDJSON_REPO $RAPIDJSON_TAG --squash
fi
fi
## Update dependencies
for dep in osmium variant sol rapidjson microtar protozero vtzero ; do
update_subtree $dep
done
read -p "Update microtar (y/n) " ok
if [[ $ok =~ [yY] ]]
then
if [ -d "third_party/microtar" ]; then
git subtree pull -P third_party/microtar/ $MICROTAR_REPO $MICROTAR_TAG --squash
else
git subtree add -P third_party/microtar/ $MICROTAR_REPO $MICROTAR_TAG --squash
fi
fi
-6
View File
@@ -75,12 +75,6 @@ int Contractor::Run()
EdgeID number_of_edge_based_nodes = updater.LoadAndUpdateEdgeExpandedGraph(
edge_based_edge_list, node_weights, connectivity_checksum);
// Convert node weights for oneway streets to INVALID_EDGE_WEIGHT
for (auto &weight : node_weights)
{
weight = (weight & 0x80000000) ? INVALID_EDGE_WEIGHT : weight;
}
// Contracting the edge-expanded graph
TIMER_START(contraction);
+9 -21
View File
@@ -74,28 +74,24 @@ void printUnreachableStatistics(const Partition &partition,
auto LoadAndUpdateEdgeExpandedGraph(const CustomizationConfig &config,
const partitioner::MultiLevelPartition &mlp,
std::vector<EdgeWeight> &node_weights,
std::vector<EdgeDuration> &node_durations,
std::uint32_t &connectivity_checksum)
{
updater::Updater updater(config.updater_config);
EdgeID num_nodes;
std::vector<extractor::EdgeBasedEdge> edge_based_edge_list;
EdgeID num_nodes = updater.LoadAndUpdateEdgeExpandedGraph(
edge_based_edge_list, node_weights, node_durations, connectivity_checksum);
std::tie(num_nodes, edge_based_edge_list, connectivity_checksum) =
updater.LoadAndUpdateEdgeExpandedGraph();
auto directed = partitioner::splitBidirectionalEdges(edge_based_edge_list);
auto tidied = partitioner::prepareEdgesForUsageInGraph<
typename partitioner::MultiLevelEdgeBasedGraph::InputEdge>(std::move(directed));
auto edge_based_graph =
partitioner::MultiLevelEdgeBasedGraph(mlp, num_nodes, std::move(tidied));
auto tidied =
partitioner::prepareEdgesForUsageInGraph<StaticEdgeBasedGraphEdge>(std::move(directed));
auto edge_based_graph = customizer::MultiLevelEdgeBasedGraph(mlp, num_nodes, std::move(tidied));
return edge_based_graph;
}
std::vector<CellMetric> customizeFilteredMetrics(const partitioner::MultiLevelEdgeBasedGraph &graph,
std::vector<CellMetric> customizeFilteredMetrics(const MultiLevelEdgeBasedGraph &graph,
const partitioner::CellStorage &storage,
const CellCustomizer &customizer,
const std::vector<std::vector<bool>> &node_filters)
@@ -123,13 +119,8 @@ int Customizer::Run(const CustomizationConfig &config)
partitioner::MultiLevelPartition mlp;
partitioner::files::readPartition(config.GetPath(".osrm.partition"), mlp);
std::vector<EdgeWeight> node_weights;
std::vector<EdgeDuration> node_durations; // TODO: to be removed later
std::uint32_t connectivity_checksum = 0;
auto graph = LoadAndUpdateEdgeExpandedGraph(
config, mlp, node_weights, node_durations, connectivity_checksum);
BOOST_ASSERT(graph.GetNumberOfNodes() == node_weights.size());
std::for_each(node_weights.begin(), node_weights.end(), [](auto &w) { w &= 0x7fffffff; });
auto graph = LoadAndUpdateEdgeExpandedGraph(config, mlp, connectivity_checksum);
util::Log() << "Loaded edge based graph: " << graph.GetNumberOfEdges() << " edges, "
<< graph.GetNumberOfNodes() << " nodes";
@@ -166,10 +157,7 @@ int Customizer::Run(const CustomizationConfig &config)
util::Log() << "MLD customization writing took " << TIMER_SEC(writing_mld_data) << " seconds";
TIMER_START(writing_graph);
MultiLevelEdgeBasedGraph shaved_graph{
std::move(graph), std::move(node_weights), std::move(node_durations)};
customizer::files::writeGraph(
config.GetPath(".osrm.mldgr"), shaved_graph, connectivity_checksum);
partitioner::files::writeGraph(config.GetPath(".osrm.mldgr"), graph, connectivity_checksum);
TIMER_STOP(writing_graph);
util::Log() << "Graph writing took " << TIMER_SEC(writing_graph) << " seconds";
+3 -4
View File
@@ -618,10 +618,9 @@ RouteSteps collapseSegregatedTurnInstructions(RouteSteps steps)
TransferLanesStrategy());
++next_step;
}
// else if the current step is segregated and the next step is not segregated
// and the next step is not a roundabout then combine with turn adjustment
else if (curr_step->is_segregated && !next_step->is_segregated &&
!hasRoundaboutType(next_step->maneuver.instruction))
// else if the current step is segregated and the next step is not then combine with turn
// adjustment
else if (curr_step->is_segregated && !next_step->is_segregated)
{
// Determine if u-turn
if (bearingsAreReversed(
+4 -9
View File
@@ -81,21 +81,16 @@ Status TablePlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms,
}
auto snapped_phantoms = SnapPhantomNodes(phantom_nodes);
auto result_table =
algorithms.ManyToManySearch(snapped_phantoms, params.sources, params.destinations);
bool request_distance = params.annotations & api::TableParameters::AnnotationsType::Distance;
bool request_duration = params.annotations & api::TableParameters::AnnotationsType::Duration;
auto result_tables_pair = algorithms.ManyToManySearch(
snapped_phantoms, params.sources, params.destinations, request_distance, request_duration);
if ((request_duration && result_tables_pair.first.empty()) ||
(request_distance && result_tables_pair.second.empty()))
if (result_table.empty())
{
return Error("NoTable", "No table found", result);
}
api::TableAPI table_api{facade, params};
table_api.MakeResponse(result_tables_pair, snapped_phantoms, result);
table_api.MakeResponse(result_table, snapped_phantoms, result);
return Status::Ok;
}
+540 -212
View File
@@ -1,6 +1,5 @@
#include "guidance/turn_instruction.hpp"
#include "engine/plugins/plugin_base.hpp"
#include "engine/plugins/plugin_base.hpp"
#include "engine/plugins/tile.hpp"
@@ -9,19 +8,21 @@
#include "util/vector_tile.hpp"
#include "util/web_mercator.hpp"
#include "engine/api/json_factory.hpp"
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/geometries.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/multi/geometries/multi_linestring.hpp>
#include <vtzero/builder.hpp>
#include <vtzero/geometry.hpp>
#include <vtzero/index.hpp>
#include <protozero/pbf_writer.hpp>
#include <protozero/varint.hpp>
#include <algorithm>
#include <numeric>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
@@ -40,7 +41,45 @@ constexpr const static int MIN_ZOOM_FOR_TURNS = 15;
namespace
{
// Creates an indexed lookup table for values - used to encoded the vector tile
// which uses a lookup table and index pointers for encoding
template <typename T> struct ValueIndexer
{
private:
std::vector<T> used_values;
std::unordered_map<T, std::size_t> value_offsets;
public:
std::size_t add(const T &value)
{
const auto found = value_offsets.find(value);
std::size_t offset;
if (found == value_offsets.end())
{
used_values.push_back(value);
offset = used_values.size() - 1;
value_offsets[value] = offset;
}
else
{
offset = found->second;
}
return offset;
}
std::size_t indexOf(const T &value) { return value_offsets[value]; }
const std::vector<T> &values() { return used_values; }
std::size_t size() const { return used_values.size(); }
};
using RTreeLeaf = datafacade::BaseDataFacade::RTreeLeaf;
// TODO: Port all this encoding logic to https://github.com/mapbox/vector-tile, which wasn't
// available when this code was originally written.
// Simple container class for WGS84 coordinates
template <typename T> struct Point final
{
@@ -93,15 +132,58 @@ const static box_t clip_box(point_t(-util::vector_tile::BUFFER, -util::vector_ti
point_t(util::vector_tile::EXTENT + util::vector_tile::BUFFER,
util::vector_tile::EXTENT + util::vector_tile::BUFFER));
/**
* Return the x1,y1,x2,y2 pixel coordinates of a line in a given
* tile.
*
* @param start the first coordinate of the line
* @param target the last coordinate of the line
* @param tile_bbox the boundaries of the tile, in mercator coordinates
* @return a FixedLine with coordinates relative to the tile_bbox.
*/
// from mapnik-vector-tile
// Encodes a linestring using protobuf zigzag encoding
inline bool encodeLinestring(const FixedLine &line,
protozero::packed_field_uint32 &geometry,
std::int32_t &start_x,
std::int32_t &start_y)
{
const std::size_t line_size = line.size();
if (line_size < 2)
{
return false;
}
const unsigned lineto_count = static_cast<const unsigned>(line_size) - 1;
auto pt = line.begin();
const constexpr int MOVETO_COMMAND = 9;
geometry.add_element(MOVETO_COMMAND); // move_to | (1 << 3)
geometry.add_element(protozero::encode_zigzag32(pt->x - start_x));
geometry.add_element(protozero::encode_zigzag32(pt->y - start_y));
start_x = pt->x;
start_y = pt->y;
// This means LINETO repeated N times
// See: https://github.com/mapbox/vector-tile-spec/tree/master/2.1#example-command-integers
geometry.add_element((lineto_count << 3u) | 2u);
// Now that we've issued the LINETO REPEAT N command, we append
// N coordinate pairs immediately after the command.
for (++pt; pt != line.end(); ++pt)
{
const std::int32_t dx = pt->x - start_x;
const std::int32_t dy = pt->y - start_y;
geometry.add_element(protozero::encode_zigzag32(dx));
geometry.add_element(protozero::encode_zigzag32(dy));
start_x = pt->x;
start_y = pt->y;
}
return true;
}
// from mapnik-vctor-tile
// Encodes a point
inline void encodePoint(const FixedPoint &pt, protozero::packed_field_uint32 &geometry)
{
const constexpr int MOVETO_COMMAND = 9;
geometry.add_element(MOVETO_COMMAND);
const std::int32_t dx = pt.x;
const std::int32_t dy = pt.y;
// Manual zigzag encoding.
geometry.add_element(protozero::encode_zigzag32(dx));
geometry.add_element(protozero::encode_zigzag32(dy));
}
linestring_t floatLineToTileLine(const FloatLine &geo_line, const BBox &tile_bbox)
{
linestring_t unclipped_line;
@@ -279,144 +361,6 @@ std::vector<NodeID> getSegregatedNodes(const DataFacadeBase &facade,
return result;
}
struct SpeedLayer : public vtzero::layer_builder
{
vtzero::value_index_small_uint uint_index;
vtzero::value_index<vtzero::double_value_type, float, std::unordered_map> double_index;
vtzero::value_index_internal<std::unordered_map> string_index;
vtzero::value_index_bool bool_index;
vtzero::index_value key_speed;
vtzero::index_value key_is_small;
vtzero::index_value key_datasource;
vtzero::index_value key_weight;
vtzero::index_value key_duration;
vtzero::index_value key_name;
vtzero::index_value key_rate;
SpeedLayer(vtzero::tile_builder &tile)
: layer_builder(tile, "speeds"), uint_index(*this), double_index(*this),
string_index(*this), bool_index(*this), key_speed(add_key_without_dup_check("speed")),
key_is_small(add_key_without_dup_check("is_small")),
key_datasource(add_key_without_dup_check("datasource")),
key_weight(add_key_without_dup_check("weight")),
key_duration(add_key_without_dup_check("duration")),
key_name(add_key_without_dup_check("name")), key_rate(add_key_without_dup_check("rate"))
{
}
}; // struct SpeedLayer
class SpeedLayerFeatureBuilder : public vtzero::linestring_feature_builder
{
SpeedLayer &m_layer;
public:
SpeedLayerFeatureBuilder(SpeedLayer &layer, uint64_t id)
: vtzero::linestring_feature_builder(layer), m_layer(layer)
{
set_id(id);
}
void set_speed(unsigned int value)
{
add_property(m_layer.key_speed, m_layer.uint_index(std::min(value, 127u)));
}
void set_is_small(bool value) { add_property(m_layer.key_is_small, m_layer.bool_index(value)); }
void set_datasource(const std::string &value)
{
add_property(m_layer.key_datasource,
m_layer.string_index(vtzero::encoded_property_value{value}));
}
void set_weight(double value) { add_property(m_layer.key_weight, m_layer.double_index(value)); }
void set_duration(double value)
{
add_property(m_layer.key_duration, m_layer.double_index(value));
}
void set_name(const boost::string_ref &value)
{
add_property(
m_layer.key_name,
m_layer.string_index(vtzero::encoded_property_value{value.data(), value.size()}));
}
void set_rate(double value) { add_property(m_layer.key_rate, m_layer.double_index(value)); }
}; // class SpeedLayerFeatureBuilder
struct TurnsLayer : public vtzero::layer_builder
{
vtzero::value_index<vtzero::sint_value_type, int, std::unordered_map> int_index;
vtzero::value_index<vtzero::float_value_type, float, std::unordered_map> float_index;
vtzero::value_index_internal<std::unordered_map> string_index;
vtzero::index_value key_bearing_in;
vtzero::index_value key_turn_angle;
vtzero::index_value key_cost;
vtzero::index_value key_weight;
vtzero::index_value key_turn_type;
vtzero::index_value key_turn_modifier;
TurnsLayer(vtzero::tile_builder &tile)
: layer_builder(tile, "turns"), int_index(*this), float_index(*this), string_index(*this),
key_bearing_in(add_key_without_dup_check("bearing_in")),
key_turn_angle(add_key_without_dup_check("turn_angle")),
key_cost(add_key_without_dup_check("cost")),
key_weight(add_key_without_dup_check("weight")),
key_turn_type(add_key_without_dup_check("type")),
key_turn_modifier(add_key_without_dup_check("modifier"))
{
}
}; // struct TurnsLayer
class TurnsLayerFeatureBuilder : public vtzero::point_feature_builder
{
TurnsLayer &m_layer;
public:
TurnsLayerFeatureBuilder(TurnsLayer &layer, uint64_t id)
: vtzero::point_feature_builder(layer), m_layer(layer)
{
set_id(id);
}
void set_bearing_in(int value)
{
add_property(m_layer.key_bearing_in, m_layer.int_index(value));
}
void set_turn_angle(int value)
{
add_property(m_layer.key_turn_angle, m_layer.int_index(value));
}
void set_cost(float value) { add_property(m_layer.key_cost, m_layer.float_index(value)); }
void set_weight(float value) { add_property(m_layer.key_weight, m_layer.float_index(value)); }
void set_turn(osrm::guidance::TurnInstruction value)
{
const auto type = osrm::guidance::internalInstructionTypeToString(value.type);
const auto modifier = osrm::guidance::instructionModifierToString(value.direction_modifier);
add_property(
m_layer.key_turn_type,
m_layer.string_index(vtzero::encoded_property_value{type.data(), type.size()}));
add_property(
m_layer.key_turn_modifier,
m_layer.string_index(vtzero::encoded_property_value{modifier.data(), modifier.size()}));
}
}; // class TurnsLayerFeatureBuilder
void encodeVectorTile(const DataFacadeBase &facade,
unsigned x,
unsigned y,
@@ -427,25 +371,117 @@ void encodeVectorTile(const DataFacadeBase &facade,
const std::vector<NodeID> &segregated_nodes,
std::string &pbf_buffer)
{
vtzero::tile_builder tile;
std::uint8_t max_datasource_id = 0;
// Vector tiles encode properties on features as indexes into a layer-specific
// lookup table. These ValueIndexer's act as memoizers for values as we discover
// them during edge explioration, and are then used to generate the lookup
// tables for each tile layer.
ValueIndexer<int> line_int_index;
ValueIndexer<util::StringView> line_string_index;
ValueIndexer<int> point_int_index;
ValueIndexer<float> point_float_index;
ValueIndexer<std::string> point_string_index;
const auto get_geometry_id = [&facade](auto edge) {
return facade.GetGeometryIndex(edge.forward_segment_id.id).id;
};
// Vector tiles encode feature properties as indexes into a lookup table. So, we need
// to "pre-loop" over all the edges to create the lookup tables. Once we have those, we
// can then encode the features, and we'll know the indexes that feature properties
// need to refer to.
for (const auto &edge_index : sorted_edge_indexes)
{
const auto &edge = edges[edge_index];
const auto geometry_id = get_geometry_id(edge);
const auto forward_datasource_range = facade.GetUncompressedForwardDatasources(geometry_id);
const auto reverse_datasource_range = facade.GetUncompressedReverseDatasources(geometry_id);
BOOST_ASSERT(edge.fwd_segment_position < forward_datasource_range.size());
const auto forward_datasource = forward_datasource_range(edge.fwd_segment_position);
BOOST_ASSERT(edge.fwd_segment_position < reverse_datasource_range.size());
const auto reverse_datasource = reverse_datasource_range(reverse_datasource_range.size() -
edge.fwd_segment_position - 1);
// Keep track of the highest datasource seen so that we don't write unnecessary
// data to the layer attribute values
max_datasource_id = std::max(max_datasource_id, forward_datasource);
max_datasource_id = std::max(max_datasource_id, reverse_datasource);
}
// Convert tile coordinates into mercator coordinates
double min_mercator_lon, min_mercator_lat, max_mercator_lon, max_mercator_lat;
util::web_mercator::xyzToMercator(
x, y, z, min_mercator_lon, min_mercator_lat, max_mercator_lon, max_mercator_lat);
const BBox tile_bbox{min_mercator_lon, min_mercator_lat, max_mercator_lon, max_mercator_lat};
// XXX leaving in some superfluous scopes to make diff easier to read.
// Protobuf serializes blocks when objects go out of scope, hence
// the extra scoping below.
protozero::pbf_writer tile_writer{pbf_buffer};
{
{
// Add a layer object to the PBF stream. 3=='layer' from the vector tile spec
// (2.1)
protozero::pbf_writer line_layer_writer(tile_writer, util::vector_tile::LAYER_TAG);
// TODO: don't write a layer if there are no features
line_layer_writer.add_uint32(util::vector_tile::VERSION_TAG, 2); // version
// Field 1 is the "layer name" field, it's a string
line_layer_writer.add_string(util::vector_tile::NAME_TAG, "speeds"); // name
// Field 5 is the tile extent. It's a uint32 and should be set to 4096
// for normal vector tiles.
line_layer_writer.add_uint32(util::vector_tile::EXTENT_TAG,
util::vector_tile::EXTENT); // extent
// Because we need to know the indexes into the vector tile lookup table,
// we need to do an initial pass over the data and create the complete
// index of used values.
for (const auto &edge_index : sorted_edge_indexes)
{
const auto &edge = edges[edge_index];
const auto geometry_id = get_geometry_id(edge);
// Get coordinates for start/end nodes of segment (NodeIDs u and v)
const auto a = facade.GetCoordinateOfNode(edge.u);
const auto b = facade.GetCoordinateOfNode(edge.v);
// Calculate the length in meters
const double length = osrm::util::coordinate_calculation::haversineDistance(a, b);
// Weight values
const auto forward_weight_range = facade.GetUncompressedForwardWeights(geometry_id);
const auto reverse_weight_range = facade.GetUncompressedReverseWeights(geometry_id);
const auto forward_weight = forward_weight_range[edge.fwd_segment_position];
const auto reverse_weight = reverse_weight_range[reverse_weight_range.size() -
edge.fwd_segment_position - 1];
line_int_index.add(forward_weight);
line_int_index.add(reverse_weight);
std::uint32_t forward_rate =
static_cast<std::uint32_t>(round(length / forward_weight * 10.));
std::uint32_t reverse_rate =
static_cast<std::uint32_t>(round(length / reverse_weight * 10.));
line_int_index.add(forward_rate);
line_int_index.add(reverse_rate);
// Duration values
const auto forward_duration_range =
facade.GetUncompressedForwardDurations(geometry_id);
const auto reverse_duration_range =
facade.GetUncompressedReverseDurations(geometry_id);
const auto forward_duration = forward_duration_range[edge.fwd_segment_position];
const auto reverse_duration = reverse_duration_range[reverse_duration_range.size() -
edge.fwd_segment_position - 1];
line_int_index.add(forward_duration);
line_int_index.add(reverse_duration);
}
// Begin the layer features block
{
SpeedLayer speeds_layer{tile};
// Each feature gets a unique id, starting at 1
unsigned id = 1;
for (const auto &edge_index : sorted_edge_indexes)
@@ -480,6 +516,7 @@ void encodeVectorTile(const DataFacadeBase &facade,
const auto reverse_duration =
reverse_duration_range[reverse_duration_range.size() -
edge.fwd_segment_position - 1];
const auto forward_datasource_idx =
forward_datasource_range(edge.fwd_segment_position);
const auto reverse_datasource_idx = reverse_datasource_range(
@@ -489,10 +526,84 @@ void encodeVectorTile(const DataFacadeBase &facade,
const auto name_id = facade.GetNameIndex(edge.forward_segment_id.id);
auto name = facade.GetNameForID(name_id);
line_string_index.add(name);
const auto encode_tile_line = [&line_layer_writer,
&component_id,
&id,
&max_datasource_id,
&line_int_index](
const FixedLine &tile_line,
const std::uint32_t speed_kmh_idx,
const std::uint32_t rate_idx,
const std::size_t weight_idx,
const std::size_t duration_idx,
const DatasourceID datasource_idx,
const std::size_t name_idx,
std::int32_t &start_x,
std::int32_t &start_y) {
// Here, we save the two attributes for our feature: the speed and
// the is_small boolean. We only serve up speeds from 0-139, so all we
// do is save the first
protozero::pbf_writer feature_writer(line_layer_writer,
util::vector_tile::FEATURE_TAG);
// Field 3 is the "geometry type" field. Value 2 is "line"
feature_writer.add_enum(
util::vector_tile::GEOMETRY_TAG,
util::vector_tile::GEOMETRY_TYPE_LINE); // geometry type
// Field 1 for the feature is the "id" field.
feature_writer.add_uint64(util::vector_tile::ID_TAG, id++); // id
{
// When adding attributes to a feature, we have to write
// pairs of numbers. The first value is the index in the
// keys array (written later), and the second value is the
// index into the "values" array (also written later). We're
// not writing the actual speed or bool value here, we're saving
// an index into the "values" array. This means many features
// can share the same value data, leading to smaller tiles.
protozero::packed_field_uint32 field(
feature_writer, util::vector_tile::FEATURE_ATTRIBUTES_TAG);
field.add_element(0); // "speed" tag key offset
field.add_element(std::min(
speed_kmh_idx, 127u)); // save the speed value, capped at 127
field.add_element(1); // "is_small" tag key offset
field.add_element(
128 + (component_id.is_tiny ? 0 : 1)); // is_small feature offset
field.add_element(2); // "datasource" tag key offset
field.add_element(130 + datasource_idx); // datasource value offset
field.add_element(3); // "weight" tag key offset
field.add_element(130 + max_datasource_id + 1 +
weight_idx); // weight value offset
field.add_element(4); // "duration" tag key offset
field.add_element(130 + max_datasource_id + 1 +
duration_idx); // duration value offset
field.add_element(5); // "name" tag key offset
field.add_element(130 + max_datasource_id + 1 +
line_int_index.values().size() + name_idx);
field.add_element(6); // rate tag key offset
field.add_element(130 + max_datasource_id + 1 + rate_idx);
}
{
// Encode the geometry for the feature
protozero::packed_field_uint32 geometry(
feature_writer, util::vector_tile::FEATURE_GEOMETRIES_TAG);
encodeLinestring(tile_line, geometry, start_x, start_y);
}
};
// If this is a valid forward edge, go ahead and add it to the tile
if (forward_duration != 0 && edge.forward_segment_id.enabled)
{
std::int32_t start_x = 0;
std::int32_t start_y = 0;
// Calculate the speed for this line
// Speeds are looked up in a simple 1:1 table, so the speed value == lookup
// table index
std::uint32_t speed_kmh_idx =
static_cast<std::uint32_t>(round(length / forward_duration * 10 * 3.6));
@@ -505,19 +616,15 @@ void encodeVectorTile(const DataFacadeBase &facade,
auto tile_line = coordinatesToTileLine(a, b, tile_bbox);
if (!tile_line.empty())
{
SpeedLayerFeatureBuilder fbuilder{speeds_layer, id};
fbuilder.add_linestring_from_container(tile_line);
fbuilder.set_speed(speed_kmh_idx);
fbuilder.set_is_small(component_id.is_tiny);
fbuilder.set_datasource(
facade.GetDatasourceName(forward_datasource_idx).to_string());
fbuilder.set_weight(forward_weight / 10.0);
fbuilder.set_duration(forward_duration / 10.0);
fbuilder.set_name(name);
fbuilder.set_rate(forward_rate / 10.0);
fbuilder.commit();
encode_tile_line(tile_line,
speed_kmh_idx,
line_int_index.indexOf(forward_rate),
line_int_index.indexOf(forward_weight),
line_int_index.indexOf(forward_duration),
forward_datasource_idx,
line_string_index.indexOf(name),
start_x,
start_y);
}
}
@@ -525,7 +632,12 @@ void encodeVectorTile(const DataFacadeBase &facade,
// properties
if (reverse_duration != 0 && edge.reverse_segment_id.enabled)
{
std::int32_t start_x = 0;
std::int32_t start_y = 0;
// Calculate the speed for this line
// Speeds are looked up in a simple 1:1 table, so the speed value == lookup
// table index
std::uint32_t speed_kmh_idx =
static_cast<std::uint32_t>(round(length / reverse_duration * 10 * 3.6));
@@ -538,52 +650,232 @@ void encodeVectorTile(const DataFacadeBase &facade,
auto tile_line = coordinatesToTileLine(b, a, tile_bbox);
if (!tile_line.empty())
{
SpeedLayerFeatureBuilder fbuilder{speeds_layer, id};
fbuilder.add_linestring_from_container(tile_line);
fbuilder.set_speed(speed_kmh_idx);
fbuilder.set_is_small(component_id.is_tiny);
fbuilder.set_datasource(
facade.GetDatasourceName(reverse_datasource_idx).to_string());
fbuilder.set_weight(reverse_weight / 10.0);
fbuilder.set_duration(reverse_duration / 10.0);
fbuilder.set_name(name);
fbuilder.set_rate(reverse_rate / 10.0);
fbuilder.commit();
encode_tile_line(tile_line,
speed_kmh_idx,
line_int_index.indexOf(reverse_rate),
line_int_index.indexOf(reverse_weight),
line_int_index.indexOf(reverse_duration),
reverse_datasource_idx,
line_string_index.indexOf(name),
start_x,
start_y);
}
}
}
}
// Field id 3 is the "keys" attribute
// We need two "key" fields, these are referred to with 0 and 1 (their array
// indexes) earlier
line_layer_writer.add_string(util::vector_tile::KEY_TAG, "speed");
line_layer_writer.add_string(util::vector_tile::KEY_TAG, "is_small");
line_layer_writer.add_string(util::vector_tile::KEY_TAG, "datasource");
line_layer_writer.add_string(util::vector_tile::KEY_TAG, "weight");
line_layer_writer.add_string(util::vector_tile::KEY_TAG, "duration");
line_layer_writer.add_string(util::vector_tile::KEY_TAG, "name");
line_layer_writer.add_string(util::vector_tile::KEY_TAG, "rate");
// Now, we write out the possible speed value arrays and possible is_tiny
// values. Field type 4 is the "values" field. It's a variable type field,
// so requires a two-step write (create the field, then write its value)
for (std::size_t i = 0; i < 128; i++)
{
// Writing field type 4 == variant type
protozero::pbf_writer values_writer(line_layer_writer,
util::vector_tile::VARIANT_TAG);
// Attribute value 5 == uint64 type
values_writer.add_uint64(util::vector_tile::VARIANT_TYPE_UINT64, i);
}
{
protozero::pbf_writer values_writer(line_layer_writer,
util::vector_tile::VARIANT_TAG);
// Attribute value 7 == bool type
values_writer.add_bool(util::vector_tile::VARIANT_TYPE_BOOL, true);
}
{
protozero::pbf_writer values_writer(line_layer_writer,
util::vector_tile::VARIANT_TAG);
// Attribute value 7 == bool type
values_writer.add_bool(util::vector_tile::VARIANT_TYPE_BOOL, false);
}
for (std::size_t i = 0; i <= max_datasource_id; i++)
{
// Writing field type 4 == variant type
protozero::pbf_writer values_writer(line_layer_writer,
util::vector_tile::VARIANT_TAG);
// Attribute value 1 == string type
values_writer.add_string(util::vector_tile::VARIANT_TYPE_STRING,
facade.GetDatasourceName(i).to_string());
}
for (auto value : line_int_index.values())
{
// Writing field type 4 == variant type
protozero::pbf_writer values_writer(line_layer_writer,
util::vector_tile::VARIANT_TAG);
// Attribute value 2 == float type
// Durations come out of OSRM in integer deciseconds, so we convert them
// to seconds with a simple /10 for display
values_writer.add_double(util::vector_tile::VARIANT_TYPE_DOUBLE, value / 10.);
}
for (const auto &name : line_string_index.values())
{
// Writing field type 4 == variant type
protozero::pbf_writer values_writer(line_layer_writer,
util::vector_tile::VARIANT_TAG);
// Attribute value 1 == string type
values_writer.add_string(
util::vector_tile::VARIANT_TYPE_STRING, name.data(), name.size());
}
}
// Only add the turn layer to the tile if it has some features (we sometimes won't
// for tiles of z<16, and tiles that don't show any intersections)
if (!all_turn_data.empty())
{
TurnsLayer turns_layer{tile};
uint64_t id = 0;
for (const auto &turn_data : all_turn_data)
struct EncodedTurnData
{
const auto tile_point = coordinatesToTilePoint(turn_data.coordinate, tile_bbox);
if (boost::geometry::within(point_t(tile_point.x, tile_point.y), clip_box))
util::Coordinate coordinate;
std::size_t angle_index;
std::size_t turn_index;
std::size_t duration_index;
std::size_t weight_index;
std::size_t turntype_index;
std::size_t turnmodifier_index;
};
// we need to pre-encode all values here because we need the full offsets later
// for encoding the actual features.
std::vector<EncodedTurnData> encoded_turn_data(all_turn_data.size());
std::transform(
all_turn_data.begin(),
all_turn_data.end(),
encoded_turn_data.begin(),
[&](const routing_algorithms::TurnData &t) {
auto angle_idx = point_int_index.add(t.in_angle);
auto turn_idx = point_int_index.add(t.turn_angle);
auto duration_idx =
point_float_index.add(t.duration / 10.0); // Note conversion to float here
auto weight_idx =
point_float_index.add(t.weight / 10.0); // Note conversion to float here
auto turntype_idx = point_string_index.add(
osrm::guidance::internalInstructionTypeToString(t.turn_instruction.type));
auto turnmodifier_idx =
point_string_index.add(osrm::guidance::instructionModifierToString(
t.turn_instruction.direction_modifier));
return EncodedTurnData{t.coordinate,
angle_idx,
turn_idx,
duration_idx,
weight_idx,
turntype_idx,
turnmodifier_idx};
});
// Now write the points layer for turn penalty data:
// Add a layer object to the PBF stream. 3=='layer' from the vector tile spec
// (2.1)
protozero::pbf_writer point_layer_writer(tile_writer, util::vector_tile::LAYER_TAG);
point_layer_writer.add_uint32(util::vector_tile::VERSION_TAG, 2); // version
point_layer_writer.add_string(util::vector_tile::NAME_TAG, "turns"); // name
point_layer_writer.add_uint32(util::vector_tile::EXTENT_TAG,
util::vector_tile::EXTENT); // extent
// Begin writing the set of point features
{
// Start each features with an ID starting at 1
int id = 1;
// Helper function to encode a new point feature on a vector tile.
const auto encode_tile_point = [&](const FixedPoint &tile_point,
const auto &point_turn_data) {
protozero::pbf_writer feature_writer(point_layer_writer,
util::vector_tile::FEATURE_TAG);
// Field 3 is the "geometry type" field. Value 1 is "point"
feature_writer.add_enum(
util::vector_tile::GEOMETRY_TAG,
util::vector_tile::GEOMETRY_TYPE_POINT); // geometry type
feature_writer.add_uint64(util::vector_tile::ID_TAG, id++); // id
{
// Write out the 4 properties we want on the feature. These
// refer to indexes in the properties lookup table, which we
// add to the tile after we add all features.
protozero::packed_field_uint32 field(
feature_writer, util::vector_tile::FEATURE_ATTRIBUTES_TAG);
field.add_element(0); // "bearing_in" tag key offset
field.add_element(point_turn_data.angle_index);
field.add_element(1); // "turn_angle" tag key offset
field.add_element(point_turn_data.turn_index);
field.add_element(2); // "cost" tag key offset
field.add_element(point_int_index.size() + point_turn_data.duration_index);
field.add_element(3); // "weight" tag key offset
field.add_element(point_int_index.size() + point_turn_data.weight_index);
field.add_element(4); // "type" tag key offset
field.add_element(point_int_index.size() + point_float_index.size() +
point_turn_data.turntype_index);
field.add_element(5); // "modifier" tag key offset
field.add_element(point_int_index.size() + point_float_index.size() +
point_turn_data.turnmodifier_index);
}
{
// Add the geometry as the last field in this feature
protozero::packed_field_uint32 geometry(
feature_writer, util::vector_tile::FEATURE_GEOMETRIES_TAG);
encodePoint(tile_point, geometry);
}
};
// Loop over all the turns we found and add them as features to the layer
for (const auto &turndata : encoded_turn_data)
{
TurnsLayerFeatureBuilder fbuilder{turns_layer, ++id};
fbuilder.add_point(tile_point);
fbuilder.set_bearing_in(turn_data.in_angle);
fbuilder.set_turn_angle(turn_data.turn_angle);
fbuilder.set_cost(turn_data.duration / 10.0);
fbuilder.set_weight(turn_data.weight / 10.0);
fbuilder.set_turn(turn_data.turn_instruction);
fbuilder.commit();
const auto tile_point = coordinatesToTilePoint(turndata.coordinate, tile_bbox);
if (!boost::geometry::within(point_t(tile_point.x, tile_point.y), clip_box))
{
continue;
}
encode_tile_point(tile_point, turndata);
}
}
// Add the names of the three attributes we added to all the turn penalty
// features previously. The indexes used there refer to these keys.
point_layer_writer.add_string(util::vector_tile::KEY_TAG, "bearing_in");
point_layer_writer.add_string(util::vector_tile::KEY_TAG, "turn_angle");
point_layer_writer.add_string(util::vector_tile::KEY_TAG, "cost");
point_layer_writer.add_string(util::vector_tile::KEY_TAG, "weight");
point_layer_writer.add_string(util::vector_tile::KEY_TAG, "type");
point_layer_writer.add_string(util::vector_tile::KEY_TAG, "modifier");
// Now, save the lists of integers and floats that our features refer to.
for (const auto &value : point_int_index.values())
{
protozero::pbf_writer values_writer(point_layer_writer,
util::vector_tile::VARIANT_TAG);
values_writer.add_sint64(util::vector_tile::VARIANT_TYPE_SINT64, value);
}
for (const auto &value : point_float_index.values())
{
protozero::pbf_writer values_writer(point_layer_writer,
util::vector_tile::VARIANT_TAG);
values_writer.add_float(util::vector_tile::VARIANT_TYPE_FLOAT, value);
}
for (const auto &value : point_string_index.values())
{
protozero::pbf_writer values_writer(point_layer_writer,
util::vector_tile::VARIANT_TAG);
values_writer.add_string(util::vector_tile::VARIANT_TYPE_STRING, value);
}
}
// OSM Node tile layer
{
protozero::pbf_writer point_layer_writer(tile_writer, util::vector_tile::LAYER_TAG);
point_layer_writer.add_uint32(util::vector_tile::VERSION_TAG, 2); // version
point_layer_writer.add_string(util::vector_tile::NAME_TAG, "osmnodes"); // name
point_layer_writer.add_uint32(util::vector_tile::EXTENT_TAG,
util::vector_tile::EXTENT); // extent
std::vector<NodeID> internal_nodes;
internal_nodes.reserve(edges.size() * 2);
for (const auto &edge : edges)
@@ -595,8 +887,6 @@ void encodeVectorTile(const DataFacadeBase &facade,
auto new_end = std::unique(internal_nodes.begin(), internal_nodes.end());
internal_nodes.resize(new_end - internal_nodes.begin());
vtzero::layer_builder osmnodes_layer{tile, "osmnodes"};
for (const auto &internal_node : internal_nodes)
{
const auto coord = facade.GetCoordinateOfNode(internal_node);
@@ -605,19 +895,32 @@ void encodeVectorTile(const DataFacadeBase &facade,
{
continue;
}
vtzero::point_feature_builder fbuilder{osmnodes_layer};
fbuilder.set_id(
static_cast<OSMNodeID::value_type>(facade.GetOSMNodeIDOfNode(internal_node)));
fbuilder.add_point(tile_point);
fbuilder.commit();
protozero::pbf_writer feature_writer(point_layer_writer,
util::vector_tile::FEATURE_TAG);
// Field 3 is the "geometry type" field. Value 1 is "point"
feature_writer.add_enum(util::vector_tile::GEOMETRY_TAG,
util::vector_tile::GEOMETRY_TYPE_POINT); // geometry type
const auto osmid =
static_cast<OSMNodeID::value_type>(facade.GetOSMNodeIDOfNode(internal_node));
feature_writer.add_uint64(util::vector_tile::ID_TAG, osmid); // id
// There are no additional properties, just the ID and the geometry
{
// Add the geometry as the last field in this feature
protozero::packed_field_uint32 geometry(
feature_writer, util::vector_tile::FEATURE_GEOMETRIES_TAG);
encodePoint(tile_point, geometry);
}
}
}
// Internal nodes tile layer
{
vtzero::layer_builder internal_nodes_layer{tile, "internal-nodes"};
protozero::pbf_writer line_layer_writer(tile_writer, util::vector_tile::LAYER_TAG);
line_layer_writer.add_uint32(util::vector_tile::VERSION_TAG, 2); // version
line_layer_writer.add_string(util::vector_tile::NAME_TAG, "internal-nodes"); // name
line_layer_writer.add_uint32(util::vector_tile::EXTENT_TAG,
util::vector_tile::EXTENT); // extent
unsigned id = 0;
for (auto edgeNodeID : segregated_nodes)
{
auto const geomIndex = facade.GetGeometryIndex(edgeNodeID);
@@ -634,21 +937,46 @@ void encodeVectorTile(const DataFacadeBase &facade,
points.push_back(facade.GetCoordinateOfNode(nodeID));
}
const auto encode_tile_line = [&line_layer_writer, &id](
const FixedLine &tile_line, std::int32_t &start_x, std::int32_t &start_y) {
protozero::pbf_writer feature_writer(line_layer_writer,
util::vector_tile::FEATURE_TAG);
feature_writer.add_enum(util::vector_tile::GEOMETRY_TAG,
util::vector_tile::GEOMETRY_TYPE_LINE); // geometry type
feature_writer.add_uint64(util::vector_tile::ID_TAG, id++); // id
{
protozero::packed_field_uint32 field(
feature_writer, util::vector_tile::FEATURE_ATTRIBUTES_TAG);
}
{
// Encode the geometry for the feature
protozero::packed_field_uint32 geometry(
feature_writer, util::vector_tile::FEATURE_GEOMETRIES_TAG);
encodeLinestring(tile_line, geometry, start_x, start_y);
}
};
std::int32_t start_x = 0;
std::int32_t start_y = 0;
auto tile_lines = coordinatesToTileLine(points, tile_bbox);
if (!tile_lines.empty())
{
vtzero::linestring_feature_builder fbuilder{internal_nodes_layer};
for (auto const &tile_line : tile_lines)
for (auto const &tl : tile_lines)
{
fbuilder.add_linestring_from_container(tile_line);
encode_tile_line(tl, start_x, start_y);
}
fbuilder.commit();
}
}
}
}
tile.serialize(pbf_buffer);
// protozero serializes data during object destructors, so once the scope closes,
// our result buffer will have all the tile data encoded into it.
}
}
+19 -24
View File
@@ -131,7 +131,7 @@ void ManipulateTableForFSE(const std::size_t source_id,
result_table.SetValue(destination_id, i, INVALID_EDGE_WEIGHT);
}
// set destination->source to zero so roundtrip treats source and
// set destination->source to zero so rountrip treats source and
// destination as one location
result_table.SetValue(destination_id, source_id, 0);
@@ -216,66 +216,61 @@ Status TripPlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms,
BOOST_ASSERT(snapped_phantoms.size() == number_of_locations);
// compute the duration table of all phantom nodes
auto result_duration_table = util::DistTableWrapper<EdgeWeight>(
algorithms
.ManyToManySearch(
snapped_phantoms, {}, {}, /*requestDistance*/ false, /*requestDuration*/ true)
.first,
number_of_locations);
auto result_table = util::DistTableWrapper<EdgeWeight>(
algorithms.ManyToManySearch(snapped_phantoms, {}, {}), number_of_locations);
if (result_duration_table.size() == 0)
if (result_table.size() == 0)
{
return Status::Error;
}
const constexpr std::size_t BF_MAX_FEASABLE = 10;
BOOST_ASSERT_MSG(result_duration_table.size() == number_of_locations * number_of_locations,
BOOST_ASSERT_MSG(result_table.size() == number_of_locations * number_of_locations,
"Distance Table has wrong size");
if (!IsStronglyConnectedComponent(result_duration_table))
if (!IsStronglyConnectedComponent(result_table))
{
return Error("NoTrips", "No trip visiting all destinations possible.", json_result);
}
if (fixed_start && fixed_end)
{
ManipulateTableForFSE(source_id, destination_id, result_duration_table);
ManipulateTableForFSE(source_id, destination_id, result_table);
}
std::vector<NodeID> duration_trip;
duration_trip.reserve(number_of_locations);
std::vector<NodeID> trip;
trip.reserve(number_of_locations);
// get an optimized order in which the destinations should be visited
if (number_of_locations < BF_MAX_FEASABLE)
{
duration_trip = trip::BruteForceTrip(number_of_locations, result_duration_table);
trip = trip::BruteForceTrip(number_of_locations, result_table);
}
else
{
duration_trip = trip::FarthestInsertionTrip(number_of_locations, result_duration_table);
trip = trip::FarthestInsertionTrip(number_of_locations, result_table);
}
// rotate result such that roundtrip starts at node with index 0
// thist first if covers scenarios: !fixed_end || fixed_start || (fixed_start && fixed_end)
if (!fixed_end || fixed_start)
{
auto desired_start_index = std::find(std::begin(duration_trip), std::end(duration_trip), 0);
BOOST_ASSERT(desired_start_index != std::end(duration_trip));
std::rotate(std::begin(duration_trip), desired_start_index, std::end(duration_trip));
auto desired_start_index = std::find(std::begin(trip), std::end(trip), 0);
BOOST_ASSERT(desired_start_index != std::end(trip));
std::rotate(std::begin(trip), desired_start_index, std::end(trip));
}
else if (fixed_end && !fixed_start && parameters.roundtrip)
{
auto desired_start_index =
std::find(std::begin(duration_trip), std::end(duration_trip), destination_id);
BOOST_ASSERT(desired_start_index != std::end(duration_trip));
std::rotate(std::begin(duration_trip), desired_start_index, std::end(duration_trip));
auto desired_start_index = std::find(std::begin(trip), std::end(trip), destination_id);
BOOST_ASSERT(desired_start_index != std::end(trip));
std::rotate(std::begin(trip), desired_start_index, std::end(trip));
}
// get the route when visiting all destinations in optimized order
InternalRouteResult route =
ComputeRoute(algorithms, snapped_phantoms, duration_trip, parameters.roundtrip);
ComputeRoute(algorithms, snapped_phantoms, trip, parameters.roundtrip);
// get api response
const std::vector<std::vector<NodeID>> trips = {duration_trip};
const std::vector<std::vector<NodeID>> trips = {trip};
const std::vector<InternalRouteResult> routes = {route};
api::TripAPI trip_api{facade, parameters};
trip_api.MakeResponse(trips, routes, snapped_phantoms, json_result);
@@ -34,27 +34,22 @@ using Facade = DataFacade<Algorithm>;
namespace
{
struct Parameters
{
// Alternative paths candidate via nodes are taken from overlapping search spaces.
// Overlapping by a third guarantees us taking candidate nodes "from the middle".
double kSearchSpaceOverlapFactor = 1.33;
// Unpack n-times more candidate paths to run high-quality checks on.
// Unpacking paths yields higher chance to find good alternatives but is also expensive.
unsigned kAlternativesToUnpackFactor = 2;
// Alternative paths length requirement (stretch).
// At most 25% longer then the shortest path.
double kAtMostLongerBy = 0.25;
// Alternative paths similarity requirement (sharing).
// At least 25% different than the shortest path.
double kAtMostSameBy = 0.75;
// Alternative paths are still reasonable around the via node candidate (local optimality).
// At least optimal around 10% sub-paths around the via node candidate.
double kAtLeastOptimalAroundViaBy = 0.1;
// Alternative paths similarity requirement (sharing) based on calles.
// At least 15% different than the shortest path.
double kCellsAtMostSameBy = 0.95;
};
// Alternative paths candidate via nodes are taken from overlapping search spaces.
// Overlapping by a third guarantees us taking candidate nodes "from the middle".
const constexpr auto kSearchSpaceOverlapFactor = 1.33;
// Unpack n-times more candidate paths to run high-quality checks on.
// Unpacking paths yields higher chance to find good alternatives but is also expensive.
const constexpr auto kAlternativesToUnpackFactor = 2.0;
// Alternative paths length requirement (stretch).
// At most 25% longer then the shortest path.
const constexpr auto kAtMostLongerBy = 0.25;
// Alternative paths similarity requirement (sharing).
// At least 15% different than the shortest path.
const constexpr auto kAtLeastDifferentBy = 0.85;
// Alternative paths are still reasonable around the via node candidate (local optimality).
// At least optimal around 10% sub-paths around the via node candidate.
const /*constexpr*/ auto kAtLeastOptimalAroundViaBy = 0.10;
// gcc 7.1 ICE ^
// Represents a via middle node where forward (from s) and backward (from t)
// search spaces overlap and the weight a path (made up of s,via and via,t) has.
@@ -76,104 +71,11 @@ struct WeightedViaNodePackedPath
// its total weight and the via node used to construct the path.
struct WeightedViaNodeUnpackedPath
{
double sharing;
WeightedViaNode via;
UnpackedNodes nodes;
UnpackedEdges edges;
};
// Scale the maximum allowed weight increase based on its magnitude:
// - Shortest path 10 minutes, alternative 13 minutes => Factor of 0.30 ok
// - Shortest path 10 hours, alternative 13 hours => Factor of 0.30 unreasonable
double getLongerByFactorBasedOnDuration(const EdgeWeight duration)
{
BOOST_ASSERT(duration != INVALID_EDGE_WEIGHT);
// We only have generic weights here and no durations without unpacking.
// We also have restricted way penalties which are huge and will screw scaling here.
//
// Users can pass us generic weights not based on durations; we can't do anything about
// it here other than either generating too many or no alternatives in these cases.
//
// We scale the weights with a step function based on some rough guestimates, so that
// they match tens of minutes, in the low hours, tens of hours, etc.
// Computed using a least-squares curve fitiing on the following values:
//
// def func(xs, a, b, c, d):
// return a + b/(xs-d) + c/(xs-d)**3
//
// xs = np.array([5 * 60, 10 * 60, 30 * 60, 60 * 60, 3 * 60 * 60, 10 * 60 * 60])
// ys = np.array([1.0, 0.75, 0.5, 0.4, 0.3, 0.2])
//
// xs_interp = np.arange(5*60, 10*60*60, 5*60)
// ys_interp = np.interp(xs_interp, xs, ys)
//
// params, _ = scipy.optimize.curve_fit(func, xs_interp, ys_interp)
//
// The hyperbolic shape was chosen because it interpolated well between
// the given datapoints and drops off for large durations.
const constexpr auto a = 1.91578463e-01;
const constexpr auto b = 1.35118442e+03;
const constexpr auto c = 2.45437877e+09;
const constexpr auto d = -2.07944571e+03;
if (duration < EdgeWeight(5 * 60))
{
return 1.0;
}
else if (duration > EdgeWeight(10 * 60 * 60))
{
return 0.20;
}
// Bigger than 10 minutes but smaller than 10 hours
BOOST_ASSERT(duration >= 5 * 60 && duration <= 10 * 60 * 60);
return a + b / (duration - d) + c / std::pow(duration - d, 3);
}
Parameters parametersFromRequest(const PhantomNodes &phantom_node_pair)
{
Parameters parameters;
const auto distance = util::coordinate_calculation::haversineDistance(
phantom_node_pair.source_phantom.location, phantom_node_pair.target_phantom.location);
// 10km
if (distance < 10000.)
{
parameters.kAlternativesToUnpackFactor = 10.0;
parameters.kCellsAtMostSameBy = 1.0;
parameters.kAtLeastOptimalAroundViaBy = 0.2;
parameters.kAtMostSameBy = 0.50;
}
// 20km
else if (distance < 20000.)
{
parameters.kAlternativesToUnpackFactor = 8.0;
parameters.kCellsAtMostSameBy = 1.0;
parameters.kAtLeastOptimalAroundViaBy = 0.2;
parameters.kAtMostSameBy = 0.60;
}
// 50km
else if (distance < 50000.)
{
parameters.kAlternativesToUnpackFactor = 6.0;
parameters.kCellsAtMostSameBy = 0.95;
parameters.kAtMostSameBy = 0.65;
}
// 100km
else if (distance < 100000.)
{
parameters.kAlternativesToUnpackFactor = 4.0;
parameters.kCellsAtMostSameBy = 0.95;
parameters.kAtMostSameBy = 0.70;
}
return parameters;
}
// Filters candidates which are on not unique.
// Returns an iterator to the uniquified range's new end.
// Note: mutates the range in-place invalidating iterators.
@@ -208,13 +110,49 @@ RandIt filterViaCandidatesByRoadImportance(RandIt first, RandIt last, const Faca
return last;
}
// Scale the maximum allowed weight increase based on its magnitude:
// - Shortest path 10 minutes, alternative 13 minutes => Factor of 0.30 ok
// - Shortest path 10 hours, alternative 13 hours => Factor of 0.30 unreasonable
double scaledAtMostLongerByFactorBasedOnDuration(EdgeWeight duration)
{
BOOST_ASSERT(duration != INVALID_EDGE_WEIGHT);
// We only have generic weights here and no durations without unpacking.
// We also have restricted way penalties which are huge and will screw scaling here.
//
// Users can pass us generic weights not based on durations; we can't do anything about
// it here other than either generating too many or no alternatives in these cases.
//
// We scale the weights with a step function based on some rough guestimates, so that
// they match tens of minutes, in the low hours, tens of hours, etc.
// Todo: instead of a piecewise constant function should this be a continuous function?
// At the moment there are "hard" jump edge cases when crossing the thresholds.
auto scaledAtMostLongerBy = kAtMostLongerBy;
const constexpr auto minutes = 60.;
const constexpr auto hours = 60. * minutes;
if (duration < EdgeWeight(10 * minutes))
scaledAtMostLongerBy *= 1.20;
else if (duration < EdgeWeight(30 * minutes))
scaledAtMostLongerBy *= 1.00;
else if (duration < EdgeWeight(1 * hours))
scaledAtMostLongerBy *= 0.90;
else if (duration < EdgeWeight(3 * hours))
scaledAtMostLongerBy *= 0.70;
else if (duration > EdgeWeight(10 * hours))
scaledAtMostLongerBy *= 0.50;
return scaledAtMostLongerBy;
}
// Filters candidates with much higher weight than the primary route. Mutates range in-place.
// Returns an iterator to the filtered range's new end.
template <typename RandIt>
RandIt filterViaCandidatesByStretch(RandIt first,
RandIt last,
const EdgeWeight weight,
const Parameters &parameters)
RandIt
filterViaCandidatesByStretch(RandIt first, RandIt last, EdgeWeight weight, double weight_multiplier)
{
util::static_assert_iter_category<RandIt, std::random_access_iterator_tag>();
util::static_assert_iter_value<RandIt, WeightedViaNode>();
@@ -222,7 +160,9 @@ RandIt filterViaCandidatesByStretch(RandIt first,
// Assumes weight roughly corresponds to duration-ish. If this is not the case e.g.
// because users are setting weight to be distance in the profiles, then we might
// either generate more candidates than we have to or not enough. But is okay.
const auto stretch_weight_limit = (1. + parameters.kAtMostLongerBy) * weight;
const auto scaled_at_most_longer_by =
scaledAtMostLongerByFactorBasedOnDuration(weight / weight_multiplier);
const auto stretch_weight_limit = (1. + scaled_at_most_longer_by) * weight;
const auto over_weight_limit = [=](const auto via) {
return via.weight > stretch_weight_limit;
@@ -258,15 +198,8 @@ filterViaCandidatesByViaNotOnPath(const WeightedViaNodePackedPath &path, RandIt
// Filters packed paths with similar cells between each other. Mutates range in-place.
// Returns an iterator to the filtered range's new end.
template <typename RandIt>
RandIt filterPackedPathsByCellSharing(RandIt first,
RandIt last,
const Partition &partition,
const Parameters &parameters)
RandIt filterPackedPathsByCellSharing(RandIt first, RandIt last, const Partition &partition)
{
// In this case we don't need to calculate sharing, because it would not filter anything
if (parameters.kCellsAtMostSameBy >= 1.0)
return last;
util::static_assert_iter_category<RandIt, std::random_access_iterator_tag>();
util::static_assert_iter_value<RandIt, WeightedViaNodePackedPath>();
@@ -293,13 +226,14 @@ RandIt filterPackedPathsByCellSharing(RandIt first,
return last;
std::unordered_set<CellID> cells;
cells.reserve(size * (shortest_path.path.size() + 1) * (1 + parameters.kAtMostLongerBy));
cells.reserve(size * (shortest_path.path.size() + 1) * (1. + kAtMostLongerBy));
cells.insert(get_cell(std::get<0>(shortest_path.path.front())));
for (const auto &edge : shortest_path.path)
cells.insert(get_cell(std::get<1>(edge)));
const auto over_sharing_limit = [&](const auto &packed) {
if (packed.path.empty())
{ // don't remove routes with single-node (empty) path
return false;
@@ -319,7 +253,7 @@ RandIt filterPackedPathsByCellSharing(RandIt first,
const auto sharing = 1. - difference;
if (sharing > parameters.kCellsAtMostSameBy)
if (sharing > kAtLeastDifferentBy)
{
return true;
}
@@ -343,8 +277,7 @@ RandIt filterPackedPathsByLocalOptimality(const WeightedViaNodePackedPath &path,
const Heap &forward_heap,
const Heap &reverse_heap,
RandIt first,
RandIt last,
const Parameters &parameters)
RandIt last)
{
util::static_assert_iter_category<RandIt, std::random_access_iterator_tag>();
util::static_assert_iter_value<RandIt, WeightedViaNodePackedPath>();
@@ -443,7 +376,7 @@ RandIt filterPackedPathsByLocalOptimality(const WeightedViaNodePackedPath &path,
const auto detour_length = forward_heap.GetKey(via) - forward_heap.GetKey(a) +
reverse_heap.GetKey(via) - reverse_heap.GetKey(b);
return plateaux_length < parameters.kAtLeastOptimalAroundViaBy * detour_length;
return plateaux_length < kAtLeastOptimalAroundViaBy * detour_length;
};
return std::remove_if(first, last, is_not_locally_optimal);
@@ -451,11 +384,7 @@ RandIt filterPackedPathsByLocalOptimality(const WeightedViaNodePackedPath &path,
// Filters unpacked paths compared to all other paths. Mutates range in-place.
// Returns an iterator to the filtered range's new end.
template <typename RandIt>
RandIt filterUnpackedPathsBySharing(RandIt first,
RandIt last,
const Facade &facade,
const Parameters &parameters)
template <typename RandIt> RandIt filterUnpackedPathsBySharing(RandIt first, RandIt last)
{
util::static_assert_iter_category<RandIt, std::random_access_iterator_tag>();
util::static_assert_iter_value<RandIt, WeightedViaNodeUnpackedPath>();
@@ -470,59 +399,46 @@ RandIt filterUnpackedPathsBySharing(RandIt first,
if (shortest_path.edges.empty())
return last;
std::unordered_set<NodeID> nodes;
nodes.reserve(size * shortest_path.nodes.size() * (1.25));
std::unordered_set<EdgeID> edges;
edges.reserve(size * shortest_path.edges.size() * (1. + kAtMostLongerBy));
nodes.insert(begin(shortest_path.nodes), end(shortest_path.nodes));
edges.insert(begin(shortest_path.edges), begin(shortest_path.edges));
const auto over_sharing_limit = [&](const auto &unpacked) {
const auto over_sharing_limit = [&](auto &unpacked) {
if (unpacked.edges.empty())
{ // don't remove routes with single-node (empty) path
return false;
}
EdgeWeight total_duration = 0;
const auto add_if_seen = [&](const EdgeWeight duration, const NodeID node) {
auto node_duration = facade.GetNodeDuration(node);
total_duration += node_duration;
if (nodes.count(node) > 0)
{
return duration + node_duration;
}
return duration;
};
const auto not_seen = [&](const EdgeID edge) { return edges.count(edge) < 1; };
const auto different = std::count_if(begin(unpacked.edges), end(unpacked.edges), not_seen);
const auto shared_duration = std::accumulate(
begin(unpacked.nodes), end(unpacked.nodes), EdgeDuration{0}, add_if_seen);
const auto difference = different / static_cast<double>(unpacked.edges.size());
BOOST_ASSERT(difference >= 0.);
BOOST_ASSERT(difference <= 1.);
unpacked.sharing = shared_duration / static_cast<double>(total_duration);
BOOST_ASSERT(unpacked.sharing >= 0.);
BOOST_ASSERT(unpacked.sharing <= 1.);
const auto sharing = 1. - difference;
if (unpacked.sharing > parameters.kAtMostSameBy)
if (sharing > kAtLeastDifferentBy)
{
return true;
}
else
{
nodes.insert(begin(unpacked.nodes), end(unpacked.nodes));
edges.insert(begin(unpacked.edges), end(unpacked.edges));
return false;
}
};
auto end = std::remove_if(first + 1, last, over_sharing_limit);
std::sort(
first + 1, end, [](const auto &lhs, const auto &rhs) { return lhs.sharing < rhs.sharing; });
return end;
return std::remove_if(first + 1, last, over_sharing_limit);
}
// Filters annotated routes by stretch based on duration. Mutates range in-place.
// Returns an iterator to the filtered range's new end.
template <typename RandIt>
RandIt filterAnnotatedRoutesByStretch(RandIt first,
RandIt last,
const InternalRouteResult &shortest_route,
const Parameters &parameters)
RandIt
filterAnnotatedRoutesByStretch(RandIt first, RandIt last, const InternalRouteResult &shortest_route)
{
util::static_assert_iter_category<RandIt, std::random_access_iterator_tag>();
util::static_assert_iter_value<RandIt, InternalRouteResult>();
@@ -530,7 +446,9 @@ RandIt filterAnnotatedRoutesByStretch(RandIt first,
BOOST_ASSERT(shortest_route.is_valid());
const auto shortest_route_duration = shortest_route.duration();
const auto stretch_duration_limit = (1. + parameters.kAtMostLongerBy) * shortest_route_duration;
const auto scaled_at_most_longer_by =
scaledAtMostLongerByFactorBasedOnDuration(shortest_route_duration);
const auto stretch_duration_limit = (1. + scaled_at_most_longer_by) * shortest_route_duration;
const auto over_duration_limit = [=](const auto &route) {
return route.duration() > stretch_duration_limit;
@@ -641,7 +559,6 @@ void unpackPackedPaths(InputIt first,
}
WeightedViaNodeUnpackedPath unpacked_path{
0.0,
WeightedViaNode{packed_path_via, packed_path_weight},
std::move(unpacked_nodes),
std::move(unpacked_edges)};
@@ -656,8 +573,7 @@ void unpackPackedPaths(InputIt first,
inline std::vector<WeightedViaNode>
makeCandidateVias(SearchEngineData<Algorithm> &search_engine_data,
const Facade &facade,
const PhantomNodes &phantom_node_pair,
const Parameters &parameters)
const PhantomNodes &phantom_node_pair)
{
Heap &forward_heap = *search_engine_data.forward_heap_1;
Heap &reverse_heap = *search_engine_data.reverse_heap_1;
@@ -689,7 +605,7 @@ makeCandidateVias(SearchEngineData<Algorithm> &search_engine_data,
while (forward_heap.Size() + reverse_heap.Size() > 0)
{
if (shortest_path_weight != INVALID_EDGE_WEIGHT)
overlap_weight = shortest_path_weight * parameters.kSearchSpaceOverlapFactor;
overlap_weight = shortest_path_weight * kSearchSpaceOverlapFactor;
// Termination criteria - when we have a shortest path this will guarantee for our overlap.
const bool keep_going = forward_heap_min + reverse_heap_min < overlap_weight;
@@ -754,7 +670,7 @@ makeCandidateVias(SearchEngineData<Algorithm> &search_engine_data,
return candidate_vias;
}
} // namespace
} // anon. ns
// Alternative Routes for MLD.
//
@@ -775,11 +691,9 @@ InternalManyRoutesResult alternativePathSearch(SearchEngineData<Algorithm> &sear
const PhantomNodes &phantom_node_pair,
unsigned number_of_alternatives)
{
Parameters parameters = parametersFromRequest(phantom_node_pair);
const auto max_number_of_alternatives = number_of_alternatives;
const auto max_number_of_alternatives_to_unpack =
parameters.kAlternativesToUnpackFactor * max_number_of_alternatives;
kAlternativesToUnpackFactor * max_number_of_alternatives;
BOOST_ASSERT(max_number_of_alternatives > 0);
BOOST_ASSERT(max_number_of_alternatives_to_unpack >= max_number_of_alternatives);
@@ -793,8 +707,7 @@ InternalManyRoutesResult alternativePathSearch(SearchEngineData<Algorithm> &sear
Heap &reverse_heap = *search_engine_data.reverse_heap_1;
// Do forward and backward search, save search space overlap as via candidates.
auto candidate_vias =
makeCandidateVias(search_engine_data, facade, phantom_node_pair, parameters);
auto candidate_vias = makeCandidateVias(search_engine_data, facade, phantom_node_pair);
const auto by_weight = [](const auto &lhs, const auto &rhs) { return lhs.weight < rhs.weight; };
auto shortest_path_via_it =
@@ -817,9 +730,6 @@ InternalManyRoutesResult alternativePathSearch(SearchEngineData<Algorithm> &sear
NodeID shortest_path_via = shortest_path_via_it->node;
EdgeWeight shortest_path_weight = shortest_path_via_it->weight;
const double duration_estimation = shortest_path_weight / facade.GetWeightMultiplier();
parameters.kAtMostLongerBy = getLongerByFactorBasedOnDuration(duration_estimation);
// Filters via candidate nodes with heuristics
// Note: filter pipeline below only makes range smaller; no need to erase items
@@ -828,7 +738,8 @@ InternalManyRoutesResult alternativePathSearch(SearchEngineData<Algorithm> &sear
it = filterViaCandidatesByUniqueNodeIds(begin(candidate_vias), it);
it = filterViaCandidatesByRoadImportance(begin(candidate_vias), it, facade);
it = filterViaCandidatesByStretch(begin(candidate_vias), it, shortest_path_weight, parameters);
it = filterViaCandidatesByStretch(
begin(candidate_vias), it, shortest_path_weight, facade.GetWeightMultiplier());
// Pre-rank by weight; sharing filtering below then discards by similarity.
std::sort(begin(candidate_vias), it, [](const auto lhs, const auto rhs) {
@@ -872,10 +783,10 @@ InternalManyRoutesResult alternativePathSearch(SearchEngineData<Algorithm> &sear
forward_heap, // paths for s, via
reverse_heap, // paths for via, t
begin(weighted_packed_paths) + 1,
alternative_paths_last,
parameters);
alternative_paths_last);
alternative_paths_last = filterPackedPathsByCellSharing(
begin(weighted_packed_paths), alternative_paths_last, partition, parameters);
begin(weighted_packed_paths), alternative_paths_last, partition);
BOOST_ASSERT(weighted_packed_paths.size() >= 1);
@@ -905,8 +816,7 @@ InternalManyRoutesResult alternativePathSearch(SearchEngineData<Algorithm> &sear
auto unpacked_paths_last = end(unpacked_paths);
unpacked_paths_last = filterUnpackedPathsBySharing(
begin(unpacked_paths), end(unpacked_paths), facade, parameters);
unpacked_paths_last = filterUnpackedPathsBySharing(begin(unpacked_paths), end(unpacked_paths));
const auto unpacked_paths_first = begin(unpacked_paths);
const auto number_of_unpacked_paths =
@@ -940,9 +850,7 @@ InternalManyRoutesResult alternativePathSearch(SearchEngineData<Algorithm> &sear
if (routes.size() > 1)
{
parameters.kAtMostLongerBy = getLongerByFactorBasedOnDuration(routes_first->duration());
routes_last = filterAnnotatedRoutesByStretch(
routes_first + 1, routes_last, *routes_first, parameters);
routes_last = filterAnnotatedRoutesByStretch(routes_first + 1, routes_last, *routes_first);
routes.erase(routes_last, end(routes));
}
@@ -1,4 +1,5 @@
#include "engine/routing_algorithms/direct_shortest_path.hpp"
#include "engine/routing_algorithms/routing_base.hpp"
#include "engine/routing_algorithms/routing_base_ch.hpp"
#include "engine/routing_algorithms/routing_base_mld.hpp"
+23 -207
View File
@@ -60,8 +60,8 @@ void relaxOutgoingEdges(const DataFacade<Algorithm> &facade,
if (DIRECTION == FORWARD_DIRECTION ? data.forward : data.backward)
{
const NodeID to = facade.GetTarget(edge);
const auto edge_weight = data.weight;
const auto edge_weight = data.weight;
const auto edge_duration = data.duration;
BOOST_ASSERT_MSG(edge_weight > 0, "edge_weight invalid");
@@ -85,13 +85,12 @@ void relaxOutgoingEdges(const DataFacade<Algorithm> &facade,
}
void forwardRoutingStep(const DataFacade<Algorithm> &facade,
const std::size_t row_index,
const std::size_t number_of_targets,
const unsigned row_idx,
const unsigned number_of_targets,
typename SearchEngineData<Algorithm>::ManyToManyQueryHeap &query_heap,
const std::vector<NodeBucket> &search_space_with_buckets,
std::vector<EdgeWeight> &weights_table,
std::vector<EdgeDuration> &durations_table,
std::vector<NodeID> &middle_nodes_table,
const PhantomNode &phantom_node)
{
const auto node = query_heap.DeleteMin();
@@ -106,12 +105,12 @@ void forwardRoutingStep(const DataFacade<Algorithm> &facade,
for (const auto &current_bucket : boost::make_iterator_range(bucket_list))
{
// Get target id from bucket entry
const auto column_index = current_bucket.column_index;
const auto column_idx = current_bucket.column_index;
const auto target_weight = current_bucket.weight;
const auto target_duration = current_bucket.duration;
auto &current_weight = weights_table[row_index * number_of_targets + column_index];
auto &current_duration = durations_table[row_index * number_of_targets + column_index];
auto &current_weight = weights_table[row_idx * number_of_targets + column_idx];
auto &current_duration = durations_table[row_idx * number_of_targets + column_idx];
// Check if new weight is better
auto new_weight = source_weight + target_weight;
@@ -123,14 +122,12 @@ void forwardRoutingStep(const DataFacade<Algorithm> &facade,
{
current_weight = std::min(current_weight, new_weight);
current_duration = std::min(current_duration, new_duration);
middle_nodes_table[row_index * number_of_targets + column_index] = node;
}
}
else if (std::tie(new_weight, new_duration) < std::tie(current_weight, current_duration))
{
current_weight = new_weight;
current_duration = new_duration;
middle_nodes_table[row_index * number_of_targets + column_index] = node;
}
}
@@ -139,7 +136,7 @@ void forwardRoutingStep(const DataFacade<Algorithm> &facade,
}
void backwardRoutingStep(const DataFacade<Algorithm> &facade,
const unsigned column_index,
const unsigned column_idx,
typename SearchEngineData<Algorithm>::ManyToManyQueryHeap &query_heap,
std::vector<NodeBucket> &search_space_with_buckets,
const PhantomNode &phantom_node)
@@ -147,11 +144,9 @@ void backwardRoutingStep(const DataFacade<Algorithm> &facade,
const auto node = query_heap.DeleteMin();
const auto target_weight = query_heap.GetKey(node);
const auto target_duration = query_heap.GetData(node).duration;
const auto parent = query_heap.GetData(node).parent;
// Store settled nodes in search space bucket
search_space_with_buckets.emplace_back(
node, parent, column_index, target_weight, target_duration);
search_space_with_buckets.emplace_back(node, column_idx, target_weight, target_duration);
relaxOutgoingEdges<REVERSE_DIRECTION>(
facade, node, target_weight, target_duration, query_heap, phantom_node);
@@ -159,187 +154,26 @@ void backwardRoutingStep(const DataFacade<Algorithm> &facade,
} // namespace ch
void retrievePackedPathFromSearchSpace(const NodeID middle_node_id,
const unsigned column_index,
const std::vector<NodeBucket> &search_space_with_buckets,
std::vector<NodeID> &packed_leg)
{
auto bucket_list = std::equal_range(search_space_with_buckets.begin(),
search_space_with_buckets.end(),
middle_node_id,
NodeBucket::ColumnCompare(column_index));
NodeID current_node_id = middle_node_id;
BOOST_ASSERT_MSG(std::distance(bucket_list.first, bucket_list.second) == 1,
"The pointers are not pointing to the same element.");
while (bucket_list.first->parent_node != current_node_id &&
bucket_list.first != search_space_with_buckets.end())
{
current_node_id = bucket_list.first->parent_node;
packed_leg.emplace_back(current_node_id);
bucket_list = std::equal_range(search_space_with_buckets.begin(),
search_space_with_buckets.end(),
current_node_id,
NodeBucket::ColumnCompare(column_index));
}
}
void calculateDistances(typename SearchEngineData<ch::Algorithm>::ManyToManyQueryHeap &query_heap,
const DataFacade<ch::Algorithm> &facade,
const std::vector<PhantomNode> &phantom_nodes,
const std::vector<std::size_t> &target_indices,
const std::size_t row_index,
const std::size_t source_index,
const PhantomNode &source_phantom,
const std::size_t number_of_targets,
const std::vector<NodeBucket> &search_space_with_buckets,
std::vector<EdgeDistance> &distances_table,
const std::vector<NodeID> &middle_nodes_table)
{
std::vector<NodeID> packed_leg;
for (auto column_index : util::irange<std::size_t>(0, number_of_targets))
{
const auto target_index = target_indices[column_index];
const auto &target_phantom = phantom_nodes[target_index];
if (source_index == target_index)
{
distances_table[row_index * number_of_targets + column_index] = 0.0;
continue;
}
NodeID middle_node_id = middle_nodes_table[row_index * number_of_targets + column_index];
if (middle_node_id == SPECIAL_NODEID) // takes care of one-ways
{
distances_table[row_index * number_of_targets + column_index] = INVALID_EDGE_DISTANCE;
continue;
}
// Step 1: Find path from source to middle node
ch::retrievePackedPathFromSingleManyToManyHeap(query_heap, middle_node_id, packed_leg);
std::reverse(packed_leg.begin(), packed_leg.end());
packed_leg.push_back(middle_node_id);
// Step 2: Find path from middle to target node
retrievePackedPathFromSearchSpace(
middle_node_id, column_index, search_space_with_buckets, packed_leg);
if (packed_leg.size() == 1 && (needsLoopForward(source_phantom, target_phantom) ||
needsLoopBackwards(source_phantom, target_phantom)))
{
auto weight = ch::getLoopWeight<false>(facade, packed_leg.front());
if (weight != INVALID_EDGE_WEIGHT)
packed_leg.push_back(packed_leg.front());
}
if (!packed_leg.empty())
{
auto annotation =
ch::calculateEBGNodeAnnotations(facade, packed_leg.begin(), packed_leg.end());
distances_table[row_index * number_of_targets + column_index] = annotation;
// check the direction of travel to figure out how to calculate the offset to/from
// the source/target
if (source_phantom.forward_segment_id.id == packed_leg.front())
{
// ............ <-- calculateEGBAnnotation returns distance from 0 to 3
// -->s <-- subtract offset to start at source
// ......... <-- want this distance as result
// entry 0---1---2---3--- <-- 3 is exit node
EdgeDistance offset = source_phantom.GetForwardDistance();
distances_table[row_index * number_of_targets + column_index] -= offset;
}
else if (source_phantom.reverse_segment_id.id == packed_leg.front())
{
// ............ <-- calculateEGBAnnotation returns distance from 0 to 3
// s<------- <-- subtract offset to start at source
// ... <-- want this distance
// entry 0---1---2---3 <-- 3 is exit node
EdgeDistance offset = source_phantom.GetReverseDistance();
distances_table[row_index * number_of_targets + column_index] -= offset;
}
if (target_phantom.forward_segment_id.id == packed_leg.back())
{
// ............ <-- calculateEGBAnnotation returns distance from 0 to 3
// ++>t <-- add offset to get to target
// ................ <-- want this distance as result
// entry 0---1---2---3--- <-- 3 is exit node
EdgeDistance offset = target_phantom.GetForwardDistance();
distances_table[row_index * number_of_targets + column_index] += offset;
}
else if (target_phantom.reverse_segment_id.id == packed_leg.back())
{
// ............ <-- calculateEGBAnnotation returns distance from 0 to 3
// <++t <-- add offset to get from target
// ................ <-- want this distance as result
// entry 0---1---2---3--- <-- 3 is exit node
EdgeDistance offset = target_phantom.GetReverseDistance();
distances_table[row_index * number_of_targets + column_index] += offset;
}
}
else
{
// there is no shortcut to unpack. source and target are on the same EBG Node.
// if the offset of the target is greater than the offset of the source, subtract it
if (target_phantom.GetForwardDistance() > source_phantom.GetForwardDistance())
{
// --------->t <-- offsets
// ->s <-- subtract source offset from target offset
// ......... <-- want this distance as result
// entry 0---1---2---3--- <-- 3 is exit node
EdgeDistance offset =
target_phantom.GetForwardDistance() - source_phantom.GetForwardDistance();
distances_table[row_index * number_of_targets + column_index] = offset;
}
else
{
// s<--- <-- offsets
// t<--------- <-- subtract source offset from target offset
// ...... <-- want this distance as result
// entry 0---1---2---3--- <-- 3 is exit node
EdgeDistance offset =
target_phantom.GetReverseDistance() - source_phantom.GetReverseDistance();
distances_table[row_index * number_of_targets + column_index] = offset;
}
}
packed_leg.clear();
}
}
template <>
std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
manyToManySearch(SearchEngineData<ch::Algorithm> &engine_working_data,
const DataFacade<ch::Algorithm> &facade,
const std::vector<PhantomNode> &phantom_nodes,
const std::vector<std::size_t> &source_indices,
const std::vector<std::size_t> &target_indices,
const bool calculate_distance,
const bool calculate_duration)
std::vector<EdgeDuration> manyToManySearch(SearchEngineData<ch::Algorithm> &engine_working_data,
const DataFacade<ch::Algorithm> &facade,
const std::vector<PhantomNode> &phantom_nodes,
const std::vector<std::size_t> &source_indices,
const std::vector<std::size_t> &target_indices)
{
(void)calculate_duration; // TODO: stub to use when computing durations become optional
const auto number_of_sources = source_indices.size();
const auto number_of_targets = target_indices.size();
const auto number_of_entries = number_of_sources * number_of_targets;
std::vector<EdgeWeight> weights_table(number_of_entries, INVALID_EDGE_WEIGHT);
std::vector<EdgeDuration> durations_table(number_of_entries, MAXIMAL_EDGE_DURATION);
std::vector<EdgeDistance> distances_table;
std::vector<NodeID> middle_nodes_table(number_of_entries, SPECIAL_NODEID);
std::vector<NodeBucket> search_space_with_buckets;
// Populate buckets with paths from all accessible nodes to destinations via backward searches
for (std::uint32_t column_index = 0; column_index < target_indices.size(); ++column_index)
for (std::uint32_t column_idx = 0; column_idx < target_indices.size(); ++column_idx)
{
const auto index = target_indices[column_index];
const auto index = target_indices[column_idx];
const auto &phantom = phantom_nodes[index];
engine_working_data.InitializeOrClearManyToManyThreadLocalStorage(
@@ -350,8 +184,7 @@ manyToManySearch(SearchEngineData<ch::Algorithm> &engine_working_data,
// Explore search space
while (!query_heap.Empty())
{
backwardRoutingStep(
facade, column_index, query_heap, search_space_with_buckets, phantom);
backwardRoutingStep(facade, column_idx, query_heap, search_space_with_buckets, phantom);
}
}
@@ -359,49 +192,32 @@ manyToManySearch(SearchEngineData<ch::Algorithm> &engine_working_data,
std::sort(search_space_with_buckets.begin(), search_space_with_buckets.end());
// Find shortest paths from sources to all accessible nodes
for (std::uint32_t row_index = 0; row_index < source_indices.size(); ++row_index)
for (std::uint32_t row_idx = 0; row_idx < source_indices.size(); ++row_idx)
{
const auto source_index = source_indices[row_index];
const auto &source_phantom = phantom_nodes[source_index];
const auto index = source_indices[row_idx];
const auto &phantom = phantom_nodes[index];
// Clear heap and insert source nodes
engine_working_data.InitializeOrClearManyToManyThreadLocalStorage(
facade.GetNumberOfNodes());
auto &query_heap = *(engine_working_data.many_to_many_heap);
insertSourceInHeap(query_heap, source_phantom);
insertSourceInHeap(query_heap, phantom);
// Explore search space
while (!query_heap.Empty())
{
forwardRoutingStep(facade,
row_index,
row_idx,
number_of_targets,
query_heap,
search_space_with_buckets,
weights_table,
durations_table,
middle_nodes_table,
source_phantom);
}
if (calculate_distance)
{
distances_table.resize(number_of_entries, INVALID_EDGE_DISTANCE);
calculateDistances(query_heap,
facade,
phantom_nodes,
target_indices,
row_index,
source_index,
source_phantom,
number_of_targets,
search_space_with_buckets,
distances_table,
middle_nodes_table);
phantom);
}
}
return std::make_pair(durations_table, distances_table);
return durations_table;
}
} // namespace routing_algorithms
+102 -476
View File
@@ -1,5 +1,5 @@
#include "engine/routing_algorithms/many_to_many.hpp"
#include "engine/routing_algorithms/routing_base_mld.hpp"
#include "engine/routing_algorithms/routing_base.hpp"
#include <boost/assert.hpp>
#include <boost/range/iterator_range_core.hpp>
@@ -19,8 +19,22 @@ namespace routing_algorithms
namespace mld
{
using PackedEdge = std::tuple</*from*/ NodeID, /*to*/ NodeID, /*from_clique_arc*/ bool>;
using PackedPath = std::vector<PackedEdge>;
template <typename MultiLevelPartition>
inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition,
const NodeID node,
const PhantomNode &phantom_node)
{
auto highest_diffrent_level = [&partition, node](const SegmentID &phantom_node) {
if (phantom_node.enabled)
return partition.GetHighestDifferentLevel(phantom_node.id, node);
return INVALID_LEVEL_ID;
};
const auto node_level = std::min(highest_diffrent_level(phantom_node.forward_segment_id),
highest_diffrent_level(phantom_node.reverse_segment_id));
return node_level;
}
template <typename MultiLevelPartition>
inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition,
@@ -36,6 +50,38 @@ inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition,
return node_level;
}
template <typename MultiLevelPartition>
inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition,
NodeID node,
const std::vector<PhantomNode> &phantom_nodes,
const std::size_t phantom_index,
const std::vector<std::size_t> &phantom_indices)
{
auto min_level = [&partition, node](const PhantomNode &phantom_node) {
const auto &forward_segment = phantom_node.forward_segment_id;
const auto forward_level =
forward_segment.enabled ? partition.GetHighestDifferentLevel(node, forward_segment.id)
: INVALID_LEVEL_ID;
const auto &reverse_segment = phantom_node.reverse_segment_id;
const auto reverse_level =
reverse_segment.enabled ? partition.GetHighestDifferentLevel(node, reverse_segment.id)
: INVALID_LEVEL_ID;
return std::min(forward_level, reverse_level);
};
// Get minimum level over all phantoms of the highest different level with respect to node
// This is equivalent to min_{∀ source, target} partition.GetQueryLevel(source, node, target)
auto result = min_level(phantom_nodes[phantom_index]);
for (const auto &index : phantom_indices)
{
result = std::min(result, min_level(phantom_nodes[index]));
}
return result;
}
template <bool DIRECTION, typename... Args>
void relaxOutgoingEdges(const DataFacade<mld::Algorithm> &facade,
const NodeID node,
@@ -79,10 +125,8 @@ void relaxOutgoingEdges(const DataFacade<mld::Algorithm> &facade,
{
query_heap.Insert(to, to_weight, {node, true, to_duration});
}
else if (std::tie(to_weight, to_duration, node) <
std::tie(query_heap.GetKey(to),
query_heap.GetData(to).duration,
query_heap.GetData(to).parent))
else if (std::tie(to_weight, to_duration) <
std::tie(query_heap.GetKey(to), query_heap.GetData(to).duration))
{
query_heap.GetData(to) = {node, true, to_duration};
query_heap.DecreaseKey(to, to_weight);
@@ -111,10 +155,8 @@ void relaxOutgoingEdges(const DataFacade<mld::Algorithm> &facade,
{
query_heap.Insert(to, to_weight, {node, true, to_duration});
}
else if (std::tie(to_weight, to_duration, node) <
std::tie(query_heap.GetKey(to),
query_heap.GetData(to).duration,
query_heap.GetData(to).parent))
else if (std::tie(to_weight, to_duration) <
std::tie(query_heap.GetKey(to), query_heap.GetData(to).duration))
{
query_heap.GetData(to) = {node, true, to_duration};
query_heap.DecreaseKey(to, to_weight);
@@ -130,8 +172,7 @@ void relaxOutgoingEdges(const DataFacade<mld::Algorithm> &facade,
for (const auto edge : facade.GetBorderEdgeRange(level, node))
{
const auto &data = facade.GetEdgeData(edge);
if ((DIRECTION == FORWARD_DIRECTION) ? facade.IsForwardEdge(edge)
: facade.IsBackwardEdge(edge))
if (DIRECTION == FORWARD_DIRECTION ? data.forward : data.backward)
{
const NodeID to = facade.GetTarget(edge);
if (facade.ExcludeNode(to))
@@ -139,16 +180,12 @@ void relaxOutgoingEdges(const DataFacade<mld::Algorithm> &facade,
continue;
}
const auto turn_id = data.turn_id;
const auto node_id = DIRECTION == FORWARD_DIRECTION ? node : facade.GetTarget(edge);
const auto node_weight = facade.GetNodeWeight(node_id);
const auto node_duration = facade.GetNodeDuration(node_id);
const auto turn_weight = node_weight + facade.GetWeightPenaltyForEdgeID(turn_id);
const auto turn_duration = node_duration + facade.GetDurationPenaltyForEdgeID(turn_id);
const auto edge_weight = data.weight;
const auto edge_duration = data.duration;
BOOST_ASSERT_MSG(node_weight + turn_weight > 0, "edge weight is invalid");
const auto to_weight = weight + turn_weight;
const auto to_duration = duration + turn_duration;
BOOST_ASSERT_MSG(edge_weight > 0, "edge_weight invalid");
const auto to_weight = weight + edge_weight;
const auto to_duration = duration + edge_duration;
// New Node discovered -> Add to Heap + Node Info Storage
if (!query_heap.WasInserted(to))
@@ -156,10 +193,8 @@ void relaxOutgoingEdges(const DataFacade<mld::Algorithm> &facade,
query_heap.Insert(to, to_weight, {node, false, to_duration});
}
// Found a shorter Path -> Update weight and set new parent
else if (std::tie(to_weight, to_duration, node) <
std::tie(query_heap.GetKey(to),
query_heap.GetData(to).duration,
query_heap.GetData(to).parent))
else if (std::tie(to_weight, to_duration) <
std::tie(query_heap.GetKey(to), query_heap.GetData(to).duration))
{
query_heap.GetData(to) = {node, false, to_duration};
query_heap.DecreaseKey(to, to_weight);
@@ -172,18 +207,14 @@ void relaxOutgoingEdges(const DataFacade<mld::Algorithm> &facade,
// Unidirectional multi-layer Dijkstra search for 1-to-N and N-to-1 matrices
//
template <bool DIRECTION>
std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
oneToManySearch(SearchEngineData<Algorithm> &engine_working_data,
const DataFacade<Algorithm> &facade,
const std::vector<PhantomNode> &phantom_nodes,
std::size_t phantom_index,
const std::vector<std::size_t> &phantom_indices,
const bool calculate_distance)
std::vector<EdgeDuration> oneToManySearch(SearchEngineData<Algorithm> &engine_working_data,
const DataFacade<Algorithm> &facade,
const std::vector<PhantomNode> &phantom_nodes,
std::size_t phantom_index,
const std::vector<std::size_t> &phantom_indices)
{
std::vector<EdgeWeight> weights(phantom_indices.size(), INVALID_EDGE_WEIGHT);
std::vector<EdgeDuration> durations(phantom_indices.size(), MAXIMAL_EDGE_DURATION);
std::vector<EdgeDistance> distances_table;
std::vector<NodeID> middle_nodes_table(phantom_indices.size(), SPECIAL_NODEID);
// Collect destination (source) nodes into a map
std::unordered_multimap<NodeID, std::tuple<std::size_t, EdgeWeight, EdgeDuration>>
@@ -251,7 +282,6 @@ oneToManySearch(SearchEngineData<Algorithm> &engine_working_data,
{
weights[index] = path_weight;
durations[index] = path_duration;
middle_nodes_table[index] = node;
}
// Remove node from destinations list
@@ -264,36 +294,21 @@ oneToManySearch(SearchEngineData<Algorithm> &engine_working_data,
}
};
// Check a single path result and insert adjacent nodes into heap
auto insert_node = [&](NodeID node, EdgeWeight initial_weight, EdgeDuration initial_duration) {
// Update single node paths
update_values(node, initial_weight, initial_duration);
query_heap.Insert(node, initial_weight, {node, initial_duration});
// Place adjacent nodes into heap
for (auto edge : facade.GetAdjacentEdgeRange(node))
{
const auto &data = facade.GetEdgeData(edge);
const auto to = facade.GetTarget(edge);
if (facade.ExcludeNode(to))
if (DIRECTION == FORWARD_DIRECTION ? data.forward : data.backward)
{
continue;
}
if ((DIRECTION == FORWARD_DIRECTION ? facade.IsForwardEdge(edge)
: facade.IsBackwardEdge(edge)) &&
!query_heap.WasInserted(to))
{
const auto turn_id = data.turn_id;
const auto node_id = DIRECTION == FORWARD_DIRECTION ? node : to;
const auto edge_weight = initial_weight + facade.GetNodeWeight(node_id) +
facade.GetWeightPenaltyForEdgeID(turn_id);
const auto edge_duration = initial_duration + facade.GetNodeDuration(node_id) +
facade.GetDurationPenaltyForEdgeID(turn_id);
query_heap.Insert(to, edge_weight, {node, edge_duration});
query_heap.Insert(facade.GetTarget(edge),
data.weight + initial_weight,
{node, data.duration + initial_duration});
}
}
};
@@ -303,35 +318,28 @@ oneToManySearch(SearchEngineData<Algorithm> &engine_working_data,
if (DIRECTION == FORWARD_DIRECTION)
{
if (phantom_node.IsValidForwardSource())
{
insert_node(phantom_node.forward_segment_id.id,
-phantom_node.GetForwardWeightPlusOffset(),
-phantom_node.GetForwardDuration());
}
if (phantom_node.IsValidReverseSource())
{
insert_node(phantom_node.reverse_segment_id.id,
-phantom_node.GetReverseWeightPlusOffset(),
-phantom_node.GetReverseDuration());
}
}
else if (DIRECTION == REVERSE_DIRECTION)
{
if (phantom_node.IsValidForwardTarget())
{
insert_node(phantom_node.forward_segment_id.id,
phantom_node.GetForwardWeightPlusOffset(),
phantom_node.GetForwardDuration());
}
if (phantom_node.IsValidReverseTarget())
{
insert_node(phantom_node.reverse_segment_id.id,
phantom_node.GetReverseWeightPlusOffset(),
phantom_node.GetReverseDuration());
}
}
}
@@ -356,127 +364,7 @@ oneToManySearch(SearchEngineData<Algorithm> &engine_working_data,
phantom_indices);
}
if (calculate_distance)
{
// Initialize unpacking heaps
engine_working_data.InitializeOrClearFirstThreadLocalStorage(
facade.GetNumberOfNodes(), facade.GetMaxBorderNodeID() + 1);
distances_table.resize(phantom_indices.size(), INVALID_EDGE_DISTANCE);
for (unsigned location = 0; location < phantom_indices.size(); ++location)
{
// Get the "middle" node that is the last node of a path
const NodeID middle_node_id = middle_nodes_table[location];
if (middle_node_id == SPECIAL_NODEID) // takes care of one-ways
{
continue;
}
// Retrieve the packed path from the heap
PackedPath packed_path = mld::retrievePackedPathFromSingleManyToManyHeap<DIRECTION>(
query_heap, middle_node_id);
// ... and reverse it to have packed edges in the correct order,
if (DIRECTION == FORWARD_DIRECTION)
{
std::reverse(packed_path.begin(), packed_path.end());
}
// ... unpack path
auto &forward_heap = *engine_working_data.forward_heap_1;
auto &reverse_heap = *engine_working_data.reverse_heap_1;
EdgeWeight weight = INVALID_EDGE_WEIGHT;
std::vector<NodeID> unpacked_nodes;
std::vector<EdgeID> unpacked_edges;
std::tie(weight, unpacked_nodes, unpacked_edges) =
unpackPathAndCalculateDistance(engine_working_data,
facade,
forward_heap,
reverse_heap,
DO_NOT_FORCE_LOOPS,
DO_NOT_FORCE_LOOPS,
INVALID_EDGE_WEIGHT,
packed_path,
middle_node_id,
phantom_nodes,
phantom_index,
phantom_indices);
// Accumulate the path length without the last node
auto annotation = 0.0;
BOOST_ASSERT(!unpacked_nodes.empty());
for (auto node = unpacked_nodes.begin(), last_node = std::prev(unpacked_nodes.end());
node != last_node;
++node)
{
annotation += computeEdgeDistance(facade, *node);
}
// ... and add negative source and positive target offsets
// ⚠ for REVERSE_DIRECTION original source and target phantom nodes are swapped
// Get source and target phantom nodes
// * 1-to-N: source is a single index, target is the corresponding from the indices list
// * N-to-1: source is the corresponding from the indices list, target is a single index
auto source_phantom_index = phantom_index;
auto target_phantom_index = phantom_indices[location];
if (DIRECTION == REVERSE_DIRECTION)
{
std::swap(source_phantom_index, target_phantom_index);
}
const auto &source_phantom = phantom_nodes[source_phantom_index];
const auto &target_phantom = phantom_nodes[target_phantom_index];
const NodeID source_node = unpacked_nodes.front();
const NodeID target_node = unpacked_nodes.back();
EdgeDistance source_offset = 0., target_offset = 0.;
if (source_phantom.IsValidForwardSource() &&
source_phantom.forward_segment_id.id == source_node)
{
// ............ <-- calculateEGBAnnotation returns distance from 0
// to 3
// -->s <-- subtract offset to start at source
// ......... <-- want this distance as result
// entry 0---1---2---3--- <-- 3 is exit node
source_offset = source_phantom.GetForwardDistance();
}
else if (source_phantom.IsValidReverseSource() &&
source_phantom.reverse_segment_id.id == source_node)
{
// ............ <-- calculateEGBAnnotation returns distance from 0 to 3
// s<------- <-- subtract offset to start at source
// ... <-- want this distance
// entry 0---1---2---3 <-- 3 is exit node
source_offset = source_phantom.GetReverseDistance();
}
if (target_phantom.IsValidForwardTarget() &&
target_phantom.forward_segment_id.id == target_node)
{
// ............ <-- calculateEGBAnnotation returns distance from 0
// to 3
// ++>t <-- add offset to get to target
// ................ <-- want this distance as result
// entry 0---1---2---3--- <-- 3 is exit node
target_offset = target_phantom.GetForwardDistance();
}
else if (target_phantom.IsValidReverseTarget() &&
target_phantom.reverse_segment_id.id == target_node)
{
// ............ <-- calculateEGBAnnotation returns distance from 0
// to 3
// <++t <-- add offset to get from target
// ................ <-- want this distance as result
// entry 0---1---2---3--- <-- 3 is exit node
target_offset = target_phantom.GetReverseDistance();
}
distances_table[location] = -source_offset + annotation + target_offset;
}
}
return std::make_pair(durations, distances_table);
return durations;
}
//
@@ -491,7 +379,6 @@ void forwardRoutingStep(const DataFacade<Algorithm> &facade,
const std::vector<NodeBucket> &search_space_with_buckets,
std::vector<EdgeWeight> &weights_table,
std::vector<EdgeDuration> &durations_table,
std::vector<NodeID> &middle_nodes_table,
const PhantomNode &phantom_node)
{
const auto node = query_heap.DeleteMin();
@@ -528,7 +415,6 @@ void forwardRoutingStep(const DataFacade<Algorithm> &facade,
{
current_weight = new_weight;
current_duration = new_duration;
middle_nodes_table[location] = node;
}
}
@@ -546,12 +432,9 @@ void backwardRoutingStep(const DataFacade<Algorithm> &facade,
const auto node = query_heap.DeleteMin();
const auto target_weight = query_heap.GetKey(node);
const auto target_duration = query_heap.GetData(node).duration;
const auto parent = query_heap.GetData(node).parent;
const auto from_clique_arc = query_heap.GetData(node).from_clique_arc;
// Store settled nodes in search space bucket
search_space_with_buckets.emplace_back(
node, parent, from_clique_arc, column_idx, target_weight, target_duration);
search_space_with_buckets.emplace_back(node, column_idx, target_weight, target_duration);
const auto &partition = facade.GetMultiLevelPartition();
const auto maximal_level = partition.GetNumberOfLevels() - 1;
@@ -561,225 +444,11 @@ void backwardRoutingStep(const DataFacade<Algorithm> &facade,
}
template <bool DIRECTION>
void retrievePackedPathFromSearchSpace(NodeID middle_node_id,
const unsigned column_idx,
const std::vector<NodeBucket> &search_space_with_buckets,
PackedPath &path)
{
auto bucket_list = std::equal_range(search_space_with_buckets.begin(),
search_space_with_buckets.end(),
middle_node_id,
NodeBucket::ColumnCompare(column_idx));
BOOST_ASSERT_MSG(std::distance(bucket_list.first, bucket_list.second) == 1,
"The pointers are not pointing to the same element.");
NodeID current_node_id = middle_node_id;
while (bucket_list.first->parent_node != current_node_id &&
bucket_list.first != search_space_with_buckets.end())
{
const auto parent_node_id = bucket_list.first->parent_node;
const auto from = DIRECTION == FORWARD_DIRECTION ? current_node_id : parent_node_id;
const auto to = DIRECTION == FORWARD_DIRECTION ? parent_node_id : current_node_id;
path.emplace_back(std::make_tuple(from, to, bucket_list.first->from_clique_arc));
current_node_id = parent_node_id;
bucket_list = std::equal_range(search_space_with_buckets.begin(),
search_space_with_buckets.end(),
current_node_id,
NodeBucket::ColumnCompare(column_idx));
BOOST_ASSERT_MSG(std::distance(bucket_list.first, bucket_list.second) == 1,
"The pointers are not pointing to the same element.");
}
}
template <bool DIRECTION>
void calculateDistances(typename SearchEngineData<mld::Algorithm>::ManyToManyQueryHeap &query_heap,
const DataFacade<mld::Algorithm> &facade,
const std::vector<PhantomNode> &phantom_nodes,
const std::vector<std::size_t> &target_indices,
const unsigned row_idx,
const std::size_t source_index,
const unsigned number_of_sources,
const unsigned number_of_targets,
const std::vector<NodeBucket> &search_space_with_buckets,
std::vector<EdgeDistance> &distances_table,
const std::vector<NodeID> &middle_nodes_table,
SearchEngineData<mld::Algorithm> &engine_working_data)
{
engine_working_data.InitializeOrClearFirstThreadLocalStorage(facade.GetNumberOfNodes(),
facade.GetMaxBorderNodeID() + 1);
for (unsigned column_idx = 0; column_idx < number_of_targets; ++column_idx)
{
// Step 1: Get source and target phantom nodes that were used in the bucketed search
auto source_phantom_index = source_index;
auto target_phantom_index = target_indices[column_idx];
const auto &source_phantom = phantom_nodes[source_phantom_index];
const auto &target_phantom = phantom_nodes[target_phantom_index];
const auto location = DIRECTION == FORWARD_DIRECTION
? row_idx * number_of_targets + column_idx
: row_idx + column_idx * number_of_sources;
if (source_phantom_index == target_phantom_index)
{
distances_table[location] = 0.0;
continue;
}
NodeID middle_node_id = middle_nodes_table[location];
if (middle_node_id == SPECIAL_NODEID) // takes care of one-ways
{
distances_table[location] = INVALID_EDGE_DISTANCE;
continue;
}
// Step 2: Find path from source to middle node
PackedPath packed_path =
mld::retrievePackedPathFromSingleManyToManyHeap<DIRECTION>(query_heap, middle_node_id);
if (DIRECTION == FORWARD_DIRECTION)
{
std::reverse(packed_path.begin(), packed_path.end());
}
auto &forward_heap = *engine_working_data.forward_heap_1;
auto &reverse_heap = *engine_working_data.reverse_heap_1;
EdgeWeight weight = INVALID_EDGE_WEIGHT;
std::vector<NodeID> unpacked_nodes_from_source;
std::vector<EdgeID> unpacked_edges;
std::tie(weight, unpacked_nodes_from_source, unpacked_edges) =
unpackPathAndCalculateDistance(engine_working_data,
facade,
forward_heap,
reverse_heap,
DO_NOT_FORCE_LOOPS,
DO_NOT_FORCE_LOOPS,
INVALID_EDGE_WEIGHT,
packed_path,
middle_node_id,
source_phantom);
// Step 3: Find path from middle to target node
packed_path.clear();
retrievePackedPathFromSearchSpace<DIRECTION>(
middle_node_id, column_idx, search_space_with_buckets, packed_path);
if (DIRECTION == REVERSE_DIRECTION)
{
std::reverse(packed_path.begin(), packed_path.end());
}
std::vector<NodeID> unpacked_nodes_to_target;
std::tie(weight, unpacked_nodes_to_target, unpacked_edges) =
unpackPathAndCalculateDistance(engine_working_data,
facade,
forward_heap,
reverse_heap,
DO_NOT_FORCE_LOOPS,
DO_NOT_FORCE_LOOPS,
INVALID_EDGE_WEIGHT,
packed_path,
middle_node_id,
target_phantom);
if (DIRECTION == REVERSE_DIRECTION)
{
std::swap(unpacked_nodes_to_target, unpacked_nodes_from_source);
}
// Step 4: Compute annotation value along the path nodes without the target node
auto annotation = 0.0;
for (auto node = unpacked_nodes_from_source.begin(),
last_node = std::prev(unpacked_nodes_from_source.end());
node != last_node;
++node)
{
annotation += computeEdgeDistance(facade, *node);
}
for (auto node = unpacked_nodes_to_target.begin(),
last_node = std::prev(unpacked_nodes_to_target.end());
node != last_node;
++node)
{
annotation += computeEdgeDistance(facade, *node);
}
// Step 5: Get phantom node offsets and compute the annotation value
EdgeDistance source_offset = 0., target_offset = 0.;
{
// ⚠ for REVERSE_DIRECTION original source and target phantom nodes are swapped
if (DIRECTION == REVERSE_DIRECTION)
{
std::swap(source_phantom_index, target_phantom_index);
}
const auto &source_phantom = phantom_nodes[source_phantom_index];
const auto &target_phantom = phantom_nodes[target_phantom_index];
NodeID source_node = unpacked_nodes_from_source.front();
NodeID target_node = unpacked_nodes_to_target.back();
if (source_phantom.IsValidForwardSource() &&
source_phantom.forward_segment_id.id == source_node)
{
// ............ <-- calculateEGBAnnotation returns distance from 0
// to 3
// -->s <-- subtract offset to start at source
// ......... <-- want this distance as result
// entry 0---1---2---3--- <-- 3 is exit node
source_offset = source_phantom.GetForwardDistance();
}
else if (source_phantom.IsValidReverseSource() &&
source_phantom.reverse_segment_id.id == source_node)
{
// ............ <-- calculateEGBAnnotation returns distance from 0 to 3
// s<------- <-- subtract offset to start at source
// ... <-- want this distance
// entry 0---1---2---3 <-- 3 is exit node
source_offset = source_phantom.GetReverseDistance();
}
if (target_phantom.IsValidForwardTarget() &&
target_phantom.forward_segment_id.id == target_node)
{
// ............ <-- calculateEGBAnnotation returns distance from 0
// to 3
// ++>t <-- add offset to get to target
// ................ <-- want this distance as result
// entry 0---1---2---3--- <-- 3 is exit node
target_offset = target_phantom.GetForwardDistance();
}
else if (target_phantom.IsValidReverseTarget() &&
target_phantom.reverse_segment_id.id == target_node)
{
// ............ <-- calculateEGBAnnotation returns distance from 0
// to 3
// <++t <-- add offset to get from target
// ................ <-- want this distance as result
// entry 0---1---2---3--- <-- 3 is exit node
target_offset = target_phantom.GetReverseDistance();
}
}
distances_table[location] = -source_offset + annotation + target_offset;
}
}
template <bool DIRECTION>
std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
manyToManySearch(SearchEngineData<Algorithm> &engine_working_data,
const DataFacade<Algorithm> &facade,
const std::vector<PhantomNode> &phantom_nodes,
const std::vector<std::size_t> &source_indices,
const std::vector<std::size_t> &target_indices,
const bool calculate_distance)
std::vector<EdgeDuration> manyToManySearch(SearchEngineData<Algorithm> &engine_working_data,
const DataFacade<Algorithm> &facade,
const std::vector<PhantomNode> &phantom_nodes,
const std::vector<std::size_t> &source_indices,
const std::vector<std::size_t> &target_indices)
{
const auto number_of_sources = source_indices.size();
const auto number_of_targets = target_indices.size();
@@ -787,8 +456,6 @@ manyToManySearch(SearchEngineData<Algorithm> &engine_working_data,
std::vector<EdgeWeight> weights_table(number_of_entries, INVALID_EDGE_WEIGHT);
std::vector<EdgeDuration> durations_table(number_of_entries, MAXIMAL_EDGE_DURATION);
std::vector<EdgeDistance> distances_table;
std::vector<NodeID> middle_nodes_table(number_of_entries, SPECIAL_NODEID);
std::vector<NodeBucket> search_space_with_buckets;
@@ -796,22 +463,22 @@ manyToManySearch(SearchEngineData<Algorithm> &engine_working_data,
for (std::uint32_t column_idx = 0; column_idx < target_indices.size(); ++column_idx)
{
const auto index = target_indices[column_idx];
const auto &target_phantom = phantom_nodes[index];
const auto &phantom = phantom_nodes[index];
engine_working_data.InitializeOrClearManyToManyThreadLocalStorage(
facade.GetNumberOfNodes(), facade.GetMaxBorderNodeID() + 1);
auto &query_heap = *(engine_working_data.many_to_many_heap);
if (DIRECTION == FORWARD_DIRECTION)
insertTargetInHeap(query_heap, target_phantom);
insertTargetInHeap(query_heap, phantom);
else
insertSourceInHeap(query_heap, target_phantom);
insertSourceInHeap(query_heap, phantom);
// explore search space
while (!query_heap.Empty())
{
backwardRoutingStep<DIRECTION>(
facade, column_idx, query_heap, search_space_with_buckets, target_phantom);
facade, column_idx, query_heap, search_space_with_buckets, phantom);
}
}
@@ -821,19 +488,18 @@ manyToManySearch(SearchEngineData<Algorithm> &engine_working_data,
// Find shortest paths from sources to all accessible nodes
for (std::uint32_t row_idx = 0; row_idx < source_indices.size(); ++row_idx)
{
const auto source_index = source_indices[row_idx];
const auto &source_phantom = phantom_nodes[source_index];
const auto index = source_indices[row_idx];
const auto &phantom = phantom_nodes[index];
// Clear heap and insert source nodes
engine_working_data.InitializeOrClearManyToManyThreadLocalStorage(
facade.GetNumberOfNodes(), facade.GetMaxBorderNodeID() + 1);
auto &query_heap = *(engine_working_data.many_to_many_heap);
if (DIRECTION == FORWARD_DIRECTION)
insertSourceInHeap(query_heap, source_phantom);
insertSourceInHeap(query_heap, phantom);
else
insertTargetInHeap(query_heap, source_phantom);
insertTargetInHeap(query_heap, phantom);
// Explore search space
while (!query_heap.Empty())
@@ -846,29 +512,11 @@ manyToManySearch(SearchEngineData<Algorithm> &engine_working_data,
search_space_with_buckets,
weights_table,
durations_table,
middle_nodes_table,
source_phantom);
}
if (calculate_distance)
{
distances_table.resize(number_of_entries, INVALID_EDGE_DISTANCE);
calculateDistances<DIRECTION>(query_heap,
facade,
phantom_nodes,
target_indices, // source_indices
row_idx,
source_index,
number_of_sources,
number_of_targets,
search_space_with_buckets,
distances_table,
middle_nodes_table,
engine_working_data);
phantom);
}
}
return std::make_pair(durations_table, distances_table);
return durations_table;
}
} // namespace mld
@@ -886,54 +534,32 @@ manyToManySearch(SearchEngineData<Algorithm> &engine_working_data,
// then search is performed on a reversed graph with phantom nodes with flipped roles and
// returning a transposed matrix.
template <>
std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
manyToManySearch(SearchEngineData<mld::Algorithm> &engine_working_data,
const DataFacade<mld::Algorithm> &facade,
const std::vector<PhantomNode> &phantom_nodes,
const std::vector<std::size_t> &source_indices,
const std::vector<std::size_t> &target_indices,
const bool calculate_distance,
const bool calculate_duration)
std::vector<EdgeDuration> manyToManySearch(SearchEngineData<mld::Algorithm> &engine_working_data,
const DataFacade<mld::Algorithm> &facade,
const std::vector<PhantomNode> &phantom_nodes,
const std::vector<std::size_t> &source_indices,
const std::vector<std::size_t> &target_indices)
{
(void)calculate_duration; // flag stub to use for calculating distances in matrix in mld in the
// future
if (source_indices.size() == 1)
{ // TODO: check if target_indices.size() == 1 and do a bi-directional search
return mld::oneToManySearch<FORWARD_DIRECTION>(engine_working_data,
facade,
phantom_nodes,
source_indices.front(),
target_indices,
calculate_distance);
return mld::oneToManySearch<FORWARD_DIRECTION>(
engine_working_data, facade, phantom_nodes, source_indices.front(), target_indices);
}
if (target_indices.size() == 1)
{
return mld::oneToManySearch<REVERSE_DIRECTION>(engine_working_data,
facade,
phantom_nodes,
target_indices.front(),
source_indices,
calculate_distance);
return mld::oneToManySearch<REVERSE_DIRECTION>(
engine_working_data, facade, phantom_nodes, target_indices.front(), source_indices);
}
if (target_indices.size() < source_indices.size())
{
return mld::manyToManySearch<REVERSE_DIRECTION>(engine_working_data,
facade,
phantom_nodes,
target_indices,
source_indices,
calculate_distance);
return mld::manyToManySearch<REVERSE_DIRECTION>(
engine_working_data, facade, phantom_nodes, target_indices, source_indices);
}
return mld::manyToManySearch<FORWARD_DIRECTION>(engine_working_data,
facade,
phantom_nodes,
source_indices,
target_indices,
calculate_distance);
return mld::manyToManySearch<FORWARD_DIRECTION>(
engine_working_data, facade, phantom_nodes, source_indices, target_indices);
}
} // namespace routing_algorithms
@@ -59,24 +59,6 @@ void retrievePackedPathFromSingleHeap(const SearchEngineData<Algorithm>::QueryHe
}
}
void retrievePackedPathFromSingleManyToManyHeap(
const SearchEngineData<Algorithm>::ManyToManyQueryHeap &search_heap,
const NodeID middle_node_id,
std::vector<NodeID> &packed_path)
{
NodeID current_node_id = middle_node_id;
// all initial nodes will have itself as parent, or a node not in the heap
// in case of a core search heap. We need a distinction between core entry nodes
// and start nodes since otherwise start node specific code that assumes
// node == node.parent (e.g. the loop code) might get actived.
while (current_node_id != search_heap.GetData(current_node_id).parent &&
search_heap.WasInserted(search_heap.GetData(current_node_id).parent))
{
current_node_id = search_heap.GetData(current_node_id).parent;
packed_path.emplace_back(current_node_id);
}
}
// assumes that heaps are already setup correctly.
// ATTENTION: This only works if no additional offset is supplied next to the Phantom Node
// Offsets.
+37 -2
View File
@@ -101,6 +101,7 @@ std::vector<TurnData> generateTurns(const datafacade &facade,
// w
// uv is the "approach"
// vw is the "exit"
// Look at every node in the directed graph we created
for (const auto &startnode : sorted_startnodes)
{
@@ -145,8 +146,42 @@ std::vector<TurnData> generateTurns(const datafacade &facade,
const auto &data = facade.GetEdgeData(edge_based_edge_id);
// Now, calculate the sum of the weight of all the segments.
const auto turn_weight = facade.GetWeightPenaltyForEdgeID(data.turn_id);
const auto turn_duration = facade.GetDurationPenaltyForEdgeID(data.turn_id);
const auto &geometry =
edge_based_node_info.find(approachedge.edge_based_node_id)->second;
EdgeWeight sum_node_weight = 0;
EdgeDuration sum_node_duration = 0;
if (geometry.is_geometry_forward)
{
const auto approach_weight =
facade.GetUncompressedForwardWeights(geometry.packed_geometry_id);
const auto approach_duration =
facade.GetUncompressedForwardDurations(geometry.packed_geometry_id);
sum_node_weight = std::accumulate(
approach_weight.begin(), approach_weight.end(), EdgeWeight{0});
sum_node_duration = std::accumulate(
approach_duration.begin(), approach_duration.end(), EdgeDuration{0});
}
else
{
const auto approach_weight =
facade.GetUncompressedReverseWeights(geometry.packed_geometry_id);
const auto approach_duration =
facade.GetUncompressedReverseDurations(geometry.packed_geometry_id);
sum_node_weight = std::accumulate(
approach_weight.begin(), approach_weight.end(), EdgeWeight{0});
sum_node_duration = std::accumulate(
approach_duration.begin(), approach_duration.end(), EdgeDuration{0});
}
// The edge.weight is the whole edge weight, which includes the turn
// cost.
// The turn cost is the edge.weight minus the sum of the individual road
// segment weights. This might not be 100% accurate, because some
// intersections include stop signs, traffic signals and other
// penalties, but at this stage, we can't divide those out, so we just
// treat the whole lot as the "turn cost" that we'll stick on the map.
const auto turn_weight = data.weight - sum_node_weight;
const auto turn_duration = data.duration - sum_node_duration;
const auto turn_instruction = facade.GetTurnInstructionForEdgeID(data.turn_id);
// Find the three nodes that make up the turn movement)
+2 -21
View File
@@ -107,13 +107,6 @@ void EdgeBasedGraphFactory::GetEdgeBasedNodeWeights(std::vector<EdgeWeight> &out
swap(m_edge_based_node_weights, output_node_weights);
}
void EdgeBasedGraphFactory::GetEdgeBasedNodeDurations(
std::vector<EdgeWeight> &output_node_durations)
{
using std::swap; // Koenig swap
swap(m_edge_based_node_durations, output_node_durations);
}
std::uint32_t EdgeBasedGraphFactory::GetConnectivityChecksum() const
{
return m_connectivity_checksum;
@@ -145,16 +138,9 @@ NBGToEBG EdgeBasedGraphFactory::InsertEdgeBasedNode(const NodeID node_u, const N
BOOST_ASSERT(nbe_to_ebn_mapping[edge_id_1] != SPECIAL_NODEID ||
nbe_to_ebn_mapping[edge_id_2] != SPECIAL_NODEID);
// ⚠ Use the sign bit of node weights to distinguish oneway streets:
// * MSB is set - a node corresponds to a one-way street
// * MSB is clear - a node corresponds to a bidirectional street
// Before using node weights data values must be adjusted:
// * in contraction if MSB is set the node weight is INVALID_EDGE_WEIGHT.
// This adjustment is needed to enforce loop creation for oneways.
// * in other cases node weights must be masked with 0x7fffffff to clear MSB
if (nbe_to_ebn_mapping[edge_id_1] != SPECIAL_NODEID &&
nbe_to_ebn_mapping[edge_id_2] == SPECIAL_NODEID)
m_edge_based_node_weights[nbe_to_ebn_mapping[edge_id_1]] |= 0x80000000;
m_edge_based_node_weights[nbe_to_ebn_mapping[edge_id_1]] = INVALID_EDGE_WEIGHT;
BOOST_ASSERT(m_compressed_edge_container.HasEntryForID(edge_id_1) ==
m_compressed_edge_container.HasEntryForID(edge_id_2));
@@ -292,7 +278,6 @@ unsigned EdgeBasedGraphFactory::LabelEdgeBasedNodes()
// heuristic: node-based graph node is a simple intersection with four edges
// (edge-based nodes)
m_edge_based_node_weights.reserve(4 * m_node_based_graph.GetNumberOfNodes());
m_edge_based_node_durations.reserve(4 * m_node_based_graph.GetNumberOfNodes());
nbe_to_ebn_mapping.resize(m_node_based_graph.GetEdgeCapacity(), SPECIAL_NODEID);
// renumber edge based node of outgoing edges
@@ -309,7 +294,6 @@ unsigned EdgeBasedGraphFactory::LabelEdgeBasedNodes()
}
m_edge_based_node_weights.push_back(edge_data.weight);
m_edge_based_node_durations.push_back(edge_data.duration);
BOOST_ASSERT(numbered_edges_count < m_node_based_graph.GetNumberOfEdges());
nbe_to_ebn_mapping[current_edge] = numbered_edges_count;
@@ -403,10 +387,8 @@ EdgeBasedGraphFactory::GenerateEdgeExpandedNodes(const WayRestrictionMap &way_re
segregated_edges.count(eid) > 0;
const auto ebn_weight = m_edge_based_node_weights[nbe_to_ebn_mapping[eid]];
BOOST_ASSERT((ebn_weight & 0x7fffffff) == edge_data.weight);
BOOST_ASSERT(ebn_weight == INVALID_EDGE_WEIGHT || ebn_weight == edge_data.weight);
m_edge_based_node_weights.push_back(ebn_weight);
m_edge_based_node_durations.push_back(
m_edge_based_node_durations[nbe_to_ebn_mapping[eid]]);
edge_based_node_id++;
progress.PrintStatus(progress_counter++);
@@ -415,7 +397,6 @@ EdgeBasedGraphFactory::GenerateEdgeExpandedNodes(const WayRestrictionMap &way_re
BOOST_ASSERT(m_edge_based_node_segments.size() == m_edge_based_node_is_startpoint.size());
BOOST_ASSERT(m_number_of_edge_based_nodes == m_edge_based_node_weights.size());
BOOST_ASSERT(m_number_of_edge_based_nodes == m_edge_based_node_durations.size());
util::Log() << "Generated " << m_number_of_edge_based_nodes << " nodes ("
<< way_restriction_map.NumberOfDuplicatedNodes()
+2 -6
View File
@@ -241,7 +241,6 @@ int Extractor::run(ScriptingEnvironment &scripting_environment)
util::DeallocatingVector<EdgeBasedEdge> edge_based_edge_list;
std::vector<bool> node_is_startpoint;
std::vector<EdgeWeight> edge_based_node_weights;
std::vector<EdgeDuration> edge_based_node_durations;
std::uint32_t ebg_connectivity_checksum = 0;
// Create a node-based graph from the OSRM file
@@ -321,7 +320,6 @@ int Extractor::run(ScriptingEnvironment &scripting_environment)
edge_based_node_segments,
node_is_startpoint,
edge_based_node_weights,
edge_based_node_durations,
edge_based_edge_list,
ebg_connectivity_checksum);
@@ -345,8 +343,8 @@ int Extractor::run(ScriptingEnvironment &scripting_environment)
util::Log() << "Saving edge-based node weights to file.";
TIMER_START(timer_write_node_weights);
extractor::files::writeEdgeBasedNodeWeightsDurations(
config.GetPath(".osrm.enw"), edge_based_node_weights, edge_based_node_durations);
extractor::files::writeEdgeBasedNodeWeights(config.GetPath(".osrm.enw"),
edge_based_node_weights);
TIMER_STOP(timer_write_node_weights);
util::Log() << "Done writing. (" << TIMER_SEC(timer_write_node_weights) << ")";
@@ -735,7 +733,6 @@ EdgeID Extractor::BuildEdgeExpandedGraph(
std::vector<EdgeBasedNodeSegment> &edge_based_node_segments,
std::vector<bool> &node_is_startpoint,
std::vector<EdgeWeight> &edge_based_node_weights,
std::vector<EdgeDuration> &edge_based_node_durations,
util::DeallocatingVector<EdgeBasedEdge> &edge_based_edge_list,
std::uint32_t &connectivity_checksum)
{
@@ -785,7 +782,6 @@ EdgeID Extractor::BuildEdgeExpandedGraph(
edge_based_graph_factory.GetEdgeBasedNodeSegments(edge_based_node_segments);
edge_based_graph_factory.GetStartPointMarkers(node_is_startpoint);
edge_based_graph_factory.GetEdgeBasedNodeWeights(edge_based_node_weights);
edge_based_graph_factory.GetEdgeBasedNodeDurations(edge_based_node_durations);
connectivity_checksum = edge_based_graph_factory.GetConnectivityChecksum();
return number_of_edge_based_nodes;
+6 -10
View File
@@ -263,7 +263,7 @@ NAN_METHOD(Engine::nearest) //
// clang-format off
/**
* Computes duration and distance tables for the given locations. Allows for both symmetric and asymmetric
* Computes duration tables for the given locations. Allows for both symmetric and asymmetric
* tables.
*
* @name table
@@ -274,20 +274,17 @@ NAN_METHOD(Engine::nearest) //
* Can be `null` or an array of `[{value},{range}]` with `integer 0 .. 360,integer 0 .. 180`.
* @param {Array} [options.radiuses] Limits the coordinate snapping to streets in the given radius in meters. Can be `null` (unlimited, default) or `double >= 0`.
* @param {Array} [options.hints] Hints for the coordinate snapping. Array of base64 encoded strings.
* @param {Array} [options.sources] An array of `index` elements (`0 <= integer < #coordinates`) to use
* location with given index as source. Default is to use all.
* @param {Array} [options.destinations] An array of `index` elements (`0 <= integer < #coordinates`) to use location with given index as destination. Default is to use all.
* @param {Array} [options.sources] An array of `index` elements (`0 <= integer < #coordinates`) to
* use
* location with given index as source. Default is to use all.
* @param {Array} [options.destinations] An array of `index` elements (`0 <= integer <
* #coordinates`) to use location with given index as destination. Default is to use all.
* @param {Array} [options.approaches] Keep waypoints on curb side. Can be `null` (unrestricted, default) or `curb`.
* @param {Array} [options.annotations] An array of the table types to return. Values can be `duration` or `distance` or both. If no annotations parameter is added, the default is to return the `durations` table. If `annotations=distance` or `annotations=duration,distance` is requested when running a MLD router, a `NotImplemented` error will be returned.
* @param {Function} callback
*
* @returns {Object} containing `durations`, `sources`, and `destinations`.
* **`durations`**: array of arrays that stores the matrix in row-major order. `durations[i][j]` gives the travel time from the i-th waypoint to the j-th waypoint.
* Values are given in seconds.
* **`distances`**: array of arrays that stores the matrix in row-major order. `distances[i][j]` gives the travel time from the i-th waypoint to the j-th waypoint.
* Values are given in meters. Note that computing the `distances` table is currently only implemented for CH. If `annotations=distance` or
* `annotations=duration,distance` is requested when running a MLD router, a `NotImplemented` error will be returned.
* **`sources`**: array of [`aypoint`](#waypoint) objects describing all sources in order.
* **`destinations`**: array of [`aypoint`](#waypoint) objects describing all destinations in order.
*
@@ -302,7 +299,6 @@ NAN_METHOD(Engine::nearest) //
* };
* osrm.table(options, function(err, response) {
* console.log(response.durations); // array of arrays, matrix in row-major order
* console.log(response.distances); // array of arrays, matrix in row-major order (currently only implemented for CH router)
* console.log(response.sources); // array of Waypoint objects
* console.log(response.destinations); // array of Waypoint objects
* });
-10
View File
@@ -144,16 +144,6 @@ int Partitioner::Run(const PartitionerConfig &config)
renumber(node_data, permutation);
extractor::files::writeNodeData(config.GetPath(".osrm.ebg_nodes"), node_data);
}
{
std::vector<EdgeWeight> node_weights;
std::vector<EdgeDuration> node_durations;
extractor::files::readEdgeBasedNodeWeightsDurations(
config.GetPath(".osrm.enw"), node_weights, node_durations);
util::inplacePermutation(node_weights.begin(), node_weights.end(), permutation);
util::inplacePermutation(node_durations.begin(), node_durations.end(), permutation);
extractor::files::writeEdgeBasedNodeWeightsDurations(
config.GetPath(".osrm.enw"), node_weights, node_durations);
}
{
const auto &filename = config.GetPath(".osrm.maneuver_overrides");
std::vector<extractor::StorageManeuverOverride> maneuver_overrides;
+1 -1
View File
@@ -554,7 +554,7 @@ void Storage::PopulateUpdatableData(const SharedDataIndex &index)
{
auto graph_view = make_multi_level_graph_view(index, "/mld/multilevelgraph");
std::uint32_t graph_connectivity_checksum = 0;
customizer::files::readGraph(
partitioner::files::readGraph(
config.GetPath(".osrm.mldgr"), graph_view, graph_connectivity_checksum);
auto turns_connectivity_checksum =
+13 -49
View File
@@ -1,8 +1,6 @@
#include "storage/serialization.hpp"
#include "storage/shared_memory.hpp"
#include "storage/shared_monitor.hpp"
#include "storage/storage.hpp"
#include "osrm/exception.hpp"
#include "util/log.hpp"
#include "util/meminfo.hpp"
@@ -27,17 +25,14 @@ void deleteRegion(const storage::SharedRegionRegister::ShmKey key)
}
}
void listRegions(bool show_blocks)
void listRegions()
{
osrm::util::Log() << "name\tshm key\ttimestamp\tsize";
if (!storage::SharedMonitor<storage::SharedRegionRegister>::exists())
{
return;
}
storage::SharedMonitor<storage::SharedRegionRegister> monitor;
std::vector<std::string> names;
const auto &shared_register = monitor.data();
shared_register.List(std::back_inserter(names));
osrm::util::Log() << "name\tshm key\ttimestamp\tsize";
for (const auto &name : names)
{
auto id = shared_register.Find(name);
@@ -45,23 +40,6 @@ void listRegions(bool show_blocks)
auto shm = osrm::storage::makeSharedMemory(region.shm_key);
osrm::util::Log() << name << "\t" << static_cast<int>(region.shm_key) << "\t"
<< region.timestamp << "\t" << shm->Size();
if (show_blocks)
{
using namespace storage;
auto memory = makeSharedMemory(region.shm_key);
io::BufferReader reader(reinterpret_cast<char *>(memory->Ptr()), memory->Size());
DataLayout layout;
serialization::read(reader, layout);
std::vector<std::string> block_names;
layout.List("", std::back_inserter(block_names));
for (auto &name : block_names)
{
osrm::util::Log() << " " << name << " " << layout.GetBlockSize(name);
}
}
}
}
@@ -98,7 +76,6 @@ bool generateDataStoreOptions(const int argc,
int &max_wait,
std::string &dataset_name,
bool &list_datasets,
bool &list_blocks,
bool &only_metric)
{
// declare a group of options that will be allowed only on command line
@@ -128,19 +105,14 @@ bool generateDataStoreOptions(const int argc,
boost::program_options::value<bool>(&list_datasets)
->default_value(false)
->implicit_value(true),
"List all OSRM datasets currently in memory") //
("list-blocks",
boost::program_options::value<bool>(&list_blocks)
"Name of the dataset to load into memory. This allows having multiple datasets in memory "
"at the same time.") //
("only-metric",
boost::program_options::value<bool>(&only_metric)
->default_value(false)
->implicit_value(true),
"List all OSRM datasets currently in memory")(
"only-metric",
boost::program_options::value<bool>(&only_metric)
->default_value(false)
->implicit_value(true),
"Only reload the metric data without updating the full dataset. This is an "
"optimization "
"for traffic updates.");
"Only reload the metric data without updating the full dataset. This is an optimization "
"for traffic updates.");
// hidden options, will be allowed on command line but will not be shown to the user
boost::program_options::options_description hidden_options("Hidden options");
@@ -235,26 +207,18 @@ int main(const int argc, const char *argv[]) try
int max_wait = -1;
std::string dataset_name;
bool list_datasets = false;
bool list_blocks = false;
bool only_metric = false;
if (!generateDataStoreOptions(argc,
argv,
verbosity,
base_path,
max_wait,
dataset_name,
list_datasets,
list_blocks,
only_metric))
if (!generateDataStoreOptions(
argc, argv, verbosity, base_path, max_wait, dataset_name, list_datasets, only_metric))
{
return EXIT_SUCCESS;
}
util::LogPolicy::GetInstance().SetLevel(verbosity);
if (list_datasets || list_blocks)
if (list_datasets)
{
listRegions(list_blocks);
listRegions();
return EXIT_SUCCESS;
}
+15 -17
View File
@@ -517,6 +517,7 @@ updateConditionalTurns(std::vector<TurnPenalty> &turn_weight_penalties,
{
if (IsRestrictionValid(time_zone_handler, penalty))
{
std::cout << "Disabling: " << penalty.turn_offset << std::endl;
turn_weight_penalties[penalty.turn_offset] = INVALID_TURN_PENALTY;
updated_turns.push_back(penalty.turn_offset);
}
@@ -525,20 +526,20 @@ updateConditionalTurns(std::vector<TurnPenalty> &turn_weight_penalties,
}
}
EdgeID
Updater::LoadAndUpdateEdgeExpandedGraph(std::vector<extractor::EdgeBasedEdge> &edge_based_edge_list,
std::vector<EdgeWeight> &node_weights,
std::uint32_t &connectivity_checksum) const
Updater::NumNodesAndEdges Updater::LoadAndUpdateEdgeExpandedGraph() const
{
std::vector<EdgeDuration> node_durations(node_weights.size());
return LoadAndUpdateEdgeExpandedGraph(
edge_based_edge_list, node_weights, node_durations, connectivity_checksum);
std::vector<EdgeWeight> node_weights;
std::vector<extractor::EdgeBasedEdge> edge_based_edge_list;
std::uint32_t connectivity_checksum;
auto number_of_edge_based_nodes = Updater::LoadAndUpdateEdgeExpandedGraph(
edge_based_edge_list, node_weights, connectivity_checksum);
return std::make_tuple(
number_of_edge_based_nodes, std::move(edge_based_edge_list), connectivity_checksum);
}
EdgeID
Updater::LoadAndUpdateEdgeExpandedGraph(std::vector<extractor::EdgeBasedEdge> &edge_based_edge_list,
std::vector<EdgeWeight> &node_weights,
std::vector<EdgeDuration> &node_durations,
std::uint32_t &connectivity_checksum) const
{
TIMER_START(load_edges);
@@ -547,9 +548,6 @@ Updater::LoadAndUpdateEdgeExpandedGraph(std::vector<extractor::EdgeBasedEdge> &e
std::vector<util::Coordinate> coordinates;
extractor::PackedOSMIDs osm_node_ids;
extractor::files::readEdgeBasedNodeWeightsDurations(
config.GetPath(".osrm.enw"), node_weights, node_durations);
extractor::files::readEdgeBasedGraph(config.GetPath(".osrm.ebg"),
number_of_edge_based_nodes,
edge_based_edge_list,
@@ -745,12 +743,12 @@ Updater::LoadAndUpdateEdgeExpandedGraph(std::vector<extractor::EdgeBasedEdge> &e
accumulated_segment_data[updated_iter - updated_segments.begin()];
// Update the node-weight cache. This is the weight of the edge-based-node
// only, it doesn't include the turn. We may visit the same node multiple times,
// but we should always assign the same value here.
BOOST_ASSERT(edge.source < node_weights.size());
node_weights[edge.source] =
node_weights[edge.source] & 0x80000000 ? new_weight | 0x80000000 : new_weight;
node_durations[edge.source] = new_duration;
// only,
// it doesn't include the turn. We may visit the same node multiple times,
// but
// we should always assign the same value here.
if (node_weights.size() > 0)
node_weights[edge.source] = new_weight;
// We found a zero-speed edge, so we'll skip this whole edge-based-edge
// which
+3 -57
View File
@@ -5,8 +5,6 @@
#include <boost/assert.hpp>
#include <mapbox/cheap_ruler.hpp>
#include <algorithm>
#include <iterator>
#include <limits>
@@ -20,44 +18,6 @@ namespace util
namespace coordinate_calculation
{
namespace
{
class CheapRulerContainer
{
public:
CheapRulerContainer(const int number_of_rulers)
: cheap_ruler_cache(number_of_rulers, mapbox::cheap_ruler::CheapRuler(0)),
step(90.0 * COORDINATE_PRECISION / number_of_rulers)
{
for (int n = 0; n < number_of_rulers; n++)
{
cheap_ruler_cache[n] = mapbox::cheap_ruler::CheapRuler(
step * (n + 0.5) / COORDINATE_PRECISION, mapbox::cheap_ruler::CheapRuler::Meters);
}
};
mapbox::cheap_ruler::CheapRuler &getRuler(const FixedLatitude lat_1, const FixedLatitude lat_2)
{
auto lat = (lat_1 + lat_2) / util::FixedLatitude{2};
return getRuler(lat);
}
mapbox::cheap_ruler::CheapRuler &getRuler(const FixedLatitude lat)
{
BOOST_ASSERT(step > 2);
// the |lat| > 0 -> |lat|-1 > -1 -> (|lat|-1)/step > -1/step > -1/2 >= -1 -> bin >= 0
std::size_t bin = (std::abs(static_cast<int>(lat)) - 1) / step;
BOOST_ASSERT(bin < cheap_ruler_cache.size());
return cheap_ruler_cache[bin];
};
private:
std::vector<mapbox::cheap_ruler::CheapRuler> cheap_ruler_cache;
const int step;
};
static CheapRulerContainer cheap_ruler_container(1800);
} // namespace
// Does not project the coordinates!
std::uint64_t squaredEuclideanDistance(const Coordinate lhs, const Coordinate rhs)
{
@@ -72,20 +32,6 @@ std::uint64_t squaredEuclideanDistance(const Coordinate lhs, const Coordinate rh
return result;
}
// Uses method described here:
// https://www.gpo.gov/fdsys/pkg/CFR-2005-title47-vol4/pdf/CFR-2005-title47-vol4-sec73-208.pdf
// should be within 0.1% or so of Vincenty method (assuming 19 buckets are enough)
// Should be more faster and more precise than Haversine
double fccApproximateDistance(const Coordinate coordinate_1, const Coordinate coordinate_2)
{
const auto lon1 = static_cast<double>(util::toFloating(coordinate_1.lon));
const auto lat1 = static_cast<double>(util::toFloating(coordinate_1.lat));
const auto lon2 = static_cast<double>(util::toFloating(coordinate_2.lon));
const auto lat2 = static_cast<double>(util::toFloating(coordinate_2.lat));
return cheap_ruler_container.getRuler(coordinate_1.lat, coordinate_2.lat)
.distance({lon1, lat1}, {lon2, lat2});
}
double haversineDistance(const Coordinate coordinate_1, const Coordinate coordinate_2)
{
auto lon1 = static_cast<int>(coordinate_1.lon);
@@ -477,6 +423,6 @@ double computeArea(const std::vector<Coordinate> &polygon)
return area / 2.;
}
} // namespace coordinate_calculation
} // namespace util
} // namespace osrm
} // ns coordinate_calculation
} // ns util
} // ns osrm
+1 -1
View File
@@ -10,7 +10,7 @@ exports.three_test_coordinates = [[7.41337, 43.72956],
exports.two_test_coordinates = exports.three_test_coordinates.slice(0, 2)
exports.test_tile = {'at': [17059, 11948, 15], 'size': 148750};
exports.test_tile = {'at': [17059, 11948, 15], 'size': 168571};
// Test files generated by the routing engine; check test/data
if (process.env.OSRM_DATA_PATH !== undefined) {
+162 -211
View File
@@ -5,228 +5,179 @@ var mld_data_path = require('./constants').mld_data_path;
var three_test_coordinates = require('./constants').three_test_coordinates;
var two_test_coordinates = require('./constants').two_test_coordinates;
test('table: test annotations paramater combination', function(assert) {
assert.plan(12);
test('table: distance table in Monaco', function(assert) {
assert.plan(11);
var osrm = new OSRM(data_path);
var options = {
coordinates: [three_test_coordinates[0], three_test_coordinates[1]],
annotations: ['distance']
};
osrm.table(options, function(err, table) {
assert.ifError(err);
assert.ok(table['distances'], 'distances table result should exist');
assert.notOk(table['durations'], 'durations table result should not exist');
});
options = {
coordinates: [three_test_coordinates[0], three_test_coordinates[1]],
annotations: ['duration']
};
osrm.table(options, function(err, table) {
assert.ifError(err);
assert.ok(table['durations'], 'durations table result should exist');
assert.notOk(table['distances'], 'distances table result should not exist');
});
options = {
coordinates: [three_test_coordinates[0], three_test_coordinates[1]],
annotations: ['duration', 'distance']
};
osrm.table(options, function(err, table) {
assert.ifError(err);
assert.ok(table['durations'], 'durations table result should exist');
assert.ok(table['distances'], 'distances table result should exist');
});
options = {
coordinates: [three_test_coordinates[0], three_test_coordinates[1]]
};
osrm.table(options, function(err, table) {
assert.ifError(err);
assert.ok(table['durations'], 'durations table result should exist');
assert.notOk(table['distances'], 'distances table result should not exist');
assert.ok(Array.isArray(table.durations), 'result must be an array');
var row_count = table.durations.length;
for (var i = 0; i < row_count; ++i) {
var column = table.durations[i];
var column_count = column.length;
assert.equal(row_count, column_count);
for (var j = 0; j < column_count; ++j) {
if (i == j) {
// check that diagonal is zero
assert.equal(0, column[j], 'diagonal must be zero');
} else {
// everything else is non-zero
assert.notEqual(0, column[j], 'other entries must be non-zero');
// and finite (not nan, inf etc.)
assert.ok(Number.isFinite(column[j]), 'distance is finite number');
}
}
}
assert.equal(options.coordinates.length, row_count);
});
});
var tables = ['distances', 'durations'];
tables.forEach(function(annotation) {
test('table: ' + annotation + ' table in Monaco', function(assert) {
assert.plan(11);
var osrm = new OSRM(data_path);
var options = {
coordinates: [three_test_coordinates[0], three_test_coordinates[1]],
annotations: [annotation.slice(0,-1)]
};
osrm.table(options, function(err, table) {
assert.ifError(err);
assert.ok(Array.isArray(table[annotation]), 'result must be an array');
var row_count = table[annotation].length;
for (var i = 0; i < row_count; ++i) {
var column = table[annotation][i];
var column_count = column.length;
assert.equal(row_count, column_count);
for (var j = 0; j < column_count; ++j) {
if (i == j) {
// check that diagonal is zero
assert.equal(0, column[j], 'diagonal must be zero');
} else {
// everything else is non-zero
assert.notEqual(0, column[j], 'other entries must be non-zero');
// and finite (not nan, inf etc.)
assert.ok(Number.isFinite(column[j]), 'distance is finite number');
}
test('table: distance table in Monaco with sources/destinations', function(assert) {
assert.plan(7);
var osrm = new OSRM(data_path);
var options = {
coordinates: [three_test_coordinates[0], three_test_coordinates[1]],
sources: [0],
destinations: [0,1]
};
osrm.table(options, function(err, table) {
assert.ifError(err);
assert.ok(Array.isArray(table.durations), 'result must be an array');
var row_count = table.durations.length;
for (var i = 0; i < row_count; ++i) {
var column = table.durations[i];
var column_count = column.length;
assert.equal(options.destinations.length, column_count);
for (var j = 0; j < column_count; ++j) {
if (i == j) {
// check that diagonal is zero
assert.equal(0, column[j], 'diagonal must be zero');
} else {
// everything else is non-zero
assert.notEqual(0, column[j], 'other entries must be non-zero');
// and finite (not nan, inf etc.)
assert.ok(Number.isFinite(column[j]), 'distance is finite number');
}
}
assert.equal(options.coordinates.length, row_count);
});
});
test('table: ' + annotation + ' table in Monaco with sources/destinations', function(assert) {
assert.plan(7);
var osrm = new OSRM(data_path);
var options = {
coordinates: [three_test_coordinates[0], three_test_coordinates[1]],
sources: [0],
destinations: [0,1],
annotations: [annotation.slice(0,-1)]
};
osrm.table(options, function(err, table) {
assert.ifError(err);
assert.ok(Array.isArray(table[annotation]), 'result must be an array');
var row_count = table[annotation].length;
for (var i = 0; i < row_count; ++i) {
var column = table[annotation][i];
var column_count = column.length;
assert.equal(options.destinations.length, column_count);
for (var j = 0; j < column_count; ++j) {
if (i == j) {
// check that diagonal is zero
assert.equal(0, column[j], 'diagonal must be zero');
} else {
// everything else is non-zero
assert.notEqual(0, column[j], 'other entries must be non-zero');
// and finite (not nan, inf etc.)
assert.ok(Number.isFinite(column[j]), 'distance is finite number');
}
}
}
assert.equal(options.sources.length, row_count);
});
});
test('table: ' + annotation + ' throws on invalid arguments', function(assert) {
assert.plan(14);
var osrm = new OSRM(data_path);
var options = {annotations: [annotation.slice(0,-1)]};
assert.throws(function() { osrm.table(options); },
/Two arguments required/);
options.coordinates = null;
assert.throws(function() { osrm.table(options, function() {}); },
/Coordinates must be an array of \(lon\/lat\) pairs/);
options.coordinates = [three_test_coordinates[0]];
assert.throws(function() { osrm.table(options, function(err, response) {}) },
/At least two coordinates must be provided/);
options.coordinates = three_test_coordinates[0];
assert.throws(function() { osrm.table(options, function(err, response) {}) },
/Coordinates must be an array of \(lon\/lat\) pairs/);
options.coordinates = [three_test_coordinates[0][0], three_test_coordinates[0][1]];
assert.throws(function() { osrm.table(options, function(err, response) {}) },
/Coordinates must be an array of \(lon\/lat\) pairs/);
options.coordinates = two_test_coordinates;
options.sources = true;
assert.throws(function() { osrm.table(options, function(err, response) {}) },
/Sources must be an array of indices \(or undefined\)/);
options.sources = [0, 4];
assert.throws(function() { osrm.table(options, function(err, response) {}) },
/Source indices must be less than or equal to the number of coordinates/);
options.sources = [0.3, 1.1];
assert.throws(function() { osrm.table(options, function(err, response) {}) },
/Source must be an integer/);
options.destinations = true;
delete options.sources;
assert.throws(function() { osrm.table(options, function(err, response) {}) },
/Destinations must be an array of indices \(or undefined\)/);
options.destinations = [0, 4];
assert.throws(function() { osrm.table(options, function(err, response) {}) },
/Destination indices must be less than or equal to the number of coordinates/);
options.destinations = [0.3, 1.1];
assert.throws(function() { osrm.table(options, function(err, response) {}) },
/Destination must be an integer/);
// does not throw: the following two have been changed in OSRM v5
options.sources = [0, 1];
delete options.destinations;
assert.doesNotThrow(function() { osrm.table(options, function(err, response) {}) },
/Both sources and destinations need to be specified/);
options.destinations = [0, 1];
assert.doesNotThrow(function() { osrm.table(options, function(err, response) {}) },
/You can either specify sources and destinations, or coordinates/);
assert.throws(function() { osrm.route({coordinates: two_test_coordinates, generate_hints: null}, function(err, route) {}) },
/generate_hints must be of type Boolean/);
});
test('table: throws on invalid arguments', function(assert) {
assert.plan(1);
var osrm = new OSRM(data_path);
assert.throws(function() { osrm.table(null, function() {}); },
/First arg must be an object/);
});
test('table: ' + annotation + ' table in Monaco with hints', function(assert) {
assert.plan(5);
var osrm = new OSRM(data_path);
var options = {
coordinates: two_test_coordinates,
generate_hints: true, // true is default but be explicit here
annotations: [annotation.slice(0,-1)]
};
osrm.table(options, function(err, table) {
assert.ifError(err);
function assertHasHints(waypoint) {
assert.notStrictEqual(waypoint.hint, undefined);
}
table.sources.map(assertHasHints);
table.destinations.map(assertHasHints);
});
});
test('table: ' + annotation + ' table in Monaco without hints', function(assert) {
assert.plan(5);
var osrm = new OSRM(data_path);
var options = {
coordinates: two_test_coordinates,
generate_hints: false, // true is default
annotations: [annotation.slice(0,-1)]
};
osrm.table(options, function(err, table) {
assert.ifError(err);
function assertHasNoHints(waypoint) {
assert.strictEqual(waypoint.hint, undefined);
}
table.sources.map(assertHasNoHints);
table.destinations.map(assertHasNoHints);
});
});
test('table: ' + annotation + ' table in Monaco without motorways', function(assert) {
assert.plan(1);
var osrm = new OSRM({path: mld_data_path, algorithm: 'MLD'});
var options = {
coordinates: two_test_coordinates,
exclude: ['motorway'],
annotations: [annotation.slice(0,-1)]
};
osrm.table(options, function(err, response) {
assert.equal(response[annotation].length, 2);
});
}
assert.equal(options.sources.length, row_count);
});
});
test('table: throws on invalid arguments', function(assert) {
assert.plan(14);
var osrm = new OSRM(data_path);
var options = {};
assert.throws(function() { osrm.table(options); },
/Two arguments required/);
options.coordinates = null;
assert.throws(function() { osrm.table(options, function() {}); },
/Coordinates must be an array of \(lon\/lat\) pairs/);
options.coordinates = [three_test_coordinates[0]];
assert.throws(function() { osrm.table(options, function(err, response) {}) },
/At least two coordinates must be provided/);
options.coordinates = three_test_coordinates[0];
assert.throws(function() { osrm.table(options, function(err, response) {}) },
/Coordinates must be an array of \(lon\/lat\) pairs/);
options.coordinates = [three_test_coordinates[0][0], three_test_coordinates[0][1]];
assert.throws(function() { osrm.table(options, function(err, response) {}) },
/Coordinates must be an array of \(lon\/lat\) pairs/);
options.coordinates = two_test_coordinates;
options.sources = true;
assert.throws(function() { osrm.table(options, function(err, response) {}) },
/Sources must be an array of indices \(or undefined\)/);
options.sources = [0, 4];
assert.throws(function() { osrm.table(options, function(err, response) {}) },
/Source indices must be less than or equal to the number of coordinates/);
options.sources = [0.3, 1.1];
assert.throws(function() { osrm.table(options, function(err, response) {}) },
/Source must be an integer/);
options.destinations = true;
delete options.sources;
assert.throws(function() { osrm.table(options, function(err, response) {}) },
/Destinations must be an array of indices \(or undefined\)/);
options.destinations = [0, 4];
assert.throws(function() { osrm.table(options, function(err, response) {}) },
/Destination indices must be less than or equal to the number of coordinates/);
options.destinations = [0.3, 1.1];
assert.throws(function() { osrm.table(options, function(err, response) {}) },
/Destination must be an integer/);
// does not throw: the following two have been changed in OSRM v5
options.sources = [0, 1];
delete options.destinations;
assert.doesNotThrow(function() { osrm.table(options, function(err, response) {}) },
/Both sources and destinations need to be specified/);
options.destinations = [0, 1];
assert.doesNotThrow(function() { osrm.table(options, function(err, response) {}) },
/You can either specify sources and destinations, or coordinates/);
assert.throws(function() { osrm.route({coordinates: two_test_coordinates, generate_hints: null}, function(err, route) {}) },
/generate_hints must be of type Boolean/);
});
test('table: throws on invalid arguments', function(assert) {
assert.plan(1);
var osrm = new OSRM(data_path);
assert.throws(function() { osrm.table(null, function() {}); },
/First arg must be an object/);
});
test('table: distance table in Monaco with hints', function(assert) {
assert.plan(5);
var osrm = new OSRM(data_path);
var options = {
coordinates: two_test_coordinates,
generate_hints: true // true is default but be explicit here
};
osrm.table(options, function(err, table) {
console.log(table);
assert.ifError(err);
function assertHasHints(waypoint) {
assert.notStrictEqual(waypoint.hint, undefined);
}
table.sources.map(assertHasHints);
table.destinations.map(assertHasHints);
});
});
test('table: distance table in Monaco without hints', function(assert) {
assert.plan(5);
var osrm = new OSRM(data_path);
var options = {
coordinates: two_test_coordinates,
generate_hints: false // true is default
};
osrm.table(options, function(err, table) {
assert.ifError(err);
function assertHasNoHints(waypoint) {
assert.strictEqual(waypoint.hint, undefined);
}
table.sources.map(assertHasNoHints);
table.destinations.map(assertHasNoHints);
});
});
test('table: table in Monaco without motorways', function(assert) {
assert.plan(2);
var osrm = new OSRM({path: mld_data_path, algorithm: 'MLD'});
var options = {
coordinates: two_test_coordinates,
exclude: ['motorway']
};
osrm.table(options, function(err, response) {
assert.ifError(err);
assert.equal(response.durations.length, 2);
});
});
-18
View File
@@ -1,18 +0,0 @@
Standard: Cpp11
IndentWidth: 4
AccessModifierOffset: -4
UseTab: Never
BinPackParameters: false
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AllowShortBlocksOnASingleLine: false
AllowShortFunctionsOnASingleLine: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
AlwaysBreakTemplateDeclarations: true
NamespaceIndentation: None
PointerBindsToType: true
SpacesInParentheses: false
BreakBeforeBraces: Attach
ColumnLimit: 100
Cpp11BracedListStyle: false
SpacesBeforeTrailingComments: 1
@@ -1,2 +0,0 @@
build/
mason_packages/
-25
View File
@@ -1,25 +0,0 @@
language: generic
matrix:
include:
- os: linux
env: CXX=g++-4.9
sudo: required
dist: trusty
addons:
apt:
sources: [ 'ubuntu-toolchain-r-test' ]
packages: [ 'g++-4.9', 'cmake', 'cmake-data' ]
- os: linux
env: CXX=g++-5
sudo: required
dist: trusty
addons:
apt:
sources: [ 'ubuntu-toolchain-r-test' ]
packages: [ 'g++-5', 'cmake', 'cmake-data' ]
cache: apt
script:
- cmake . && make && ./cheap_ruler
-27
View File
@@ -1,27 +0,0 @@
cmake_minimum_required(VERSION 3.0.0 FATAL_ERROR)
project(cheap_ruler LANGUAGES CXX C)
include(cmake/build.cmake)
include(cmake/mason.cmake)
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.10)
set(CMAKE_CONFIGURATION_TYPES Debug Release)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall -Wextra -Wpedantic -Wshadow")
mason_use(geometry VERSION 0.9.2 HEADER_ONLY)
mason_use(gtest VERSION 1.8.0)
mason_use(variant VERSION 1.1.4 HEADER_ONLY)
add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0)
add_executable(cheap_ruler
${PROJECT_SOURCE_DIR}/test/cheap_ruler.cpp
)
target_include_directories(cheap_ruler
PUBLIC ${PROJECT_SOURCE_DIR}/include
)
target_add_mason_package(cheap_ruler PRIVATE geometry)
target_add_mason_package(cheap_ruler PRIVATE gtest)
target_add_mason_package(cheap_ruler PRIVATE variant)
-15
View File
@@ -1,15 +0,0 @@
ISC License
Copyright (c) 2017, Mapbox
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
-199
View File
@@ -1,199 +0,0 @@
# cheap-ruler-cpp
Port to C++ of [Cheap Ruler](https://github.com/mapbox/cheap-ruler), a collection of very fast approximations to common geodesic measurements.
[![Build Status](https://travis-ci.org/mapbox/cheap-ruler-cpp.svg?branch=master)](https://travis-ci.org/mapbox/cheap-ruler-cpp)
# Usage
```cpp
#include <mapbox/cheap_ruler.hpp>
namespace cr = mapbox::cheap_ruler;
```
All `point`, `line_string`, `polygon`, and `box` references are [mapbox::geometry](https://github.com/mapbox/geometry.hpp) data structures.
## Create a ruler object
#### `CheapRuler(double latitude, Unit unit)`
Creates a ruler object that will approximate measurements around the given latitude with an optional distance unit. Once created, the ruler object has access to the [methods](#methods) below.
```cpp
auto ruler = cr::CheapRuler(32.8351);
auto milesRuler = cr::CheapRuler(32.8351, cr::CheapRuler::Miles);
```
Possible units:
* `cheap_ruler::CheapRuler::Unit`
* `cheap_ruler::CheapRuler::Kilometers`
* `cheap_ruler::CheapRuler::Miles`
* `cheap_ruler::CheapRuler::NauticalMiles`
* `cheap_ruler::CheapRuler::Meters`
* `cheap_ruler::CheapRuler::Yards`
* `cheap_ruler::CheapRuler::Feet`
* `cheap_ruler::CheapRuler::Inches`
#### `CheapRuler::fromTile(uint32_t y, uint32_t z)`
Creates a ruler object from tile coordinates (`y` and `z` integers). Convenient in tile-reduce scripts.
```cpp
auto ruler = cr::CheapRuler::fromTile(11041, 15);
```
## Methods
#### `distance(point a, point b)`
Given two points of the form [x = longitude, y = latitude], returns the distance (`double`).
```cpp
cr::point point_a{-96.9148, 32.8351};
cr::point point_b{-96.9146, 32.8386};
auto distance = ruler.distance(point_a, point_b);
std::clog << distance; // 0.388595
```
#### `bearing(point a, point b)`
Returns the bearing (`double`) between two points in angles.
```cpp
cr::point point_a{-96.9148, 32.8351};
cr::point point_b{-96.9146, 32.8386};
auto bearing = ruler.bearing(point_a, point_b);
std::clog << bearing; // 2.76206
```
#### `destination(point origin, double distance, double bearing)`
Returns a new point (`point`) given distance and bearing from the starting point.
```cpp
cr::point point_a{-96.9148, 32.8351};
auto dest = ruler.destination(point_a, 1.0, -175);
std::clog << dest.x << ", " << dest.y; // -96.9148, 32.8261
```
#### `offset(point origin, double dx, double dy)`
Returns a new point (`point`) given easting and northing offsets from the starting point.
```cpp
cr::point point_a{-96.9148, 32.8351};
auto os = ruler.offset(point_a, 10.0, -5.0);
std::clog << os.x << ", " << os.y; // -96.808, 32.79
```
#### `lineDistance(const line_string& points)`
Given a line (an array of points), returns the total line distance (`double`).
```cpp
cr::line_string line_a{{ -96.9, 32.8 }, { -96.8, 32.8 }, { -96.2, 32.3 }};
auto line_distance = ruler.lineDistance(line_a);
std::clog << line_distance; // 88.2962
```
#### `area(polygon poly)`
Given a polygon (an array of rings, where each ring is an array of points), returns the area (`double`).
```cpp
cr::linear_ring ring{{ -96.9, 32.8 }, { -96.8, 32.8 }, { -96.2, 32.3 }, { -96.9, 32.8 }};
auto area = ruler.area(cr::polygon{ ring });
std::clog << area; //
```
#### `along(const line_string& line, double distance)`
Returns the point (`point`) at a specified distance along the line.
```cpp
cr::linear_ring ring{{ -96.9, 32.8 }, { -96.8, 32.8 }, { -96.2, 32.3 }, { -96.9, 32.8 }};
auto area = ruler.area(cr::polygon{ ring });
std::clog << area; // 259.581
```
#### `pointOnLine(const line_string& line, point p)`
Returns a tuple of the form `std::pair<point, unsigned>` where point is closest point on the line from the given point, index is the start index of the segment with the closest point, and t is a parameter from 0 to 1 that indicates where the closest point is on that segment.
```cpp
cr::line_string line{{ -96.9, 32.8 }, { -96.8, 32.8 }, { -96.2, 32.3 }};
cr::point point{-96.9, 32.79};
auto pol = ruler.pointOnLine(line, point);
auto point = std::get<0>(pol);
std::clog << point.x << ", " << point.y; // -96.9, 32.8 (point)
std::clog << std::get<1>(pol); // 0 (index)
std::clog << std::get<2>(pol); // 0. (t)
```
#### `lineSlice(point start, point stop, const line_string& line)`
Returns a part of the given line (`line_string`) between the start and the stop points (or their closest points on the line).
```cpp
cr::line_string line{{ -96.9, 32.8 }, { -96.8, 32.8 }, { -96.2, 32.3 }};
cr::point start_point{-96.9, 32.8};
cr::point stop_point{-96.8, 32.8};
auto slice = ruler.lineSlice(start_point, stop_point, line);
std::clog << slice[0].x << ", " << slice[0].y; // -96.9, 32.8
std::clog << slice[1].x << ", " << slice[1].y; // -96.8, 32.8
```
#### `lineSliceAlong(double start, double stop, const line_string& line)`
Returns a part of the given line (`line_string`) between the start and the stop points indicated by distance along the line.
```cpp
cr::line_string line{{ -96.9, 32.8 }, { -96.8, 32.8 }, { -96.2, 32.3 }};
auto slice = ruler.lineSliceAlong(0.1, 1.2, line);
```
#### `bufferPoint(point p, double buffer)`
Given a point, returns a bounding box object ([w, s, e, n]) created from the given point buffered by a given distance.
```cpp
cr::point point{-96.9, 32.8};
auto box = ruler.bufferPoint(point, 0.1);
```
#### `bufferBBox(box bbox, double buffer)`
Given a bounding box, returns the box buffered by a given distance.
```cpp
cr::box bbox({ 30, 38 }, { 40, 39 });
auto bbox2 = ruler.bufferBBox(bbox, 1);
```
#### `insideBBox(point p, box bbox)`
Returns true (`bool`) if the given point is inside in the given bounding box, otherwise false.
```cpp
cr::box bbox({ 30, 38 }, { 40, 39 });
auto inside = ruler.insideBBox({ 35, 38.5 }, bbox);
std::clog << inside; // true
```
# Develop
```shell
# create targets
cmake .
# build
make
# test
./cheap_ruler
# or just do it all in one!
cmake . && make && ./cheap_ruler
```
-11
View File
@@ -1,11 +0,0 @@
# Generate source groups so the files are properly sorted in IDEs like Xcode.
function(create_source_groups target)
get_target_property(sources ${target} SOURCES)
foreach(file ${sources})
get_filename_component(file "${file}" ABSOLUTE)
string(REGEX REPLACE "^${CMAKE_SOURCE_DIR}/" "" group "${file}")
get_filename_component(group "${group}" DIRECTORY)
string(REPLACE "/" "\\" group "${group}")
source_group("${group}" FILES "${file}")
endforeach()
endfunction()
-235
View File
@@ -1,235 +0,0 @@
# Mason CMake
include(CMakeParseArguments)
function(mason_detect_platform)
# Determine platform
if(NOT MASON_PLATFORM)
# we call uname -s manually here since
# CMAKE_HOST_SYSTEM_NAME will not be defined before the project() call
execute_process(
COMMAND uname -s
OUTPUT_VARIABLE UNAME
OUTPUT_STRIP_TRAILING_WHITESPACE)
if (UNAME STREQUAL "Darwin")
set(MASON_PLATFORM "osx" PARENT_SCOPE)
else()
set(MASON_PLATFORM "linux" PARENT_SCOPE)
endif()
endif()
# Determine platform version string
if(NOT MASON_PLATFORM_VERSION)
# Android Studio only passes ANDROID_ABI, but we need to adjust that to the Mason
if(MASON_PLATFORM STREQUAL "android" AND NOT MASON_PLATFORM_VERSION)
if (ANDROID_ABI STREQUAL "armeabi")
set(MASON_PLATFORM_VERSION "arm-v5-9" PARENT_SCOPE)
elseif (ANDROID_ABI STREQUAL "armeabi-v7a")
set(MASON_PLATFORM_VERSION "arm-v7-9" PARENT_SCOPE)
elseif (ANDROID_ABI STREQUAL "arm64-v8a")
set(MASON_PLATFORM_VERSION "arm-v8-21" PARENT_SCOPE)
elseif (ANDROID_ABI STREQUAL "x86")
set(MASON_PLATFORM_VERSION "x86-9" PARENT_SCOPE)
elseif (ANDROID_ABI STREQUAL "x86_64")
set(MASON_PLATFORM_VERSION "x86-64-21" PARENT_SCOPE)
elseif (ANDROID_ABI STREQUAL "mips")
set(MASON_PLATFORM_VERSION "mips-9" PARENT_SCOPE)
elseif (ANDROID_ABI STREQUAL "mips64")
set(MASON_PLATFORM_VERSION "mips-64-9" PARENT_SCOPE)
else()
message(FATAL_ERROR "Unknown ANDROID_ABI '${ANDROID_ABI}'.")
endif()
elseif(MASON_PLATFORM STREQUAL "ios")
set(MASON_PLATFORM_VERSION "8.0" PARENT_SCOPE)
else()
execute_process(
COMMAND uname -m
OUTPUT_VARIABLE MASON_PLATFORM_VERSION
OUTPUT_STRIP_TRAILING_WHITESPACE)
set(MASON_PLATFORM_VERSION "${MASON_PLATFORM_VERSION}" PARENT_SCOPE)
endif()
endif()
endfunction()
function(mason_use _PACKAGE)
if(NOT _PACKAGE)
message(FATAL_ERROR "[Mason] No package name given")
endif()
cmake_parse_arguments("" "HEADER_ONLY" "VERSION" "" ${ARGN})
if(_UNPARSED_ARGUMENTS)
message(FATAL_ERROR "[Mason] mason_use() called with unrecognized arguments: ${_UNPARSED_ARGUMENTS}")
endif()
if(NOT _VERSION)
message(FATAL_ERROR "[Mason] Specifying a version is required")
endif()
if(MASON_PACKAGE_${_PACKAGE}_INVOCATION STREQUAL "${MASON_INVOCATION}")
# Check that the previous invocation of mason_use didn't select another version of this package
if(NOT MASON_PACKAGE_${_PACKAGE}_VERSION STREQUAL ${_VERSION})
message(FATAL_ERROR "[Mason] Already using ${_PACKAGE} ${MASON_PACKAGE_${_PACKAGE}_VERSION}. Cannot select version ${_VERSION}.")
endif()
else()
if(_HEADER_ONLY)
set(_PLATFORM_ID "headers")
else()
set(_PLATFORM_ID "${MASON_PLATFORM}-${MASON_PLATFORM_VERSION}")
endif()
set(_SLUG "${_PLATFORM_ID}/${_PACKAGE}/${_VERSION}")
set(_INSTALL_PATH "${MASON_PACKAGE_DIR}/${_SLUG}")
file(RELATIVE_PATH _INSTALL_PATH_RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${_INSTALL_PATH}")
if(NOT EXISTS "${_INSTALL_PATH}")
set(_CACHE_PATH "${MASON_PACKAGE_DIR}/.binaries/${_SLUG}.tar.gz")
if (NOT EXISTS "${_CACHE_PATH}")
# Download the package
set(_URL "${MASON_REPOSITORY}/${_SLUG}.tar.gz")
message("[Mason] Downloading package ${_URL}...")
set(_FAILED)
set(_ERROR)
# Note: some CMake versions are compiled without SSL support
get_filename_component(_CACHE_DIR "${_CACHE_PATH}" DIRECTORY)
file(MAKE_DIRECTORY "${_CACHE_DIR}")
execute_process(
COMMAND curl --retry 3 -s -f -S -L "${_URL}" -o "${_CACHE_PATH}.tmp"
RESULT_VARIABLE _FAILED
ERROR_VARIABLE _ERROR)
if(_FAILED)
message(FATAL_ERROR "[Mason] Failed to download ${_URL}: ${_ERROR}")
else()
# We downloaded to a temporary file to prevent half-finished downloads
file(RENAME "${_CACHE_PATH}.tmp" "${_CACHE_PATH}")
endif()
endif()
# Unpack the package
message("[Mason] Unpacking package to ${_INSTALL_PATH_RELATIVE}...")
file(MAKE_DIRECTORY "${_INSTALL_PATH}")
execute_process(
COMMAND ${CMAKE_COMMAND} -E tar xzf "${_CACHE_PATH}"
WORKING_DIRECTORY "${_INSTALL_PATH}")
endif()
# Error out if there is no config file.
if(NOT EXISTS "${_INSTALL_PATH}/mason.ini")
message(FATAL_ERROR "[Mason] Could not find mason.ini for package ${_PACKAGE} ${_VERSION}")
endif()
set(MASON_PACKAGE_${_PACKAGE}_PREFIX "${_INSTALL_PATH}" CACHE STRING "${_PACKAGE} ${_INSTALL_PATH}" FORCE)
mark_as_advanced(MASON_PACKAGE_${_PACKAGE}_PREFIX)
# Load the configuration from the ini file
file(STRINGS "${_INSTALL_PATH}/mason.ini" _CONFIG_FILE)
foreach(_LINE IN LISTS _CONFIG_FILE)
string(REGEX MATCH "^([a-z_]+) *= *" _KEY "${_LINE}")
if (_KEY)
string(LENGTH "${_KEY}" _KEY_LENGTH)
string(SUBSTRING "${_LINE}" ${_KEY_LENGTH} -1 _VALUE)
string(REGEX REPLACE ";.*$" "" _VALUE "${_VALUE}") # Trim trailing commas
string(REPLACE "{prefix}" "${_INSTALL_PATH}" _VALUE "${_VALUE}")
string(STRIP "${_VALUE}" _VALUE)
string(REPLACE "=" "" _KEY "${_KEY}")
string(STRIP "${_KEY}" _KEY)
string(TOUPPER "${_KEY}" _KEY)
if(_KEY STREQUAL "INCLUDE_DIRS" OR _KEY STREQUAL "STATIC_LIBS" )
separate_arguments(_VALUE)
endif()
set(MASON_PACKAGE_${_PACKAGE}_${_KEY} "${_VALUE}" CACHE STRING "${_PACKAGE} ${_KEY}" FORCE)
mark_as_advanced(MASON_PACKAGE_${_PACKAGE}_${_KEY})
endif()
endforeach()
# Compare version in the package to catch errors early on
if(NOT _VERSION STREQUAL MASON_PACKAGE_${_PACKAGE}_VERSION)
message(FATAL_ERROR "[Mason] Package at ${_INSTALL_PATH_RELATIVE} has version '${MASON_PACKAGE_${_PACKAGE}_VERSION}', but required '${_VERSION}'")
endif()
if(NOT _PACKAGE STREQUAL MASON_PACKAGE_${_PACKAGE}_NAME)
message(FATAL_ERROR "[Mason] Package at ${_INSTALL_PATH_RELATIVE} has name '${MASON_PACKAGE_${_PACKAGE}_NAME}', but required '${_NAME}'")
endif()
if(NOT _HEADER_ONLY)
if(NOT MASON_PLATFORM STREQUAL MASON_PACKAGE_${_PACKAGE}_PLATFORM)
message(FATAL_ERROR "[Mason] Package at ${_INSTALL_PATH_RELATIVE} has platform '${MASON_PACKAGE_${_PACKAGE}_PLATFORM}', but required '${MASON_PLATFORM}'")
endif()
if(NOT MASON_PLATFORM_VERSION STREQUAL MASON_PACKAGE_${_PACKAGE}_PLATFORM_VERSION)
message(FATAL_ERROR "[Mason] Package at ${_INSTALL_PATH_RELATIVE} has platform version '${MASON_PACKAGE_${_PACKAGE}_PLATFORM_VERSION}', but required '${MASON_PLATFORM_VERSION}'")
endif()
endif()
# Concatenate the static libs and libraries
set(_LIBRARIES)
list(APPEND _LIBRARIES ${MASON_PACKAGE_${_PACKAGE}_STATIC_LIBS} ${MASON_PACKAGE_${_PACKAGE}_LDFLAGS})
set(MASON_PACKAGE_${_PACKAGE}_LIBRARIES "${_LIBRARIES}" CACHE STRING "${_PACKAGE} _LIBRARIES" FORCE)
mark_as_advanced(MASON_PACKAGE_${_PACKAGE}_LIBRARIES)
if(NOT _HEADER_ONLY)
string(REGEX MATCHALL "(^| +)-L *([^ ]+)" MASON_PACKAGE_${_PACKAGE}_LIBRARY_DIRS "${MASON_PACKAGE_${_PACKAGE}_LDFLAGS}")
string(REGEX REPLACE "(^| +)-L *" "\\1" MASON_PACKAGE_${_PACKAGE}_LIBRARY_DIRS "${MASON_PACKAGE_${_PACKAGE}_LIBRARY_DIRS}")
set(MASON_PACKAGE_${_PACKAGE}_LIBRARY_DIRS "${MASON_PACKAGE_${_PACKAGE}_LIBRARY_DIRS}" CACHE STRING "${_PACKAGE} ${MASON_PACKAGE_${_PACKAGE}_LIBRARY_DIRS}" FORCE)
mark_as_advanced(MASON_PACKAGE_${_PACKAGE}_LIBRARY_DIRS)
endif()
# Store invocation ID to prevent different versions of the same package in one invocation
set(MASON_PACKAGE_${_PACKAGE}_INVOCATION "${MASON_INVOCATION}" CACHE INTERNAL "${_PACKAGE} invocation ID" FORCE)
endif()
endfunction()
macro(target_add_mason_package _TARGET _VISIBILITY _PACKAGE)
if (NOT MASON_PACKAGE_${_PACKAGE}_INVOCATION)
message(FATAL_ERROR "[Mason] Package ${_PACKAGE} has not been initialized yet")
endif()
target_include_directories(${_TARGET} ${_VISIBILITY} "${MASON_PACKAGE_${_PACKAGE}_INCLUDE_DIRS}")
target_compile_definitions(${_TARGET} ${_VISIBILITY} "${MASON_PACKAGE_${_PACKAGE}_DEFINITIONS}")
target_compile_options(${_TARGET} ${_VISIBILITY} "${MASON_PACKAGE_${_PACKAGE}_OPTIONS}")
target_link_libraries(${_TARGET} ${_VISIBILITY} "${MASON_PACKAGE_${_PACKAGE}_LIBRARIES}")
endmacro()
# Setup
string(RANDOM LENGTH 16 MASON_INVOCATION)
# Read environment variables if CMake is run in command mode
if (CMAKE_ARGC)
set(MASON_PLATFORM "$ENV{MASON_PLATFORM}")
set(MASON_PLATFORM_VERSION "$ENV{MASON_PLATFORM_VERSION}")
set(MASON_PACKAGE_DIR "$ENV{MASON_PACKAGE_DIR}")
set(MASON_REPOSITORY "$ENV{MASON_REPOSITORY}")
endif()
# Directory where Mason packages are located; typically ends with mason_packages
if (NOT MASON_PACKAGE_DIR)
set(MASON_PACKAGE_DIR "${CMAKE_SOURCE_DIR}/mason_packages")
endif()
# URL prefix of where packages are located.
if (NOT MASON_REPOSITORY)
set(MASON_REPOSITORY "https://mason-binaries.s3.amazonaws.com")
endif()
mason_detect_platform()
# Execute commands if CMake is run in command mode
if (CMAKE_ARGC)
# Collect remaining arguments for passing to mason_use
set(_MASON_ARGS)
foreach(I RANGE 4 ${CMAKE_ARGC})
list(APPEND _MASON_ARGS "${CMAKE_ARGV${I}}")
endforeach()
# Install the package
mason_use(${_MASON_ARGS})
# Optionally print variables
if(DEFINED MASON_PACKAGE_${CMAKE_ARGV4}_${CMAKE_ARGV3})
# CMake can't write to stdout with message()
execute_process(COMMAND ${CMAKE_COMMAND} -E echo "${MASON_PACKAGE_${CMAKE_ARGV4}_${CMAKE_ARGV3}}")
endif()
endif()

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